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