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