Remove duplicated code for rib support
[bgpcep.git] / bgp / l3vpn / src / main / java / org / opendaylight / protocol / bgp / l3vpn / AbstractVpnRIBSupport.java
1 /*
2  * Copyright (c) 2016 Brocade Communications 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.l3vpn;
9
10 import com.google.common.base.Optional;
11 import com.google.common.collect.ImmutableCollection;
12 import com.google.common.collect.ImmutableSet;
13 import io.netty.buffer.ByteBuf;
14 import io.netty.buffer.Unpooled;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.List;
19 import java.util.stream.Collectors;
20 import javax.annotation.Nonnull;
21 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
22 import org.opendaylight.protocol.bgp.labeled.unicast.LabeledUnicastRIBSupport;
23 import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBSupport;
24 import org.opendaylight.protocol.util.ByteArray;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlri;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlriBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlri;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlriBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
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.AddressFamily;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.MplsLabeledVpnSubsequentAddressFamily;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RouteDistinguisher;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.RouteDistinguisherBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.next.hop.CNextHop;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.vpn.rev160413.l3vpn.ip.destination.type.VpnDestination;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.vpn.rev160413.l3vpn.ip.destination.type.VpnDestinationBuilder;
41 import org.opendaylight.yangtools.yang.binding.DataObject;
42 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
45 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
48 import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
49 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
51 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
52 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
53 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
54 import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
55 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
56 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 /**
61  * @author Kevin Wang
62  */
63 public abstract class AbstractVpnRIBSupport extends AbstractRIBSupport {
64     private static final Logger LOG = LoggerFactory.getLogger(AbstractVpnRIBSupport.class);
65     private final NodeIdentifier NLRI_ROUTES_LIST;
66     private final NodeIdentifier PREFIX_TYPE_NID;
67     private final NodeIdentifier LABEL_STACK_NID;
68     private final NodeIdentifier LV_NID;
69     private final NodeIdentifier RD_NID;
70     private final NodeIdentifier DESTINATION;
71     private final QName ROUTE_KEY;
72     private final NodeIdentifier ROUTE;
73     private final Class<? extends AddressFamily> ADDRESS_FAMILY_CLAZZ;
74     private final QName CONTAINER_CLASS_QNAME;
75     private final QName LIST_CLASS_QNAME;
76     private final ChoiceNode EMPTY_ROUTES;
77
78     /**
79      * Default constructor. Requires the QName of the container augmented under the routes choice
80      * node in instantiations of the rib grouping. It is assumed that this container is defined by
81      * the same model which populates it with route grouping instantiation, and by extension with
82      * the route attributes container.
83      *
84      * @param cazeClass      Binding class of the AFI/SAFI-specific case statement, must not be null
85      * @param containerClass Binding class of the container in routes choice, must not be null.
86      * @param listClass      Binding class of the route list, nust not be null;
87      */
88     protected AbstractVpnRIBSupport(
89         Class<? extends Routes> cazeClass,
90         Class<? extends DataObject> containerClass,
91         Class<? extends Route> listClass,
92         Class<? extends AddressFamily> addressFamilyClass,
93         final QName VPN_DST_CONTAINER_CLASS_QNAME
94     ) {
95         super(cazeClass, containerClass, listClass);
96         CONTAINER_CLASS_QNAME = BindingReflections.findQName(containerClass).intern();
97         LIST_CLASS_QNAME =
98             QName.create(
99                 CONTAINER_CLASS_QNAME.getNamespace(), CONTAINER_CLASS_QNAME.getRevision(), BindingReflections.findQName(listClass).intern().getLocalName()
100             );
101         ROUTE = NodeIdentifier.create(LIST_CLASS_QNAME);
102         ROUTE_KEY = QName.create(LIST_CLASS_QNAME, "route-key").intern();
103         EMPTY_ROUTES = Builders.choiceBuilder()
104             .withNodeIdentifier(YangInstanceIdentifier.NodeIdentifier.create(Routes.QNAME))
105             .addChild(
106                 Builders.containerBuilder()
107                     .withNodeIdentifier(YangInstanceIdentifier.NodeIdentifier.create(CONTAINER_CLASS_QNAME))
108                     .addChild(
109                         ImmutableNodes.mapNodeBuilder(
110                             LIST_CLASS_QNAME
111                         ).build()
112                     ).build()
113             ).build();
114         final QName VPN_DST_CLASS_QNAME =
115             QName.create(
116                 CONTAINER_CLASS_QNAME.getNamespace(), CONTAINER_CLASS_QNAME.getRevision(), VpnDestination.QNAME.getLocalName()
117             );
118         NLRI_ROUTES_LIST = NodeIdentifier.create(VPN_DST_CLASS_QNAME);
119         PREFIX_TYPE_NID = NodeIdentifier.create(QName.create(VPN_DST_CLASS_QNAME, "prefix").intern());
120         LABEL_STACK_NID = NodeIdentifier.create(QName.create(VPN_DST_CLASS_QNAME, "label-stack").intern());
121         LV_NID = NodeIdentifier.create(QName.create(VPN_DST_CLASS_QNAME, "label-value").intern());
122         RD_NID = NodeIdentifier.create(QName.create(VPN_DST_CLASS_QNAME, "route-distinguisher").intern());
123         DESTINATION = NodeIdentifier.create(VPN_DST_CONTAINER_CLASS_QNAME);
124         ADDRESS_FAMILY_CLAZZ = addressFamilyClass;
125     }
126
127     private VpnDestination extractVpnDestination(DataContainerNode<? extends PathArgument> route) {
128         final VpnDestination dst = new VpnDestinationBuilder()
129             .setPrefix(LabeledUnicastRIBSupport.extractPrefix(route, PREFIX_TYPE_NID))
130             .setLabelStack(LabeledUnicastRIBSupport.extractLabel(route, LABEL_STACK_NID, LV_NID))
131             .setRouteDistinguisher(extractRouteDistinguisher(route))
132             .build();
133         return dst;
134     }
135
136     private RouteDistinguisher extractRouteDistinguisher(final DataContainerNode<? extends YangInstanceIdentifier.PathArgument> route) {
137         if (route.getChild(RD_NID).isPresent()) {
138             return RouteDistinguisherBuilder.getDefaultInstance((String) route.getChild(RD_NID).get().getValue());
139         }
140         return null;
141     }
142
143     @Nonnull
144     @Override
145     public ChoiceNode emptyRoutes() {
146         return EMPTY_ROUTES;
147     }
148
149     @Nonnull
150     @Override
151     protected NodeIdentifier destinationContainerIdentifier() {
152         return DESTINATION;
153     }
154
155     @Override
156     protected void deleteDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, YangInstanceIdentifier.NodeIdentifier routesNodeId) {
157         processDestination(tx, tablePath.node(routesNodeId), destination, null, DELETE_ROUTE);
158     }
159
160     @Override
161     protected void putDestinationRoutes(DOMDataWriteTransaction tx, YangInstanceIdentifier tablePath, ContainerNode destination, ContainerNode attributes, YangInstanceIdentifier.NodeIdentifier routesNodeId) {
162         processDestination(tx, tablePath.node(routesNodeId), destination, attributes, putRoute);
163     }
164
165     protected abstract DestinationType getAdvertizedDestinationType(List<VpnDestination> dests);
166
167     protected abstract DestinationType getWithdrawnDestinationType(List<VpnDestination> dests);
168
169     @Nonnull
170     @Override
171     protected MpReachNlri buildReach(Collection<MapEntryNode> routes, CNextHop hop) {
172         final MpReachNlriBuilder mb = new MpReachNlriBuilder()
173             .setAfi(ADDRESS_FAMILY_CLAZZ)
174             .setSafi(MplsLabeledVpnSubsequentAddressFamily.class)
175             .setCNextHop(hop);
176
177         final List<VpnDestination> dests = new ArrayList<>(routes.size());
178         dests.addAll(routes.stream().map(this::extractVpnDestination).collect(Collectors.toList()));
179
180         mb.setAdvertizedRoutes(
181             new AdvertizedRoutesBuilder().setDestinationType(
182                 getAdvertizedDestinationType(dests)
183             ).build()
184         ).build();
185         return mb.build();
186     }
187
188     @Nonnull
189     @Override
190     protected MpUnreachNlri buildUnreach(Collection<MapEntryNode> routes) {
191         final MpUnreachNlriBuilder mb = new MpUnreachNlriBuilder()
192             .setAfi(ADDRESS_FAMILY_CLAZZ)
193             .setSafi(MplsLabeledVpnSubsequentAddressFamily.class);
194
195         final List<VpnDestination> dests = new ArrayList<>(routes.size());
196         dests.addAll(routes.stream().map(this::extractVpnDestination).collect(Collectors.toList()));
197
198         mb.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(
199             getWithdrawnDestinationType(dests)
200             ).build()
201         ).build();
202         return mb.build();
203     }
204
205     @Nonnull
206     @Override
207     public ImmutableCollection<Class<? extends DataObject>> cacheableAttributeObjects() {
208         return ImmutableSet.of();
209     }
210
211     @Nonnull
212     @Override
213     public ImmutableCollection<Class<? extends DataObject>> cacheableNlriObjects() {
214         return ImmutableSet.of();
215     }
216
217     @Override
218     public boolean isComplexRoute() {
219         return true;
220     }
221
222     private void processDestination(final DOMDataWriteTransaction tx, final YangInstanceIdentifier routesPath,
223                                     final ContainerNode destination, final ContainerNode attributes, final ApplyRoute function) {
224         if (destination != null) {
225             final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = destination.getChild(NLRI_ROUTES_LIST);
226             if (maybeRoutes.isPresent()) {
227                 final DataContainerChild<? extends PathArgument, ?> routes = maybeRoutes.get();
228                 if (routes instanceof UnkeyedListNode) {
229                     UnkeyedListNode routeListNode = (UnkeyedListNode) routes;
230                     LOG.debug("{} routes are found", routeListNode.getSize());
231                     final YangInstanceIdentifier base = routesPath.node(routesContainerIdentifier()).node(ROUTE);
232                     for (final UnkeyedListEntryNode e : routeListNode.getValue()) {
233                         final NodeIdentifierWithPredicates routeKey = createRouteKey(e);
234                         LOG.debug("Route {} is processed.", routeKey);
235                         function.apply(tx, base, routeKey, e, attributes);
236                     }
237                 } else {
238                     LOG.warn("Routes {} are not a map", routes);
239                 }
240             }
241         } else {
242             LOG.debug("Destination is null.");
243         }
244     }
245
246     private NodeIdentifierWithPredicates createRouteKey(final UnkeyedListEntryNode l3vpn) {
247         final ByteBuf buffer = Unpooled.buffer();
248
249         final VpnDestination dest = extractVpnDestination(l3vpn);
250         AbstractVpnNlriParser.serializeNlri(Collections.singletonList(dest), buffer);
251         return new NodeIdentifierWithPredicates(LIST_CLASS_QNAME, ROUTE_KEY, ByteArray.readAllBytes(buffer));
252     }
253 }