Use case-based addressing in RIBSupport
[bgpcep.git] / bgp / rib-spi / src / main / java / org / opendaylight / protocol / bgp / rib / spi / AbstractRIBSupport.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.protocol.bgp.rib.spi;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import java.util.Collection;
14 import java.util.Collections;
15 import java.util.Optional;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
18 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.UpdateBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1Builder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2Builder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.destination.DestinationType;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutes;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.BgpRib;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.Route;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.Rib;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.bgp.rib.rib.LocRib;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.TablesKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisher;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisherBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.next.hop.CNextHop;
49 import org.opendaylight.yangtools.yang.binding.ChildOf;
50 import org.opendaylight.yangtools.yang.binding.ChoiceIn;
51 import org.opendaylight.yangtools.yang.binding.DataObject;
52 import org.opendaylight.yangtools.yang.binding.Identifiable;
53 import org.opendaylight.yangtools.yang.binding.Identifier;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
56 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
57 import org.opendaylight.yangtools.yang.common.QName;
58 import org.opendaylight.yangtools.yang.common.QNameModule;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
60 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
61 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
62 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
63 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
64 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
65 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
66 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
67 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
68 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
69 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
70 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
71 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
72 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
73 import org.slf4j.Logger;
74 import org.slf4j.LoggerFactory;
75
76 @Beta
77 public abstract class AbstractRIBSupport<
78         C extends Routes & DataObject & ChoiceIn<Tables>,
79         S extends ChildOf<? super C>,
80         R extends Route & ChildOf<? super S> & Identifiable<I>,
81         I extends Identifier<R>>
82         implements RIBSupport<C, S, R, I> {
83     public static final String ROUTE_KEY = "route-key";
84     private static final Logger LOG = LoggerFactory.getLogger(AbstractRIBSupport.class);
85     private static final NodeIdentifier ADVERTISED_ROUTES = new NodeIdentifier(AdvertizedRoutes.QNAME);
86     private static final NodeIdentifier WITHDRAWN_ROUTES = new NodeIdentifier(WithdrawnRoutes.QNAME);
87     private static final NodeIdentifier DESTINATION_TYPE = new NodeIdentifier(DestinationType.QNAME);
88     private static final InstanceIdentifier<Tables> TABLES_II = InstanceIdentifier.create(BgpRib.class)
89             .child(Rib.class).child(LocRib.class).child(Tables.class);
90     private static final NodeIdentifier ROUTES = new NodeIdentifier(Routes.QNAME);
91     private static final ApplyRoute DELETE_ROUTE = new DeleteRoute();
92     private final NodeIdentifier routesContainerIdentifier;
93     private final NodeIdentifier routesListIdentifier;
94     private final NodeIdentifier routeAttributesIdentifier;
95     private final Class<C> cazeClass;
96     private final Class<S> containerClass;
97     private final Class<R> listClass;
98     private final ApplyRoute putRoute = new PutRoute();
99     private final ChoiceNode emptyRoutes;
100     private final QName routeQname;
101     private final Class<? extends AddressFamily> afiClass;
102     private final Class<? extends SubsequentAddressFamily> safiClass;
103     private final NodeIdentifier destinationNid;
104     private final QName pathIdQname;
105     private final NodeIdentifier pathIdNid;
106     private final QName routeKeyQname;
107     private final NodeIdentifier prefixTypeNid;
108     private final NodeIdentifier rdNid;
109     protected final BindingNormalizedNodeSerializer mappingService;
110     protected final YangInstanceIdentifier routeDefaultYii;
111     private final TablesKey tk;
112
113     /**
114      * Default constructor. Requires the QName of the container augmented under the routes choice
115      * node in instantiations of the rib grouping. It is assumed that this container is defined by
116      * the same model which populates it with route grouping instantiation, and by extension with
117      * the route attributes container.
118      *
119      * @param mappingService   Serialization service
120      * @param cazeClass        Binding class of the AFI/SAFI-specific case statement, must not be null
121      * @param containerClass   Binding class of the container in routes choice, must not be null.
122      * @param listClass        Binding class of the route list, nust not be null;
123      * @param afiClass         address Family Class
124      * @param safiClass        SubsequentAddressFamily
125      * @param destContainerQname destination Container Qname
126      */
127     protected AbstractRIBSupport(
128             final BindingNormalizedNodeSerializer mappingService,
129             final Class<C> cazeClass,
130             final Class<S> containerClass,
131             final Class<R> listClass,
132             final Class<? extends AddressFamily> afiClass,
133             final Class<? extends SubsequentAddressFamily> safiClass,
134             final QName destContainerQname) {
135         final QNameModule module = BindingReflections.getQNameModule(cazeClass);
136         this.routesContainerIdentifier
137                 = new NodeIdentifier(BindingReflections.findQName(containerClass).withModule(module));
138         this.routeAttributesIdentifier = new NodeIdentifier(Attributes.QNAME.withModule(module));
139         this.cazeClass = requireNonNull(cazeClass);
140         this.mappingService = requireNonNull(mappingService);
141         this.containerClass = requireNonNull(containerClass);
142         this.listClass = requireNonNull(listClass);
143         this.routeQname = BindingReflections.findQName(listClass).withModule(module);
144         this.routesListIdentifier = new NodeIdentifier(this.routeQname);
145         this.tk = new TablesKey(afiClass, safiClass);
146         //FIXME Use Route Case IId instead of Tables IId.
147         this.emptyRoutes = (ChoiceNode) ((MapEntryNode) this.mappingService
148                 .toNormalizedNode(TABLES_II, new TablesBuilder().setKey(tk)
149                         .setRoutes(emptyRoutesCase()).build()).getValue())
150                 .getChild(new NodeIdentifier(BindingReflections.findQName(Routes.class))).get();
151         this.afiClass = afiClass;
152         this.safiClass = safiClass;
153         this.destinationNid = new NodeIdentifier(destContainerQname);
154         this.pathIdQname = QName.create(routeQName(), "path-id").intern();
155         this.pathIdNid = new NodeIdentifier(this.pathIdQname);
156         this.routeKeyQname = QName.create(routeQName(), ROUTE_KEY).intern();
157         this.prefixTypeNid = NodeIdentifier.create(QName.create(destContainerQname, "prefix").intern());
158         this.rdNid = NodeIdentifier.create(QName.create(destContainerQname, "route-distinguisher").intern());
159         this.routeDefaultYii =
160                 YangInstanceIdentifier.builder()
161                         .node(BgpRib.QNAME)
162                         .node(Rib.QNAME)
163                         .node(Rib.QNAME)
164                         .node(LocRib.QNAME)
165                         .node(Tables.QNAME)
166                         .node(Tables.QNAME)
167                         .node(Routes.QNAME)
168                         .node(BindingReflections.findQName(containerClass).withModule(module))
169                         .node(this.routeQname)
170                         .node(this.routeQname).build();
171     }
172
173     @Override
174     public final TablesKey getTablesKey() {
175         return this.tk;
176     }
177
178     @Override
179     public final Class<C> routesCaseClass() {
180         return this.cazeClass;
181     }
182
183     @Override
184     public final Class<S> routesContainerClass() {
185         return this.containerClass;
186     }
187
188     @Override
189     public final Class<R> routesListClass() {
190         return this.listClass;
191     }
192
193     @Override
194     public final ChoiceNode emptyRoutes() {
195         return this.emptyRoutes;
196     }
197
198     public final QName routeQName() {
199         return this.routeQname;
200     }
201
202     protected final NodeIdentifier prefixNid() {
203         return this.prefixTypeNid;
204     }
205
206     protected final NodeIdentifier routeNid() {
207         return this.routesListIdentifier;
208     }
209
210     @Override
211     public final Class<? extends AddressFamily> getAfi() {
212         return this.afiClass;
213     }
214
215     @Override
216     public final Class<? extends SubsequentAddressFamily> getSafi() {
217         return this.safiClass;
218     }
219
220     /**
221      * Build MpReachNlri object from DOM representation.
222      *
223      * @param routes Collection of MapEntryNode DOM representation of routes
224      * @param hop    CNextHop as it was parsed from Attributes, to be included in MpReach object
225      * @return MpReachNlri
226      */
227     private MpReachNlri buildReach(final Collection<MapEntryNode> routes, final CNextHop hop) {
228         final MpReachNlriBuilder mb = new MpReachNlriBuilder();
229         mb.setAfi(this.getAfi());
230         mb.setSafi(this.getSafi());
231         mb.setCNextHop(hop);
232         mb.setAdvertizedRoutes(new AdvertizedRoutesBuilder().setDestinationType(buildDestination(routes)).build());
233         return mb.build();
234     }
235
236     /**
237      * Build MpUnReachNlri object from DOM representation.
238      *
239      * @param routes Collection of MapEntryNode DOM representation of routes
240      * @return MpUnreachNlri
241      */
242     private MpUnreachNlri buildUnreach(final Collection<MapEntryNode> routes) {
243         final MpUnreachNlriBuilder mb = new MpUnreachNlriBuilder();
244         mb.setAfi(this.getAfi());
245         mb.setSafi(this.getSafi());
246         mb.setWithdrawnRoutes(new WithdrawnRoutesBuilder()
247                 .setDestinationType(buildWithdrawnDestination(routes)).build());
248         return mb.build();
249     }
250
251     protected abstract DestinationType buildDestination(Collection<MapEntryNode> routes);
252
253     protected abstract DestinationType buildWithdrawnDestination(Collection<MapEntryNode> routes);
254
255     /**
256      * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
257      * the RIB routes.
258      *
259      * @return Container identifier, may not be null.
260      */
261     protected final NodeIdentifier routesContainerIdentifier() {
262         return this.routesContainerIdentifier;
263     }
264
265     /**
266      * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
267      * the NLRI destination.
268      *
269      * @return Container identifier, may not be null.
270      */
271     private NodeIdentifier destinationContainerIdentifier() {
272         return this.destinationNid;
273     }
274
275     /**
276      * Given the destination as ContainerNode, implementation needs to parse the DOM model
277      * from this point onward:
278      *
279      * {@code /bgp-mp:mp-unreach-nlri/bgp-mp:withdrawn-routes/bgp-mp:destination-type}
280      * and delete the routes from its RIBs.
281      *
282      * @param tx           DOMDataWriteTransaction to be passed into implementation
283      * @param tablePath    YangInstanceIdentifier to be passed into implementation
284      * @param destination  ContainerNode DOM representation of NLRI in Update message
285      * @param routesNodeId NodeIdentifier
286      */
287     private void deleteDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
288             final ContainerNode destination, final NodeIdentifier routesNodeId) {
289         processDestination(tx, tablePath.node(routesNodeId), destination, null, DELETE_ROUTE);
290     }
291
292     /**
293      * Given the destination as ContainerNode, implementation needs to parse the DOM model
294      * from this point onward:
295      *
296      * {@code /bgp-mp:mp-reach-nlri/bgp-mp:advertized-routes/bgp-mp:destination-type}
297      * and put the routes to its RIBs.
298      *
299      * @param tx           DOMDataWriteTransaction to be passed into implementation
300      * @param tablePath    YangInstanceIdentifier to be passed into implementation
301      * @param destination  ContainerNode DOM representation of NLRI in Update message
302      * @param attributes   ContainerNode to be passed into implementation
303      * @param routesNodeId NodeIdentifier
304      */
305     private void putDestinationRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
306             final ContainerNode destination, final ContainerNode attributes, final NodeIdentifier routesNodeId) {
307         processDestination(tx, tablePath.node(routesNodeId), destination, attributes, this.putRoute);
308     }
309
310     protected abstract void processDestination(DOMDataWriteTransaction tx, YangInstanceIdentifier routesPath,
311             ContainerNode destination, ContainerNode attributes, ApplyRoute applyFunction);
312
313     private static ContainerNode getDestination(final DataContainerChild<? extends PathArgument, ?> routes,
314             final NodeIdentifier destinationId) {
315         if (routes instanceof ContainerNode) {
316             final java.util.Optional<DataContainerChild<? extends PathArgument, ?>> maybeDestination =
317                     ((ContainerNode) routes).getChild(DESTINATION_TYPE);
318             if (maybeDestination.isPresent()) {
319                 final DataContainerChild<? extends PathArgument, ?> destination = maybeDestination.get();
320                 if (destination instanceof ChoiceNode) {
321                     final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRet =
322                             ((ChoiceNode) destination).getChild(destinationId);
323                     if (maybeRet.isPresent()) {
324                         final DataContainerChild<? extends PathArgument, ?> ret = maybeRet.get();
325                         if (ret instanceof ContainerNode) {
326                             return (ContainerNode) ret;
327                         }
328
329                         LOG.debug("Specified node {} is not a container, ignoring it", ret);
330                     } else {
331                         LOG.debug("Specified container {} is not present in destination {}",
332                                 destinationId, destination);
333                     }
334                 } else {
335                     LOG.warn("Destination {} is not a choice, ignoring it", destination);
336                 }
337             } else {
338                 LOG.debug("Destination is not present in routes {}", routes);
339             }
340         } else {
341             LOG.warn("Advertized routes {} are not a container, ignoring it", routes);
342         }
343
344         return null;
345     }
346
347     @Override
348     public final NodeIdentifier routeAttributesIdentifier() {
349         return this.routeAttributesIdentifier;
350     }
351
352     @Override
353     public final Collection<DataTreeCandidateNode> changedRoutes(final DataTreeCandidateNode routes) {
354         final DataTreeCandidateNode myRoutes = routes.getModifiedChild(this.routesContainerIdentifier);
355         if (myRoutes == null) {
356             return Collections.emptySet();
357         }
358         final DataTreeCandidateNode routesMap = myRoutes.getModifiedChild(routeNid());
359         if (routesMap == null) {
360             return Collections.emptySet();
361         }
362         // Well, given the remote possibility of augmentation, we should perform a filter here,
363         // to make sure the type matches what routeType() reports.
364         return routesMap.getChildNodes();
365     }
366
367     @Override
368     public final YangInstanceIdentifier routePath(final YangInstanceIdentifier routesPath, final PathArgument routeId) {
369         return routesPath.node(this.routesContainerIdentifier).node(routeNid()).node(routeId);
370     }
371
372     @Override
373     public final InstanceIdentifier<R> createRouteIdentifier(
374             final KeyedInstanceIdentifier<Tables, TablesKey> tableIId, final I key) {
375         return tableIId.child(routesCaseClass(), routesContainerClass()).child(routesListClass(), key);
376     }
377
378     @Override
379     public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
380             final ContainerNode nlri) {
381         deleteRoutes(tx, tablePath, nlri, ROUTES);
382     }
383
384     @Override
385     public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
386             final ContainerNode nlri, final ContainerNode attributes) {
387         putRoutes(tx, tablePath, nlri, attributes, ROUTES);
388     }
389
390     @Override
391     public final Update buildUpdate(final Collection<MapEntryNode> advertised, final Collection<MapEntryNode> withdrawn,
392             final Attributes attr) {
393         final UpdateBuilder ub = new UpdateBuilder();
394         final AttributesBuilder ab = new AttributesBuilder(attr);
395         final CNextHop hop = ab.getCNextHop();
396
397         LOG.debug("cnextHop before={}", hop);
398         // do not preserve next hop in attributes if we are using MpReach
399         ab.setCNextHop(null);
400
401         if (!advertised.isEmpty()) {
402             final MpReachNlri mb = buildReach(advertised, hop);
403             ab.addAugmentation(Attributes1.class, new Attributes1Builder().setMpReachNlri(mb).build());
404             LOG.debug("mpreach nexthop={}", mb);
405         }
406         if (!withdrawn.isEmpty()) {
407             final MpUnreachNlri mb = buildUnreach(withdrawn);
408             ab.addAugmentation(Attributes2.class, new Attributes2Builder().setMpUnreachNlri(mb).build());
409             LOG.debug("mpunrach mb={}", mb);
410         }
411
412         ub.setAttributes(ab.build());
413         LOG.debug("update {}", ub.build());
414         return ub.build();
415     }
416
417     @Override
418     @SuppressWarnings("checkstyle:OverloadMethodsDeclarationOrder")
419     public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
420             final ContainerNode nlri, final NodeIdentifier routesNodeId) {
421         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(WITHDRAWN_ROUTES);
422         if (maybeRoutes.isPresent()) {
423             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationContainerIdentifier());
424             if (destination != null) {
425                 deleteDestinationRoutes(tx, tablePath, destination, routesNodeId);
426             }
427         } else {
428             LOG.debug("Withdrawn routes are not present in NLRI {}", nlri);
429         }
430     }
431
432     @Override
433     public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath,
434             final ContainerNode nlri, final ContainerNode attributes, final NodeIdentifier routesNodeId) {
435         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(ADVERTISED_ROUTES);
436         if (maybeRoutes.isPresent()) {
437             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationContainerIdentifier());
438             if (destination != null) {
439                 putDestinationRoutes(tx, tablePath, destination, attributes, routesNodeId);
440             }
441         } else {
442             LOG.debug("Advertized routes are not present in NLRI {}", nlri);
443         }
444     }
445
446     private static final class DeleteRoute implements ApplyRoute {
447         @Override
448         public void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base,
449                 final NodeIdentifierWithPredicates routeKey, final DataContainerNode<?> route,
450                 final ContainerNode attributes) {
451             tx.delete(LogicalDatastoreType.OPERATIONAL, base.node(routeKey));
452         }
453     }
454
455     private final class PutRoute implements ApplyRoute {
456         @Override
457         public void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base,
458                 final NodeIdentifierWithPredicates routeKey, final DataContainerNode<?> route,
459                 final ContainerNode attributes) {
460             // Build the DataContainer data
461             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> b =
462                     ImmutableNodes.mapEntryBuilder();
463             b.withNodeIdentifier(routeKey);
464
465             route.getValue().forEach(b::withChild);
466             // Add attributes
467             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> cb =
468                     Builders.containerBuilder(attributes);
469             cb.withNodeIdentifier(routeAttributesIdentifier());
470             b.withChild(cb.build());
471             tx.put(LogicalDatastoreType.OPERATIONAL, base.node(routeKey), b.build());
472         }
473     }
474
475     protected final NodeIdentifier routePathIdNid() {
476         return this.pathIdNid;
477     }
478
479     protected final QName pathIdQName() {
480         return this.pathIdQname;
481     }
482
483     protected final QName routeKeyQName() {
484         return this.routeKeyQname;
485     }
486
487     protected final String extractPrefix(final DataContainerNode<? extends PathArgument> route) {
488         return (String) route.getChild(prefixTypeNid).get().getValue();
489     }
490
491     protected final RouteDistinguisher extractRouteDistinguisher(
492             final DataContainerNode<? extends PathArgument> route) {
493         if (route.getChild(this.rdNid).isPresent()) {
494             return RouteDistinguisherBuilder.getDefaultInstance((String) route.getChild(this.rdNid).get().getValue());
495         }
496         return null;
497     }
498 }