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