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