Remove duplicated code for rib support
[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 com.google.common.annotations.Beta;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import java.util.Collection;
14 import java.util.Collections;
15 import javax.annotation.Nonnull;
16 import javax.annotation.Nullable;
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
18 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.Update;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.UpdateBuilder;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.Attributes;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev130919.path.attributes.AttributesBuilder;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes1;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes1Builder;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes2;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.Attributes2Builder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlri;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutes;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.Route;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev130925.rib.tables.Routes;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.CNextHop;
35 import org.opendaylight.yangtools.yang.binding.DataObject;
36 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
39 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
40 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
42 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
45 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
46 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
47 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
48 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
49 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
50 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
51 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
52 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 @Beta
57 public abstract class AbstractRIBSupport implements RIBSupport {
58     private static final Logger LOG = LoggerFactory.getLogger(AbstractRIBSupport.class);
59     private static final NodeIdentifier ADVERTIZED_ROUTES = new NodeIdentifier(AdvertizedRoutes.QNAME);
60     private static final NodeIdentifier WITHDRAWN_ROUTES = new NodeIdentifier(WithdrawnRoutes.QNAME);
61     private static final NodeIdentifier DESTINATION_TYPE = new NodeIdentifier(DestinationType.QNAME);
62     private static final NodeIdentifier ROUTES = new NodeIdentifier(Routes.QNAME);
63     protected static final ApplyRoute DELETE_ROUTE = new DeleteRoute();
64     private static final long NON_PATH_ID = 0;
65
66     private final NodeIdentifier routesContainerIdentifier;
67     private final NodeIdentifier routesListIdentifier;
68     private final NodeIdentifier routeAttributesIdentifier;
69     private final Class<? extends Routes> cazeClass;
70     private final Class<? extends DataObject> containerClass;
71     private final Class<? extends Route> listClass;
72     protected final ApplyRoute putRoute = new PutRoute();
73
74     /**
75      * Default constructor. Requires the QName of the container augmented under the routes choice
76      * node in instantiations of the rib grouping. It is assumed that this container is defined by
77      * the same model which populates it with route grouping instantiation, and by extension with
78      * the route attributes container.
79      *
80      * @param cazeClass Binding class of the AFI/SAFI-specific case statement, must not be null
81      * @param containerClass Binding class of the container in routes choice, must not be null.
82      * @param listClass Binding class of the route list, nust not be null;
83      */
84     protected AbstractRIBSupport(final Class<? extends Routes> cazeClass, final Class<? extends DataObject> containerClass, final Class<? extends Route> listClass) {
85         final QName qname = BindingReflections.findQName(containerClass).intern();
86         this.routesContainerIdentifier = new NodeIdentifier(qname);
87         this.routeAttributesIdentifier = new NodeIdentifier(QName.create(qname, Attributes.QNAME.getLocalName().intern()));
88         this.cazeClass = Preconditions.checkNotNull(cazeClass);
89         this.containerClass = Preconditions.checkNotNull(containerClass);
90         this.listClass = Preconditions.checkNotNull(listClass);
91         this.routesListIdentifier = new NodeIdentifier(
92             QName.create(
93                 qname.getNamespace(), qname.getRevision(), BindingReflections.findQName(listClass).intern().getLocalName()
94             )
95         );
96     }
97
98     @Override
99     public final Class<? extends Routes> routesCaseClass() {
100         return this.cazeClass;
101     }
102
103     @Override
104     public final Class<? extends DataObject> routesContainerClass() {
105         return this.containerClass;
106     }
107
108     @Override
109     public final Class<? extends Route> routesListClass() {
110         return this.listClass;
111     }
112
113     /**
114      * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
115      * the RIB routes.
116      *
117      * @return Container identifier, may not be null.
118      */
119     protected final NodeIdentifier routesContainerIdentifier() {
120         return this.routesContainerIdentifier;
121     }
122
123     /**
124      * Return the {@link NodeIdentifier} of the AFI/SAFI-specific container under
125      * the NLRI destination.
126      *
127      * @return Container identifier, may not be null.
128      */
129     @Nonnull protected abstract NodeIdentifier destinationContainerIdentifier();
130
131     /**
132      * Given the destination as ContainerNode, implementation needs to parse the DOM model
133      * from this point onward:
134      *
135      * {@code /bgp-mp:mp-unreach-nlri/bgp-mp:withdrawn-routes/bgp-mp:destination-type }
136      *
137      * and delete the routes from its RIBs.
138      *
139      * @param tx DOMDataWriteTransaction to be passed into implementation
140      * @param tablePath YangInstanceIdentifier to be passed into implementation
141      * @param destination ContainerNode DOM representation of NLRI in Update message
142      * @param routesNodeId NodeIdentifier
143      */
144     protected abstract void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, NodeIdentifier routesNodeId);
145
146     /**
147      * Given the destination as ContainerNode, implementation needs to parse the DOM model
148      * from this point onward:
149      *
150      * {@code /bgp-mp:mp-reach-nlri/bgp-mp:advertized-routes/bgp-mp:destination-type }
151      *
152      * and put the routes to its RIBs.
153      *
154      * @param tx DOMDataWriteTransaction to be passed into implementation
155      * @param tablePath YangInstanceIdentifier to be passed into implementation
156      * @param destination ContainerNode DOM representation of NLRI in Update message
157      * @param attributes ContainerNode to be passed into implementation
158      * @param routesNodeId NodeIdentifier
159      */
160     protected abstract void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, ContainerNode attributes,
161             NodeIdentifier routesNodeId);
162
163     private static ContainerNode getDestination(final DataContainerChild<? extends PathArgument, ?> routes, final NodeIdentifier destinationId) {
164         if (routes instanceof ContainerNode) {
165             final Optional<DataContainerChild<? extends PathArgument, ?>> maybeDestination = ((ContainerNode)routes).getChild(DESTINATION_TYPE);
166             if (maybeDestination.isPresent()) {
167                 final DataContainerChild<? extends PathArgument, ?> destination = maybeDestination.get();
168                 if (destination instanceof ChoiceNode) {
169                     final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRet = ((ChoiceNode)destination).getChild(destinationId);
170                     if (maybeRet.isPresent()) {
171                         final DataContainerChild<? extends PathArgument, ?> ret = maybeRet.get();
172                         if (ret instanceof ContainerNode) {
173                             return (ContainerNode)ret;
174                         } else {
175                             LOG.debug("Specified node {} is not a container, ignoring it", ret);
176                         }
177                     } else {
178                         LOG.debug("Specified container {} is not present in destination {}", destinationId, destination);
179                     }
180                 } else {
181                     LOG.warn("Destination {} is not a choice, ignoring it", destination);
182                 }
183             } else {
184                 LOG.debug("Destination is not present in routes {}", routes);
185             }
186         } else {
187             LOG.warn("Advertized routes {} are not a container, ignoring it", routes);
188         }
189
190         return null;
191     }
192
193     @Override
194     public final NodeIdentifier routeAttributesIdentifier() {
195         return this.routeAttributesIdentifier;
196     }
197
198     @Override
199     public final Collection<DataTreeCandidateNode> changedRoutes(final DataTreeCandidateNode routes) {
200         final DataTreeCandidateNode myRoutes = routes.getModifiedChild(this.routesContainerIdentifier);
201         if (myRoutes == null) {
202             return Collections.emptySet();
203         }
204         final DataTreeCandidateNode routesMap = myRoutes.getModifiedChild(this.routesListIdentifier);
205         if (routesMap == null) {
206             return Collections.emptySet();
207         }
208         // Well, given the remote possibility of augmentation, we should perform a filter here,
209         // to make sure the type matches what routeType() reports.
210         return routesMap.getChildNodes();
211     }
212
213     @Override
214     public final YangInstanceIdentifier routePath(final YangInstanceIdentifier routesPath, final PathArgument routeId) {
215         return routesPath.node(this.routesContainerIdentifier).node(this.routesListIdentifier).node(routeId);
216     }
217
218     @Override
219     public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri) {
220         deleteRoutes(tx, tablePath, nlri, ROUTES);
221     }
222
223     @Override
224     public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri, final ContainerNode attributes) {
225         putRoutes(tx, tablePath, nlri, attributes, ROUTES);
226     }
227
228     /**
229      * Build MpReachNlri object from DOM representation.
230      *
231      * @param routes Collection of MapEntryNode DOM representation of routes
232      * @param hop CNextHop as it was parsed from Attributes, to be included in MpReach object
233      * @return MpReachNlri
234      */
235     @Nonnull protected abstract MpReachNlri buildReach(Collection<MapEntryNode> routes, CNextHop hop);
236
237     /**
238      * Build MpUnReachNlri object from DOM representation.
239      *
240      * @param routes Collection of MapEntryNode DOM representation of routes
241      * @return MpUnreachNlri
242      */
243     @Nonnull protected abstract MpUnreachNlri buildUnreach(Collection<MapEntryNode> routes);
244
245     @Override
246     public Update buildUpdate(final Collection<MapEntryNode> advertised, final Collection<MapEntryNode> withdrawn, final Attributes attr) {
247         final UpdateBuilder ub = new UpdateBuilder();
248         final AttributesBuilder ab = new AttributesBuilder(attr);
249         final CNextHop hop = ab.getCNextHop();
250
251         // do not preserve next hop in attributes if we are using MpReach
252         ab.setCNextHop(null);
253
254         if (!advertised.isEmpty()) {
255             final MpReachNlri mb = buildReach(advertised, hop);
256             ab.addAugmentation(Attributes1.class, new Attributes1Builder().setMpReachNlri(mb).build());
257         }
258         if (!withdrawn.isEmpty()) {
259             final MpUnreachNlri mb = buildUnreach(withdrawn);
260             ab.addAugmentation(Attributes2.class, new Attributes2Builder().setMpUnreachNlri(mb).build());
261         }
262
263         ub.setAttributes(ab.build());
264         return ub.build();
265     }
266
267     @Override
268     public final void deleteRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri,
269             final NodeIdentifier routesNodeId) {
270         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(WITHDRAWN_ROUTES);
271         if (maybeRoutes.isPresent()) {
272             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationContainerIdentifier());
273             if (destination != null) {
274                 deleteDestinationRoutes(tx, tablePath, destination, routesNodeId);
275             }
276         } else {
277             LOG.debug("Withdrawn routes are not present in NLRI {}", nlri);
278         }
279     }
280
281     @Override
282     public final void putRoutes(final DOMDataWriteTransaction tx, final YangInstanceIdentifier tablePath, final ContainerNode nlri,
283             final ContainerNode attributes, final NodeIdentifier routesNodeId) {
284         final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = nlri.getChild(ADVERTIZED_ROUTES);
285         if (maybeRoutes.isPresent()) {
286             final ContainerNode destination = getDestination(maybeRoutes.get(), destinationContainerIdentifier());
287             if (destination != null) {
288                 putDestinationRoutes(tx, tablePath, destination, attributes, routesNodeId);
289             }
290         } else {
291             LOG.debug("Advertized routes are not present in NLRI {}", nlri);
292         }
293     }
294
295     @Nullable
296     @Override
297     /**
298      * Return null for non supporting Add path models
299      */
300     public PathArgument getRouteIdAddPath(final long pathId, final PathArgument routeId) {
301         return null;
302     }
303
304     @Override
305     public Long extractPathId(final NormalizedNode<?, ?> data) {
306         return NON_PATH_ID;
307     }
308
309
310     private static class DeleteRoute implements ApplyRoute {
311         @Override
312         public void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base, final NodeIdentifierWithPredicates routeKey,
313             final DataContainerNode<?> route, final ContainerNode attributes) {
314             tx.delete(LogicalDatastoreType.OPERATIONAL, base.node(routeKey));
315         }
316     }
317
318     private final class PutRoute implements ApplyRoute {
319         @Override
320         public void apply(final DOMDataWriteTransaction tx, final YangInstanceIdentifier base, final NodeIdentifierWithPredicates routeKey,
321             final DataContainerNode<?> route, final ContainerNode attributes) {
322             // Build the DataContainer data
323             final DataContainerNodeBuilder<NodeIdentifierWithPredicates, MapEntryNode> b = ImmutableNodes.mapEntryBuilder();
324             b.withNodeIdentifier(routeKey);
325
326             route.getValue().forEach(b::withChild);
327             // Add attributes
328             final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> cb = Builders.containerBuilder(attributes);
329             cb.withNodeIdentifier(routeAttributesIdentifier());
330             b.withChild(cb.build());
331             tx.put(LogicalDatastoreType.OPERATIONAL, base.node(routeKey), b.build());
332         }
333     }
334 }