L3VPN MULTICAST 82/72482/8
authorClaudio D. Gasparini <claudio.gasparini@pantheon.tech>
Mon, 28 May 2018 10:54:42 +0000 (12:54 +0200)
committerClaudio D. Gasparini <claudio.gasparini@pantheon.tech>
Thu, 14 Jun 2018 11:23:46 +0000 (13:23 +0200)
- RFC6513
- RFC6514

JIRA: BGPCEP-396
Change-Id: I7d4a7628d8a32f4b00b28a4ee4ad0f926a8f0070
Signed-off-by: Claudio D. Gasparini <claudio.gasparini@pantheon.tech>
17 files changed:
bgp/config-example/src/main/resources/initial/protocols-config.xml
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/BGPActivator.java
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/RIBActivator.java
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/TableTypeActivator.java
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/AbstractL3vpnMcastIpRIBSupport.java [new file with mode: 0644]
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv4RIBSupport.java [new file with mode: 0644]
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv6RIBSupport.java [new file with mode: 0644]
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv4NlriHandler.java [new file with mode: 0644]
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv6NlriHandler.java [new file with mode: 0644]
bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializer.java [new file with mode: 0644]
bgp/l3vpn/src/main/yang/bgp-l3vpn-mcast.yang [new file with mode: 0644]
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/BGPActivatorTest.java [new file with mode: 0644]
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/RIBActivatorTest.java [new file with mode: 0644]
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/TableTypeActivatorTest.java
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv4RIBSupportTest.java [new file with mode: 0644]
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv6RIBSupportTest.java [new file with mode: 0644]
bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializerTest.java [new file with mode: 0644]

index 86639d4d64ebd30279446e614b7168304974b4c4..3b9f58418721e392b08b40ee094bba8773dbaa01 100644 (file)
                     <afi-safi>
                         <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-UNICAST</afi-safi-name>
                     </afi-safi>
+                    <afi-safi>
+                        <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV4-MULTICAST</afi-safi-name>
+                    </afi-safi>
+                    <afi-safi>
+                        <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-MULTICAST</afi-safi-name>
+                    </afi-safi>
                     <afi-safi>
                         <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L2VPN-EVPN</afi-safi-name>
                     </afi-safi>
                         <afi-safi>
                             <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-UNICAST</afi-safi-name>
                         </afi-safi>
+                        <afi-safi>
+                            <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV4-MULTICAST</afi-safi-name>
+                        </afi-safi>
+                        <afi-safi>
+                            <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-MULTICAST</afi-safi-name>
+                        </afi-safi>
                         <afi-safi>
                             <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L2VPN-EVPN</afi-safi-name>
                         </afi-safi>
                         <afi-safi>
                             <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-UNICAST</afi-safi-name>
                         </afi-safi>
+                        <afi-safi>
+                            <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV4-MULTICAST</afi-safi-name>
+                        </afi-safi>
+                        <afi-safi>
+                            <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L3VPN-IPV6-MULTICAST</afi-safi-name>
+                        </afi-safi>
                         <afi-safi>
                             <afi-safi-name xmlns:x="http://openconfig.net/yang/bgp-types">x:L2VPN-EVPN</afi-safi-name>
                         </afi-safi>
index dabf2ec8d05b5e1c7c21145651d2f7c655b2d4f0..46c8f7e303af984118b06add48cf67dfb6ea90f1 100644 (file)
@@ -8,14 +8,22 @@
 
 package org.opendaylight.protocol.bgp.l3vpn;
 
+import com.google.common.annotations.VisibleForTesting;
 import java.util.ArrayList;
 import java.util.List;
+import org.opendaylight.protocol.bgp.inet.codec.nexthop.Ipv4NextHopParserSerializer;
+import org.opendaylight.protocol.bgp.inet.codec.nexthop.Ipv6NextHopParserSerializer;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.nlri.L3vpnMcastIpv4NlriHandler;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.nlri.L3vpnMcastIpv6NlriHandler;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv4.VpnIpv4NextHopParserSerializer;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv4.VpnIpv4NlriParser;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv6.VpnIpv6NextHopParserSerializer;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv6.VpnIpv6NlriParser;
 import org.opendaylight.protocol.bgp.parser.spi.AbstractBGPExtensionProviderActivator;
 import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv4.L3vpnMcastRoutesIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv6.L3vpnMcastRoutesIpv6;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
@@ -30,6 +38,10 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.vpn.
  * @author Claudio D. Gasparini
  */
 public final class BGPActivator extends AbstractBGPExtensionProviderActivator {
+    @VisibleForTesting
+    static final int MCAST_L3VPN_SAFI = 129;
+
+
     private static void registerNlri(
             final BGPExtensionProviderContext context,
             final List<AutoCloseable> regs) {
@@ -46,11 +58,30 @@ public final class BGPActivator extends AbstractBGPExtensionProviderActivator {
         regs.add(context.registerNlriParser(Ipv6AddressFamily.class, MplsLabeledVpnSubsequentAddressFamily.class,
                 vpnIpv6NlriParser, vpnIpv6NextHopParserSerializer, Ipv6NextHopCase.class));
         regs.add(context.registerNlriSerializer(VpnIpv6Routes.class, vpnIpv6NlriParser));
+
+        final L3vpnMcastIpv4NlriHandler l3vpnMcastIpv4NlriHandler = new L3vpnMcastIpv4NlriHandler();
+        final Ipv4NextHopParserSerializer ipv4NextHopParser = new Ipv4NextHopParserSerializer();
+        regs.add(context.registerNlriParser(Ipv4AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class,
+                l3vpnMcastIpv4NlriHandler, ipv4NextHopParser, Ipv4NextHopCase.class));
+        regs.add(context.registerNlriSerializer(L3vpnMcastRoutesIpv4.class, l3vpnMcastIpv4NlriHandler));
+
+
+        final L3vpnMcastIpv6NlriHandler l3vpnMcastIpv6NlriHandler = new L3vpnMcastIpv6NlriHandler();
+        final Ipv6NextHopParserSerializer ipv6NextHopParser = new Ipv6NextHopParserSerializer();
+        regs.add(context.registerNlriParser(Ipv6AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class,
+                l3vpnMcastIpv6NlriHandler, ipv6NextHopParser, Ipv6NextHopCase.class));
+        regs.add(context.registerNlriSerializer(L3vpnMcastRoutesIpv6.class, l3vpnMcastIpv6NlriHandler));
+    }
+
+    private static void registerAfiSafi(final BGPExtensionProviderContext context, final List<AutoCloseable> regs) {
+        regs.add(context.registerSubsequentAddressFamily(McastMplsLabeledVpnSubsequentAddressFamily.class,
+                MCAST_L3VPN_SAFI));
     }
 
     @Override
     protected List<AutoCloseable> startImpl(final BGPExtensionProviderContext context) {
         final List<AutoCloseable> regs = new ArrayList<>();
+        registerAfiSafi(context, regs);
         registerNlri(context, regs);
         return regs;
     }
index 2734e9f37d6e007eb17792aa46672ad825a7a7e0..e18051cb70cb47c757f599d7872724f0d642ea6b 100644 (file)
@@ -11,10 +11,13 @@ package org.opendaylight.protocol.bgp.l3vpn;
 import com.google.common.collect.Lists;
 import java.util.List;
 import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.L3VpnMcastIpv4RIBSupport;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.L3VpnMcastIpv6RIBSupport;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv4.VpnIpv4RIBSupport;
 import org.opendaylight.protocol.bgp.l3vpn.unicast.ipv6.VpnIpv6RIBSupport;
 import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBExtensionProviderActivator;
 import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
@@ -32,7 +35,11 @@ public final class RIBActivator extends AbstractRIBExtensionProviderActivator {
                 context.registerRIBSupport(Ipv4AddressFamily.class,
                         MplsLabeledVpnSubsequentAddressFamily.class, VpnIpv4RIBSupport.getInstance(mappingService)),
                 context.registerRIBSupport(Ipv6AddressFamily.class,
-                        MplsLabeledVpnSubsequentAddressFamily.class, VpnIpv6RIBSupport.getInstance(mappingService))
+                        MplsLabeledVpnSubsequentAddressFamily.class, VpnIpv6RIBSupport.getInstance(mappingService)),
+                context.registerRIBSupport(Ipv4AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class,
+                        L3VpnMcastIpv4RIBSupport.getInstance(mappingService)),
+                context.registerRIBSupport(Ipv6AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class,
+                        L3VpnMcastIpv6RIBSupport.getInstance(mappingService))
         );
     }
 }
index 76d20f5c0b9c91262bed141f55295321d5813710..46e369aeef02faf13318e6ac84cda12bfcc70c9a 100644 (file)
@@ -12,8 +12,11 @@ import com.google.common.collect.Lists;
 import java.util.List;
 import org.opendaylight.protocol.bgp.openconfig.spi.AbstractBGPTableTypeRegistryProviderActivator;
 import org.opendaylight.protocol.bgp.openconfig.spi.BGPTableTypeRegistryProvider;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV4MULTICAST;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV4UNICAST;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV6MULTICAST;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV6UNICAST;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.MplsLabeledVpnSubsequentAddressFamily;
@@ -32,7 +35,11 @@ public final class TableTypeActivator extends AbstractBGPTableTypeRegistryProvid
                 provider.registerBGPTableType(Ipv4AddressFamily.class,
                         MplsLabeledVpnSubsequentAddressFamily.class, L3VPNIPV4UNICAST.class),
                 provider.registerBGPTableType(Ipv6AddressFamily.class,
-                        MplsLabeledVpnSubsequentAddressFamily.class, L3VPNIPV6UNICAST.class)
+                        MplsLabeledVpnSubsequentAddressFamily.class, L3VPNIPV6UNICAST.class),
+                provider.registerBGPTableType(Ipv4AddressFamily.class,
+                        McastMplsLabeledVpnSubsequentAddressFamily.class, L3VPNIPV4MULTICAST.class),
+                provider.registerBGPTableType(Ipv6AddressFamily.class,
+                        McastMplsLabeledVpnSubsequentAddressFamily.class, L3VPNIPV6MULTICAST.class)
         );
     }
 }
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/AbstractL3vpnMcastIpRIBSupport.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/AbstractL3vpnMcastIpRIBSupport.java
new file mode 100644 (file)
index 0000000..304283e
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast;
+
+import com.google.common.collect.ImmutableCollection;
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import org.opendaylight.bgp.concepts.RouteDistinguisherUtil;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
+import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBSupport;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.L3vpnMcastRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
+import org.opendaylight.yangtools.yang.binding.ChildOf;
+import org.opendaylight.yangtools.yang.binding.ChoiceIn;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Abstract L3VPN Multicast RIBSupport.
+ *
+ * @author Claudio D. Gasparini
+ */
+abstract class AbstractL3vpnMcastIpRIBSupport<
+        C extends Routes & DataObject & ChoiceIn<Tables>,
+        S extends ChildOf<? super C> & L3vpnMcastRoutes>
+        extends AbstractRIBSupport<C, S, L3vpnMcastRoute, L3vpnMcastRouteKey> {
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractL3vpnMcastIpRIBSupport.class);
+    private final NodeIdentifier nlriRoutesList;
+    private final NodeIdentifier rdNid;
+    private final ImmutableCollection<Class<? extends DataObject>> cacheableNlriObjects;
+
+    /**
+     * Default constructor. Requires the QName of the container augmented under the routes choice
+     * node in instantiations of the rib grouping. It is assumed that this container is defined by
+     * the same model which populates it with route grouping instantiation, and by extension with
+     * the route attributes container.
+     *
+     * @param mappingService     Serialization service
+     * @param cazeClass          Binding class of the AFI/SAFI-specific case statement, must not be null
+     * @param afiClass           address Family Class
+     * @param destContainerQname destination container Qname
+     * @param destListQname      destinations list Qname
+     */
+    AbstractL3vpnMcastIpRIBSupport(
+            final BindingNormalizedNodeSerializer mappingService,
+            final Class<C> cazeClass,
+            final Class<S> containerClass,
+            final Class<? extends AddressFamily> afiClass,
+            final QName destContainerQname,
+            final QName destListQname) {
+        super(mappingService, cazeClass, containerClass, L3vpnMcastRoute.class, afiClass,
+                McastMplsLabeledVpnSubsequentAddressFamily.class, destContainerQname);
+        final QNameModule module = BindingReflections.getQNameModule(cazeClass);
+        this.nlriRoutesList = NodeIdentifier.create(destListQname);
+        this.rdNid = new NodeIdentifier(QName.create(module, "route-distinguisher"));
+        this.cacheableNlriObjects = ImmutableSet.of(cazeClass);
+
+    }
+
+    @Override
+    public final ImmutableCollection<Class<? extends DataObject>> cacheableNlriObjects() {
+        return this.cacheableNlriObjects;
+    }
+
+    protected abstract IpPrefix createPrefix(String prefix);
+
+    @Override
+    public final L3vpnMcastRoute createRoute(final L3vpnMcastRoute route, final String routeKey, final long pathId,
+            final Attributes attributes) {
+        final L3vpnMcastRouteBuilder builder;
+        if (route != null) {
+            builder = new L3vpnMcastRouteBuilder(route);
+        } else {
+            builder = new L3vpnMcastRouteBuilder();
+        }
+        return builder.withKey(createRouteListKey(pathId, routeKey)).setAttributes(attributes).build();
+    }
+
+    @Override
+    public final L3vpnMcastRouteKey createRouteListKey(final long pathId, final String routeKey) {
+        return new L3vpnMcastRouteKey(new PathId(pathId), routeKey);
+    }
+
+    @Override
+    protected final void processDestination(
+            DOMDataWriteTransaction tx,
+            final YangInstanceIdentifier routesPath,
+            final ContainerNode destination,
+            final ContainerNode attributes,
+            final ApplyRoute function) {
+        if (destination != null) {
+            final Optional<DataContainerChild<? extends PathArgument, ?>> maybeRoutes = destination
+                    .getChild(nlriRoutesList);
+            if (maybeRoutes.isPresent()) {
+                final DataContainerChild<? extends PathArgument, ?> routes = maybeRoutes.get();
+                if (routes instanceof UnkeyedListNode) {
+                    final YangInstanceIdentifier base = routesPath.node(routesContainerIdentifier()).node(routeNid());
+                    for (final UnkeyedListEntryNode l3vpnDest : ((UnkeyedListNode) routes).getValue()) {
+                        final YangInstanceIdentifier.NodeIdentifierWithPredicates routeKey = createRouteKey(l3vpnDest);
+                        function.apply(tx, base, routeKey, l3vpnDest, attributes);
+                    }
+                } else {
+                    LOG.warn("Routes {} are not a map", routes);
+                }
+            }
+        }
+    }
+
+
+    final List<L3vpnMcastDestination> extractRoutes(final Collection<MapEntryNode> routes) {
+        return routes.stream().map(this::extractDestinations).collect(Collectors.toList());
+    }
+
+    final L3vpnMcastDestination extractDestinations(
+            final DataContainerNode<? extends PathArgument> destination) {
+        return new L3vpnMcastDestinationBuilder()
+                .setRouteDistinguisher(RouteDistinguisherUtil.extractRouteDistinguisher(destination, rdNid))
+                .setPrefix(createPrefix(extractPrefix(destination)))
+                .setPathId(PathIdUtil.buildPathId(destination, routePathIdNid()))
+                .build();
+    }
+
+    abstract NodeIdentifierWithPredicates createRouteKey(UnkeyedListEntryNode l3vpn);
+}
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv4RIBSupport.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv4RIBSupport.java
new file mode 100644 (file)
index 0000000..5c321c0
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.nlri.L3vpnMcastNlriSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
+import org.opendaylight.protocol.util.ByteArray;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv4Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv4CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv4.L3vpnMcastRoutesIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv4.L3vpnMcastRoutesIpv4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv4.advertized._case.DestinationIpv4L3vpnMcast;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv4.advertized._case.DestinationIpv4L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+
+/**
+ * Ipv4 L3VPN Multicast RIBSupport.
+ *
+ * @author Claudio D. Gasparini
+ */
+public final class L3VpnMcastIpv4RIBSupport
+        extends AbstractL3vpnMcastIpRIBSupport<L3vpnMcastRoutesIpv4Case, L3vpnMcastRoutesIpv4> {
+    private static final L3vpnMcastRoutesIpv4 EMPTY_CONTAINER
+            = new L3vpnMcastRoutesIpv4Builder().setL3vpnMcastRoute(Collections.emptyList()).build();
+    private static final L3vpnMcastRoutesIpv4Case EMPTY_CASE
+            = new L3vpnMcastRoutesIpv4CaseBuilder().setL3vpnMcastRoutesIpv4(EMPTY_CONTAINER).build();
+    private static L3VpnMcastIpv4RIBSupport SINGLETON;
+
+
+    private L3VpnMcastIpv4RIBSupport(final BindingNormalizedNodeSerializer mappingService) {
+        super(mappingService,
+                L3vpnMcastRoutesIpv4Case.class,
+                L3vpnMcastRoutesIpv4.class,
+                Ipv4AddressFamily.class,
+                DestinationIpv4L3vpnMcast.QNAME,
+                L3vpnMcastDestination.QNAME);
+    }
+
+    public static synchronized L3VpnMcastIpv4RIBSupport getInstance(
+            final BindingNormalizedNodeSerializer mappingService) {
+        if (SINGLETON == null) {
+            SINGLETON = new L3VpnMcastIpv4RIBSupport(mappingService);
+        }
+        return SINGLETON;
+    }
+
+
+    @Override
+    protected DestinationL3vpnMcastIpv4AdvertizedCase buildDestination(final Collection<MapEntryNode> routes) {
+        return new DestinationL3vpnMcastIpv4AdvertizedCaseBuilder().setDestinationIpv4L3vpnMcast(
+                new DestinationIpv4L3vpnMcastBuilder().setL3vpnMcastDestination(extractRoutes(routes)).build()).build();
+    }
+
+    @Override
+    protected DestinationL3vpnMcastIpv4WithdrawnCase buildWithdrawnDestination(final Collection<MapEntryNode> routes) {
+        return new DestinationL3vpnMcastIpv4WithdrawnCaseBuilder().setDestinationIpv4L3vpnMcast(
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update
+                        .attributes.mp.unreach.nlri.withdrawn.routes.destination.type.destination.l3vpn.mcast.ipv4
+                        .withdrawn._case.DestinationIpv4L3vpnMcastBuilder()
+                        .setL3vpnMcastDestination(extractRoutes(routes)).build()).build();
+    }
+
+    @Override
+    public L3vpnMcastRoutesIpv4Case emptyRoutesCase() {
+        return EMPTY_CASE;
+    }
+
+    @Override
+    public L3vpnMcastRoutesIpv4 emptyRoutesContainer() {
+        return EMPTY_CONTAINER;
+    }
+
+    @Override
+    protected IpPrefix createPrefix(final String prefix) {
+        return new IpPrefix(new Ipv4Prefix(prefix));
+    }
+
+    @Override
+    public NodeIdentifierWithPredicates createRouteKey(final UnkeyedListEntryNode l3vpn) {
+        final ByteBuf buffer = Unpooled.buffer();
+        final L3vpnMcastDestination dest = extractDestinations(l3vpn);
+        L3vpnMcastNlriSerializer.serializeNlri(Collections.singletonList(dest), buffer);
+        final Optional<DataContainerChild<? extends PathArgument, ?>> maybePathIdLeaf =
+                l3vpn.getChild(routePathIdNid());
+        return PathIdUtil.createNidKey(routeQName(), routeKeyQName(),
+                pathIdQName(), ByteArray.encodeBase64(buffer), maybePathIdLeaf);
+    }
+}
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv6RIBSupport.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3VpnMcastIpv6RIBSupport.java
new file mode 100644 (file)
index 0000000..142de74
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Optional;
+import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeSerializer;
+import org.opendaylight.protocol.bgp.l3vpn.mcast.nlri.L3vpnMcastNlriSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
+import org.opendaylight.protocol.util.ByteArray;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv6Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv6CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv6.L3vpnMcastRoutesIpv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv6.L3vpnMcastRoutesIpv6Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv6.advertized._case.DestinationIpv6L3vpnMcast;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv6.advertized._case.DestinationIpv6L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+
+/**
+ * Ipv6 L3VPN Multicast RIBSupport.
+ *
+ * @author Claudio D. Gasparini
+ */
+public final class L3VpnMcastIpv6RIBSupport
+        extends AbstractL3vpnMcastIpRIBSupport<L3vpnMcastRoutesIpv6Case, L3vpnMcastRoutesIpv6> {
+    private static final L3vpnMcastRoutesIpv6 EMPTY_CONTAINER
+            = new L3vpnMcastRoutesIpv6Builder().setL3vpnMcastRoute(Collections.emptyList()).build();
+    private static final L3vpnMcastRoutesIpv6Case EMPTY_CASE
+            = new L3vpnMcastRoutesIpv6CaseBuilder().setL3vpnMcastRoutesIpv6(EMPTY_CONTAINER).build();
+    private static L3VpnMcastIpv6RIBSupport SINGLETON;
+
+
+    private L3VpnMcastIpv6RIBSupport(final BindingNormalizedNodeSerializer mappingService) {
+        super(mappingService,
+                L3vpnMcastRoutesIpv6Case.class,
+                L3vpnMcastRoutesIpv6.class,
+                Ipv6AddressFamily.class,
+                DestinationIpv6L3vpnMcast.QNAME,
+                L3vpnMcastDestination.QNAME);
+    }
+
+    public static synchronized L3VpnMcastIpv6RIBSupport getInstance(
+            final BindingNormalizedNodeSerializer mappingService) {
+        if (SINGLETON == null) {
+            SINGLETON = new L3VpnMcastIpv6RIBSupport(mappingService);
+        }
+        return SINGLETON;
+    }
+
+
+    @Override
+    protected DestinationL3vpnMcastIpv6AdvertizedCase buildDestination(final Collection<MapEntryNode> routes) {
+        return new DestinationL3vpnMcastIpv6AdvertizedCaseBuilder().setDestinationIpv6L3vpnMcast(
+                new DestinationIpv6L3vpnMcastBuilder().setL3vpnMcastDestination(extractRoutes(routes)).build()).build();
+    }
+
+    @Override
+    protected DestinationL3vpnMcastIpv6WithdrawnCase buildWithdrawnDestination(final Collection<MapEntryNode> routes) {
+        return new DestinationL3vpnMcastIpv6WithdrawnCaseBuilder().setDestinationIpv6L3vpnMcast(
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update
+                        .attributes.mp.unreach.nlri.withdrawn.routes.destination.type.destination.l3vpn.mcast.ipv6
+                        .withdrawn._case.DestinationIpv6L3vpnMcastBuilder()
+                        .setL3vpnMcastDestination(extractRoutes(routes)).build()).build();
+    }
+
+    @Override
+    public L3vpnMcastRoutesIpv6Case emptyRoutesCase() {
+        return EMPTY_CASE;
+    }
+
+    @Override
+    public L3vpnMcastRoutesIpv6 emptyRoutesContainer() {
+        return EMPTY_CONTAINER;
+    }
+
+    @Override
+    protected IpPrefix createPrefix(final String prefix) {
+        return new IpPrefix(new Ipv6Prefix(prefix));
+    }
+
+    @Override
+    public NodeIdentifierWithPredicates createRouteKey(final UnkeyedListEntryNode l3vpn) {
+        final ByteBuf buffer = Unpooled.buffer();
+        final L3vpnMcastDestination dest = extractDestinations(l3vpn);
+        L3vpnMcastNlriSerializer.serializeNlri(Collections.singletonList(dest), buffer);
+        final Optional<DataContainerChild<? extends PathArgument, ?>> maybePathIdLeaf =
+                l3vpn.getChild(routePathIdNid());
+        return PathIdUtil.createNidKey(routeQName(), routeKeyQName(),
+                pathIdQName(), ByteArray.encodeBase64(buffer), maybePathIdLeaf);
+    }
+}
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv4NlriHandler.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv4NlriHandler.java
new file mode 100644 (file)
index 0000000..d7378a3
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast.nlri;
+
+import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupportUtil;
+import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
+import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv4.advertized._case.DestinationIpv4L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * https://tools.ietf.org/html/rfc6514#section-4.
+ *
+ * @author Claudio D. Gasparini
+ */
+public final class L3vpnMcastIpv4NlriHandler implements NlriParser, NlriSerializer {
+    @Override
+    public void parseNlri(
+            final ByteBuf nlri,
+            final MpReachNlriBuilder builder,
+            final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+        if (!nlri.isReadable()) {
+            return;
+        }
+        final boolean mPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint,
+                new BgpTableTypeImpl(builder.getAfi(), builder.getSafi()));
+        builder.setAdvertizedRoutes(new AdvertizedRoutesBuilder()
+                .setDestinationType(new DestinationL3vpnMcastIpv4AdvertizedCaseBuilder()
+                        .setDestinationIpv4L3vpnMcast(new DestinationIpv4L3vpnMcastBuilder()
+                                .setL3vpnMcastDestination(L3vpnMcastNlriSerializer.extractDest(nlri, mPathSupported))
+                                .build()).build()).build());
+    }
+
+
+    @Override
+    public void parseNlri(
+            final ByteBuf nlri,
+            final MpUnreachNlriBuilder builder,
+            final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+        if (!nlri.isReadable()) {
+            return;
+        }
+        final boolean mPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint,
+                new BgpTableTypeImpl(builder.getAfi(), builder.getSafi()));
+        builder.setWithdrawnRoutes(new WithdrawnRoutesBuilder()
+                .setDestinationType(new DestinationL3vpnMcastIpv4WithdrawnCaseBuilder().setDestinationIpv4L3vpnMcast(
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417
+                                .update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.destination.l3vpn
+                                .mcast.ipv4.withdrawn._case.DestinationIpv4L3vpnMcastBuilder()
+                                .setL3vpnMcastDestination(L3vpnMcastNlriSerializer.extractDest(nlri, mPathSupported))
+                                .build()).build()).build());
+    }
+
+    @Override
+    public void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
+        Preconditions.checkArgument(attribute instanceof Attributes,
+                "Attribute parameter is not a Attributes object");
+        final Attributes pathAttributes = (Attributes) attribute;
+        final Attributes1 pathAttributes1 = pathAttributes.augmentation(Attributes1.class);
+        final Attributes2 pathAttributes2 = pathAttributes.augmentation(Attributes2.class);
+        if (pathAttributes1 != null) {
+            final AdvertizedRoutes routes = pathAttributes1.getMpReachNlri().getAdvertizedRoutes();
+            if (routes != null && routes.getDestinationType() instanceof DestinationL3vpnMcastIpv4AdvertizedCase) {
+                final DestinationL3vpnMcastIpv4AdvertizedCase reach
+                        = (DestinationL3vpnMcastIpv4AdvertizedCase) routes.getDestinationType();
+                L3vpnMcastNlriSerializer.serializeNlri(reach.getDestinationIpv4L3vpnMcast().getL3vpnMcastDestination(),
+                        byteAggregator);
+            }
+        } else if (pathAttributes2 != null) {
+            final WithdrawnRoutes withdrawnRoutes = pathAttributes2.getMpUnreachNlri().getWithdrawnRoutes();
+            if (withdrawnRoutes != null
+                    && withdrawnRoutes.getDestinationType() instanceof DestinationL3vpnMcastIpv4WithdrawnCase) {
+                final DestinationL3vpnMcastIpv4WithdrawnCase reach
+                        = (DestinationL3vpnMcastIpv4WithdrawnCase) withdrawnRoutes.getDestinationType();
+                L3vpnMcastNlriSerializer.serializeNlri(reach.getDestinationIpv4L3vpnMcast().getL3vpnMcastDestination(),
+                        byteAggregator);
+            }
+        }
+    }
+}
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv6NlriHandler.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastIpv6NlriHandler.java
new file mode 100644 (file)
index 0000000..9463c31
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast.nlri;
+
+import com.google.common.base.Preconditions;
+import io.netty.buffer.ByteBuf;
+import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
+import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupportUtil;
+import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
+import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv6.advertized._case.DestinationIpv6L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * https://tools.ietf.org/html/rfc6514#section-4.
+ *
+ * @author Claudio D. Gasparini
+ */
+public final class L3vpnMcastIpv6NlriHandler implements NlriParser, NlriSerializer {
+    @Override
+    public void parseNlri(
+            final ByteBuf nlri,
+            final MpReachNlriBuilder builder,
+            final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+        if (!nlri.isReadable()) {
+            return;
+        }
+        final boolean mPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint,
+                new BgpTableTypeImpl(builder.getAfi(), builder.getSafi()));
+
+        builder.setAdvertizedRoutes(new AdvertizedRoutesBuilder().setDestinationType(
+                new DestinationL3vpnMcastIpv6AdvertizedCaseBuilder().setDestinationIpv6L3vpnMcast(
+                        new DestinationIpv6L3vpnMcastBuilder().setL3vpnMcastDestination(
+                                L3vpnMcastNlriSerializer.extractDest(nlri, mPathSupported)).build()).build()).build());
+    }
+
+
+    @Override
+    public void parseNlri(
+            final ByteBuf nlri,
+            final MpUnreachNlriBuilder builder,
+            final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+        if (!nlri.isReadable()) {
+            return;
+        }
+        final boolean mPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint,
+                new BgpTableTypeImpl(builder.getAfi(), builder.getSafi()));
+        builder.setWithdrawnRoutes(new WithdrawnRoutesBuilder()
+                .setDestinationType(new DestinationL3vpnMcastIpv6WithdrawnCaseBuilder().setDestinationIpv6L3vpnMcast(
+                        new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417
+                                .update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.destination.l3vpn
+                                .mcast.ipv6.withdrawn._case.DestinationIpv6L3vpnMcastBuilder()
+                                .setL3vpnMcastDestination(L3vpnMcastNlriSerializer.extractDest(nlri, mPathSupported))
+                                .build()).build()).build());
+    }
+
+    @Override
+    public void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
+        Preconditions.checkArgument(attribute instanceof Attributes,
+                "Attribute parameter is not a Attributes object");
+        final Attributes pathAttributes = (Attributes) attribute;
+        final Attributes1 pathAttributes1 = pathAttributes.augmentation(Attributes1.class);
+        final Attributes2 pathAttributes2 = pathAttributes.augmentation(Attributes2.class);
+        if (pathAttributes1 != null) {
+            final AdvertizedRoutes routes = pathAttributes1.getMpReachNlri().getAdvertizedRoutes();
+            if (routes != null && routes.getDestinationType() instanceof DestinationL3vpnMcastIpv6AdvertizedCase) {
+                final DestinationL3vpnMcastIpv6AdvertizedCase reach
+                        = (DestinationL3vpnMcastIpv6AdvertizedCase) routes.getDestinationType();
+                L3vpnMcastNlriSerializer.serializeNlri(reach.getDestinationIpv6L3vpnMcast().getL3vpnMcastDestination(),
+                        byteAggregator);
+            }
+        } else if (pathAttributes2 != null) {
+            final WithdrawnRoutes withdrawnRoutes = pathAttributes2.getMpUnreachNlri().getWithdrawnRoutes();
+            if (withdrawnRoutes != null
+                    && withdrawnRoutes.getDestinationType() instanceof DestinationL3vpnMcastIpv6WithdrawnCase) {
+                final DestinationL3vpnMcastIpv6WithdrawnCase reach
+                        = (DestinationL3vpnMcastIpv6WithdrawnCase) withdrawnRoutes.getDestinationType();
+                L3vpnMcastNlriSerializer.serializeNlri(reach.getDestinationIpv6L3vpnMcast().getL3vpnMcastDestination(),
+                        byteAggregator);
+            }
+        }
+    }
+}
diff --git a/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializer.java b/bgp/l3vpn/src/main/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializer.java
new file mode 100644 (file)
index 0000000..6c7bdb3
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast.nlri;
+
+import static org.opendaylight.protocol.util.Ipv6Util.IPV6_LENGTH;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.bgp.concepts.RouteDistinguisherUtil;
+import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
+import org.opendaylight.protocol.util.Ipv4Util;
+import org.opendaylight.protocol.util.Ipv6Util;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestinationBuilder;
+
+public final class L3vpnMcastNlriSerializer {
+    private L3vpnMcastNlriSerializer() {
+        throw new UnsupportedOperationException();
+    }
+
+    static List<L3vpnMcastDestination> extractDest(final ByteBuf nlri, final boolean addPath) {
+        List<L3vpnMcastDestination> dests = new ArrayList<>();
+        while (nlri.isReadable()) {
+            final L3vpnMcastDestinationBuilder builder = new L3vpnMcastDestinationBuilder();
+            if (addPath) {
+                builder.setPathId(PathIdUtil.readPathId(nlri));
+            }
+            final int length = nlri.readUnsignedByte();
+            builder.setRouteDistinguisher(RouteDistinguisherUtil.parseRouteDistinguisher(nlri));
+            if (length == IPV6_LENGTH) {
+                builder.setPrefix(new IpPrefix(Ipv6Util.prefixForByteBuf(nlri)));
+            } else {
+                builder.setPrefix(new IpPrefix(Ipv4Util.prefixForByteBuf(nlri)));
+            }
+            dests.add(builder.build());
+        }
+        return dests;
+    }
+
+    public static void serializeNlri(final List<L3vpnMcastDestination> destinationList, final ByteBuf output) {
+        for (final L3vpnMcastDestination dest : destinationList) {
+            PathIdUtil.writePathId(dest.getPathId(), output);
+            ByteBuf nlriOutput = Unpooled.buffer();
+            RouteDistinguisherUtil.serializeRouteDistinquisher(dest.getRouteDistinguisher(), nlriOutput);
+            final IpPrefix prefix = dest.getPrefix();
+            if (prefix.getIpv4Prefix() != null) {
+                output.writeByte(Ipv4Util.IP4_BITS_LENGTH);
+                nlriOutput.writeBytes(Ipv4Util.bytesForPrefix(prefix.getIpv4Prefix()));
+            } else {
+                output.writeByte(Ipv6Util.IPV6_BITS_LENGTH);
+                nlriOutput.writeBytes(Ipv6Util.bytesForPrefix(prefix.getIpv6Prefix()));
+            }
+            while (nlriOutput.readableBytes() % 8 != 0) {
+                nlriOutput.writeZero(1);
+            }
+            output.writeBytes(nlriOutput);
+        }
+    }
+}
diff --git a/bgp/l3vpn/src/main/yang/bgp-l3vpn-mcast.yang b/bgp/l3vpn/src/main/yang/bgp-l3vpn-mcast.yang
new file mode 100644 (file)
index 0000000..4d68bea
--- /dev/null
@@ -0,0 +1,172 @@
+module bgp-l3vpn-mcast {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:bgp:l3vpn:mcast";
+    prefix "bgp-l3vpn-mcast";
+
+    import bgp-rib { prefix bgp-rib; revision-date 2018-03-29; }
+    import bgp-multiprotocol { prefix bgp-mp; revision-date 2018-03-29; }
+    import ietf-inet-types { prefix inet; revision-date 2013-07-15; }
+    import bgp-message { prefix bgp-msg; revision-date 2018-03-29; }
+    import bmp-monitor { prefix bmp-mon; revision-date 2018-03-29; }
+    import bgp-types { prefix bgp-t; revision-date 2018-03-29; }
+
+    organization "AT&T Services, Inc.";
+
+    contact "Claudio D. Gasparini <claudio.gasparini@pantheon.tech>";
+
+    description
+        "This module contains the base data model of a BGP message.
+        It rolls up the definitions contained in RFC6513, RFC6514.
+
+        Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+
+        This program and the accompanying materials are made available
+        under the terms of the Eclipse Public License v1.0 which
+        accompanies this distribution, and is available at
+        http://www.eclipse.org/legal/epl-v10.html";
+
+    revision 2018-04-17 {
+        description "Initial version";
+    }
+
+    identity mcast-mpls-labeled-vpn-subsequent-address-family {
+        description "SAFI 129 multicast l3vpn";
+        reference "https://www.iana.org/assignments/safi-namespace/safi-namespace.xhtml";
+
+        base bgp-t:subsequent-address-family;
+    }
+
+    grouping l3vpn-mcast-route {
+        leaf prefix {
+            type inet:ip-prefix;
+        }
+        uses bgp-t:route-distinguisher-grouping;
+    }
+
+    grouping l3vpn-mcast-destination {
+        list l3vpn-mcast-destination {
+            uses l3vpn-mcast-route;
+            uses bgp-msg:path-id-grouping;
+        }
+    }
+
+    grouping l3vpn-mcast-routes {
+        list l3vpn-mcast-route {
+            key "route-key path-id";
+            uses bgp-rib:route;
+            uses l3vpn-mcast-route;
+        }
+    }
+
+    grouping l3vpn-mcast-routes-ipv4 {
+        container l3vpn-mcast-routes-ipv4 {
+            uses l3vpn-mcast-routes;
+        }
+    }
+
+    grouping l3vpn-mcast-routes-ipv6 {
+        container l3vpn-mcast-routes-ipv6 {
+            uses l3vpn-mcast-routes;
+        }
+    }
+
+    augment "/bgp-msg:update/bgp-msg:attributes/bgp-mp:mp-reach-nlri/bgp-mp:advertized-routes/bgp-mp:destination-type" {
+        case destination-l3vpn-mcast-ipv4-advertized-case {
+            when "../../afi = ipv4";
+            container destination-ipv4-l3vpn-mcast {
+                uses l3vpn-mcast-destination;
+            }
+        }
+        case destination-l3vpn-mcast-ipv6-advertized-case {
+            when "../../afi = ipv6";
+            container destination-ipv6-l3vpn-mcast {
+                uses l3vpn-mcast-destination;
+            }
+        }
+    }
+
+    augment "/bgp-msg:update/bgp-msg:attributes/bgp-mp:mp-unreach-nlri/bgp-mp:withdrawn-routes/bgp-mp:destination-type" {
+        case destination-l3vpn-mcast-ipv4-withdrawn-case {
+            when "../../afi = ipv4";
+            container destination-ipv4-l3vpn-mcast {
+                uses l3vpn-mcast-destination;
+            }
+        }
+        case destination-l3vpn-mcast-ipv6-withdrawn-case {
+            when "../../afi = ipv6";
+            container destination-ipv6-l3vpn-mcast {
+                uses l3vpn-mcast-destination;
+            }
+        }
+    }
+
+    augment "/bgp-rib:application-rib/bgp-rib:tables/bgp-rib:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bgp-rib:bgp-rib/bgp-rib:rib/bgp-rib:loc-rib/bgp-rib:tables/bgp-rib:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bgp-rib:bgp-rib/bgp-rib:rib/bgp-rib:peer/bgp-rib:adj-rib-in/bgp-rib:tables/bgp-rib:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bgp-rib:bgp-rib/bgp-rib:rib/bgp-rib:peer/bgp-rib:effective-rib-in/bgp-rib:tables/bgp-rib:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bgp-rib:bgp-rib/bgp-rib:rib/bgp-rib:peer/bgp-rib:adj-rib-out/bgp-rib:tables/bgp-rib:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bmp-mon:bmp-monitor/bmp-mon:monitor/bmp-mon:router/bmp-mon:peer/bmp-mon:pre-policy-rib/bmp-mon:tables/bmp-mon:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+
+    augment "/bmp-mon:bmp-monitor/bmp-mon:monitor/bmp-mon:router/bmp-mon:peer/bmp-mon:post-policy-rib/bmp-mon:tables/bmp-mon:routes" {
+        case l3vpn-mcast-routes-ipv4-case {
+            uses l3vpn-mcast-routes-ipv4;
+        }
+
+        case l3vpn-mcast-routes-ipv6-case {
+            uses l3vpn-mcast-routes-ipv6;
+        }
+    }
+}
\ No newline at end of file
diff --git a/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/BGPActivatorTest.java b/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/BGPActivatorTest.java
new file mode 100644 (file)
index 0000000..34da9c2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext;
+import org.opendaylight.protocol.bgp.parser.spi.pojo.SimpleBGPExtensionProviderContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
+
+public final class BGPActivatorTest {
+    @Test
+    public void testActivator() {
+        final BGPActivator act = new BGPActivator();
+        final BGPExtensionProviderContext context = new SimpleBGPExtensionProviderContext();
+        assertNull(context.getSubsequentAddressFamilyRegistry().classForFamily(BGPActivator.MCAST_L3VPN_SAFI));
+        act.start(context);
+        assertEquals(McastMplsLabeledVpnSubsequentAddressFamily.class, context.getSubsequentAddressFamilyRegistry()
+                .classForFamily(BGPActivator.MCAST_L3VPN_SAFI));
+        act.close();
+    }
+}
\ No newline at end of file
diff --git a/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/RIBActivatorTest.java b/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/RIBActivatorTest.java
new file mode 100644 (file)
index 0000000..91beccf
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBActivatorTest;
+import org.opendaylight.protocol.bgp.rib.spi.RIBExtensionProviderContext;
+import org.opendaylight.protocol.bgp.rib.spi.SimpleRIBExtensionProviderContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
+
+public class RIBActivatorTest extends AbstractRIBActivatorTest {
+    @Test
+    public void testRIBActivator() {
+        final RIBActivator ribAct = new RIBActivator();
+        final RIBExtensionProviderContext context = new SimpleRIBExtensionProviderContext();
+        assertNull(context.getRIBSupport(Ipv4AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class));
+        assertNull(context.getRIBSupport(Ipv6AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class));
+        ribAct.startRIBExtensionProvider(context, this.mappingService);
+        assertNotNull(context.getRIBSupport(Ipv4AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class));
+        assertNotNull(context.getRIBSupport(Ipv6AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class));
+        ribAct.close();
+    }
+}
\ No newline at end of file
index 9586b3d1506c71c17d1f0cfb43e57937ff6bca13..98e6dd767602c66f4c2986c1b8b704875577ebdd 100644 (file)
@@ -14,8 +14,11 @@ import org.junit.Test;
 import org.opendaylight.protocol.bgp.openconfig.spi.SimpleBGPTableTypeRegistryProvider;
 import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.AfiSafiType;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV4MULTICAST;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV4UNICAST;
+import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV6MULTICAST;
 import org.opendaylight.yang.gen.v1.http.openconfig.net.yang.bgp.types.rev151009.L3VPNIPV6UNICAST;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.McastMplsLabeledVpnSubsequentAddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv6AddressFamily;
@@ -28,6 +31,11 @@ public class TableTypeActivatorTest {
     private static final BgpTableType IPV6 = new BgpTableTypeImpl(Ipv6AddressFamily.class,
             MplsLabeledVpnSubsequentAddressFamily.class);
 
+    private static final BgpTableType MCAST_L3VPN_IPV4 = new BgpTableTypeImpl(
+            Ipv4AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class);
+    private static final BgpTableType MCAST_L3VPN_IPV6 = new BgpTableTypeImpl(
+            Ipv6AddressFamily.class, McastMplsLabeledVpnSubsequentAddressFamily.class);
+
     @Test
     public void testActivator() {
         final TableTypeActivator tableTypeActivator = new TableTypeActivator();
@@ -44,8 +52,17 @@ public class TableTypeActivatorTest {
         final Optional<BgpTableType> tableType2 = registry.getTableType(L3VPNIPV6UNICAST.class);
         Assert.assertEquals(IPV6, tableType2.get());
 
+        final Optional<Class<? extends AfiSafiType>> afiSafiType4 = registry.getAfiSafiType(MCAST_L3VPN_IPV4);
+        Assert.assertEquals(L3VPNIPV4MULTICAST.class, afiSafiType4.get());
+        final Optional<Class<? extends AfiSafiType>> afiSafiType6 = registry.getAfiSafiType(MCAST_L3VPN_IPV6);
+        Assert.assertEquals(L3VPNIPV6MULTICAST.class, afiSafiType6.get());
+
+        final Optional<BgpTableType> tableType4 = registry.getTableType(L3VPNIPV4MULTICAST.class);
+        Assert.assertEquals(MCAST_L3VPN_IPV4, tableType4.get());
+        final Optional<BgpTableType> tableType6 = registry.getTableType(L3VPNIPV6MULTICAST.class);
+        Assert.assertEquals(MCAST_L3VPN_IPV6, tableType6.get());
+
         tableTypeActivator.stopBGPTableTypeRegistryProvider();
         tableTypeActivator.close();
     }
-
 }
diff --git a/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv4RIBSupportTest.java b/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv4RIBSupportTest.java
new file mode 100644 (file)
index 0000000..142196b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBSupportTest;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv4Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv4CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv4.L3vpnMcastRoutesIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv4.L3vpnMcastRoutesIpv4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv4AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv4.advertized._case.DestinationIpv4L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv4WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisher;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+
+public class L3vpnMcastIpv4RIBSupportTest extends AbstractRIBSupportTest<L3vpnMcastRoutesIpv4Case ,L3vpnMcastRoutesIpv4,
+        L3vpnMcastRoute, L3vpnMcastRouteKey> {
+    private static final PathId PATH_ID = new PathId(0L);
+    private static final IpPrefix IPV4_PREFIX = new IpPrefix(new Ipv4Prefix("34.1.22.0/24"));
+    private static final RouteDistinguisher RD = new RouteDistinguisher(new RdIpv4("1.2.3.4:258"));
+    private static final L3vpnMcastRouteKey ROUTE_KEY
+            = new L3vpnMcastRouteKey(PATH_ID, "IAABAQIDBAECIgEWABgAAAA=");
+    private static final L3vpnMcastRoute ROUTE = new L3vpnMcastRouteBuilder()
+            .setRouteKey(ROUTE_KEY.getRouteKey())
+            .setPathId(ROUTE_KEY.getPathId())
+            .setAttributes(ATTRIBUTES)
+            .setRouteDistinguisher(RD)
+            .setPrefix(IPV4_PREFIX)
+            .build();
+    private static final L3vpnMcastRoutesIpv4 MCAST_L3VPN_ROUTES
+            = new L3vpnMcastRoutesIpv4Builder().setL3vpnMcastRoute(Collections.singletonList(ROUTE)).build();
+
+    private static final L3vpnMcastDestination MCAST_L3VPN_DESTINATION = new L3vpnMcastDestinationBuilder()
+            .setRouteDistinguisher(RD)
+            .setPrefix(IPV4_PREFIX)
+            .setPathId(PATH_ID)
+            .build();
+    private static final DestinationL3vpnMcastIpv4AdvertizedCase REACH_NLRI
+            = new DestinationL3vpnMcastIpv4AdvertizedCaseBuilder()
+            .setDestinationIpv4L3vpnMcast(new DestinationIpv4L3vpnMcastBuilder()
+                    .setL3vpnMcastDestination(Collections.singletonList(MCAST_L3VPN_DESTINATION)).build()).build();
+    private static final DestinationL3vpnMcastIpv4WithdrawnCase UNREACH_NLRI
+            = new DestinationL3vpnMcastIpv4WithdrawnCaseBuilder()
+            .setDestinationIpv4L3vpnMcast(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
+                    .l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type
+                    .destination.l3vpn.mcast.ipv4.withdrawn._case.DestinationIpv4L3vpnMcastBuilder()
+                    .setL3vpnMcastDestination(Collections.singletonList(MCAST_L3VPN_DESTINATION)).build()).build();
+
+    private L3VpnMcastIpv4RIBSupport ribSupport;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        ribSupport = L3VpnMcastIpv4RIBSupport.getInstance(this.mappingService);
+        setUpTestCustomizer(ribSupport);
+    }
+
+    @Test
+    public void testDeleteRoutes() {
+        final ContainerNode withdraw = createNlriWithDrawnRoute(UNREACH_NLRI);
+        this.ribSupport.deleteRoutes(this.tx, getTablePath(), withdraw);
+        final InstanceIdentifier<L3vpnMcastRoute> instanceIdentifier = this.deletedRoutes.get(0);
+        assertEquals(ROUTE_KEY, instanceIdentifier.firstKeyOf(L3vpnMcastRoute.class));
+    }
+
+    @Test
+    public void testPutRoutes() {
+        this.ribSupport.putRoutes(this.tx, getTablePath(), createNlriAdvertiseRoute(REACH_NLRI), createAttributes());
+        final L3vpnMcastRoute route = (L3vpnMcastRoute) this.insertedRoutes.get(0).getValue();
+        assertEquals(ROUTE, route);
+    }
+
+
+    @Test
+    public void testEmptyRoute() {
+        final Routes empty = new L3vpnMcastRoutesIpv4CaseBuilder()
+                .setL3vpnMcastRoutesIpv4(new L3vpnMcastRoutesIpv4Builder()
+                        .setL3vpnMcastRoute(Collections.emptyList()).build()).build();
+        final ChoiceNode expected = createRoutes(empty);
+        Assert.assertEquals(expected, this.ribSupport.emptyRoutes());
+    }
+
+    @Test
+    public void testBuildMpUnreachNlriUpdate() {
+        final Collection<MapEntryNode> routes = createRoutes(MCAST_L3VPN_ROUTES);
+        final Update update = this.ribSupport.buildUpdate(Collections.emptyList(), routes, ATTRIBUTES);
+        assertEquals(UNREACH_NLRI, update.getAttributes().augmentation(Attributes2.class).getMpUnreachNlri()
+                .getWithdrawnRoutes().getDestinationType());
+        assertNull(update.getAttributes().augmentation(Attributes1.class));
+    }
+
+    @Test
+    public void testBuildMpReachNlriUpdate() {
+        final Collection<MapEntryNode> routes = createRoutes(MCAST_L3VPN_ROUTES);
+        final Update update = this.ribSupport.buildUpdate(routes, Collections.emptyList(), ATTRIBUTES);
+        assertEquals(REACH_NLRI, update.getAttributes().augmentation(Attributes1.class).getMpReachNlri()
+                .getAdvertizedRoutes().getDestinationType());
+        assertNull(update.getAttributes().augmentation(Attributes2.class));
+    }
+
+    @Test
+    public void testCacheableNlriObjects() {
+        Assert.assertEquals(ImmutableSet.of(L3vpnMcastRoutesIpv4Case.class), this.ribSupport.cacheableNlriObjects());
+    }
+
+    @Test
+    public void testCacheableAttributeObjects() {
+        Assert.assertEquals(ImmutableSet.of(), this.ribSupport.cacheableAttributeObjects());
+    }
+
+    @Test
+    public void testRouteIdAddPath() {
+        Assert.assertEquals(ROUTE_KEY, this.ribSupport.createRouteListKey(ROUTE_KEY.getPathId().getValue(),
+                ROUTE_KEY.getRouteKey()));
+    }
+
+    @Test
+    public void testRoutePath() {
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates prefixNii = createRouteNIWP(MCAST_L3VPN_ROUTES);
+        final YangInstanceIdentifier expected = getRoutePath().node(prefixNii);
+        final YangInstanceIdentifier actual = this.ribSupport.routePath(getTablePath().node(Routes.QNAME), prefixNii);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testRouteAttributesIdentifier() {
+        Assert.assertEquals(new YangInstanceIdentifier.NodeIdentifier(
+                        Attributes.QNAME.withModule(BindingReflections.getQNameModule(L3vpnMcastRoutesIpv4Case.class))),
+                this.ribSupport.routeAttributesIdentifier());
+    }
+
+    @Test
+    public void testRoutesCaseClass() {
+        Assert.assertEquals(L3vpnMcastRoutesIpv4Case.class, this.ribSupport.routesCaseClass());
+    }
+
+    @Test
+    public void testRoutesContainerClass() {
+        Assert.assertEquals(L3vpnMcastRoutesIpv4.class, this.ribSupport.routesContainerClass());
+    }
+
+    @Test
+    public void testRoutesListClass() {
+        Assert.assertEquals(L3vpnMcastRoute.class, this.ribSupport.routesListClass());
+    }
+
+    @Test
+    public void testChangedRoutes() {
+        final Routes emptyCase = new L3vpnMcastRoutesIpv4CaseBuilder().build();
+        DataTreeCandidateNode tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(),
+                createRoutes(emptyCase)).getRootNode();
+        assertTrue(this.ribSupport.changedRoutes(tree).isEmpty());
+
+        final Routes emptyRoutes
+                = new L3vpnMcastRoutesIpv4CaseBuilder()
+                .setL3vpnMcastRoutesIpv4(new L3vpnMcastRoutesIpv4Builder().build()).build();
+        tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(), createRoutes(emptyRoutes)).getRootNode();
+        assertTrue(this.ribSupport.changedRoutes(tree).isEmpty());
+
+        final Routes routes = new L3vpnMcastRoutesIpv4CaseBuilder().setL3vpnMcastRoutesIpv4(MCAST_L3VPN_ROUTES).build();
+        tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(), createRoutes(routes)).getRootNode();
+        final Collection<DataTreeCandidateNode> result = this.ribSupport.changedRoutes(tree);
+        assertFalse(result.isEmpty());
+    }
+}
diff --git a/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv6RIBSupportTest.java b/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/L3vpnMcastIpv6RIBSupportTest.java
new file mode 100644 (file)
index 0000000..fe92e47
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.Collection;
+import java.util.Collections;
+import org.junit.Assert;
+import org.junit.Test;
+import org.opendaylight.protocol.bgp.rib.spi.AbstractRIBSupportTest;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv6Case;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.bgp.rib.rib.loc.rib.tables.routes.L3vpnMcastRoutesIpv6CaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRoute;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.L3vpnMcastRouteKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv6.L3vpnMcastRoutesIpv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.routes.ipv6.L3vpnMcastRoutesIpv6Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.DestinationL3vpnMcastIpv6AdvertizedCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.reach.nlri.advertized.routes.destination.type.destination.l3vpn.mcast.ipv6.advertized._case.DestinationIpv6L3vpnMcastBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type.DestinationL3vpnMcastIpv6WithdrawnCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.PathId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.rib.rev180329.rib.tables.Routes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisher;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidates;
+
+public class L3vpnMcastIpv6RIBSupportTest extends AbstractRIBSupportTest<L3vpnMcastRoutesIpv6Case, L3vpnMcastRoutesIpv6,
+        L3vpnMcastRoute, L3vpnMcastRouteKey> {
+    private static final PathId PATH_ID = new PathId(0L);
+    private static final IpPrefix IPV6_PREFIX = new IpPrefix(new Ipv6Prefix("2001:db8:1:1::/64"));
+    private static final RouteDistinguisher RD = new RouteDistinguisher(new RdIpv4("1.2.3.4:258"));
+    private static final L3vpnMcastRouteKey ROUTE_KEY
+            = new L3vpnMcastRouteKey(PATH_ID, "gAABAQIDBAECIAENuAABAAEAAAAAAAAAAEAAAAAAAAAA");
+    private static final L3vpnMcastRoute ROUTE = new L3vpnMcastRouteBuilder()
+            .setRouteKey(ROUTE_KEY.getRouteKey())
+            .setPathId(ROUTE_KEY.getPathId())
+            .setAttributes(ATTRIBUTES)
+            .setRouteDistinguisher(RD)
+            .setPrefix(IPV6_PREFIX)
+            .build();
+    private static final L3vpnMcastRoutesIpv6 MCAST_L3VPN_ROUTES
+            = new L3vpnMcastRoutesIpv6Builder().setL3vpnMcastRoute(Collections.singletonList(ROUTE)).build();
+
+    private static final L3vpnMcastDestination MCAST_L3VPN_DESTINATION = new L3vpnMcastDestinationBuilder()
+            .setRouteDistinguisher(RD)
+            .setPrefix(IPV6_PREFIX)
+            .setPathId(PATH_ID)
+            .build();
+    private static final DestinationL3vpnMcastIpv6AdvertizedCase REACH_NLRI
+            = new DestinationL3vpnMcastIpv6AdvertizedCaseBuilder()
+            .setDestinationIpv6L3vpnMcast(new DestinationIpv6L3vpnMcastBuilder()
+                    .setL3vpnMcastDestination(Collections.singletonList(MCAST_L3VPN_DESTINATION)).build()).build();
+    private static final DestinationL3vpnMcastIpv6WithdrawnCase UNREACH_NLRI
+            = new DestinationL3vpnMcastIpv6WithdrawnCaseBuilder()
+            .setDestinationIpv6L3vpnMcast(new org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp
+                    .l3vpn.mcast.rev180417.update.attributes.mp.unreach.nlri.withdrawn.routes.destination.type
+                    .destination.l3vpn.mcast.ipv6.withdrawn._case.DestinationIpv6L3vpnMcastBuilder()
+                    .setL3vpnMcastDestination(Collections.singletonList(MCAST_L3VPN_DESTINATION)).build()).build();
+
+    private L3VpnMcastIpv6RIBSupport ribSupport;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        ribSupport = L3VpnMcastIpv6RIBSupport.getInstance(this.mappingService);
+        setUpTestCustomizer(ribSupport);
+    }
+
+    @Test
+    public void testDeleteRoutes() {
+        final ContainerNode withdraw = createNlriWithDrawnRoute(UNREACH_NLRI);
+        this.ribSupport.deleteRoutes(this.tx, getTablePath(), withdraw);
+        final InstanceIdentifier<L3vpnMcastRoute> instanceIdentifier = this.deletedRoutes.get(0);
+        assertEquals(ROUTE_KEY, instanceIdentifier.firstKeyOf(L3vpnMcastRoute.class));
+    }
+
+    @Test
+    public void testPutRoutes() {
+        this.ribSupport.putRoutes(this.tx, getTablePath(), createNlriAdvertiseRoute(REACH_NLRI), createAttributes());
+        final L3vpnMcastRoute route = (L3vpnMcastRoute) this.insertedRoutes.get(0).getValue();
+        assertEquals(ROUTE, route);
+    }
+
+
+    @Test
+    public void testEmptyRoute() {
+        final Routes empty = new L3vpnMcastRoutesIpv6CaseBuilder()
+                .setL3vpnMcastRoutesIpv6(new L3vpnMcastRoutesIpv6Builder()
+                        .setL3vpnMcastRoute(Collections.emptyList()).build()).build();
+        final ChoiceNode expected = createRoutes(empty);
+        Assert.assertEquals(expected, this.ribSupport.emptyRoutes());
+    }
+
+    @Test
+    public void testBuildMpUnreachNlriUpdate() {
+        final Collection<MapEntryNode> routes = createRoutes(MCAST_L3VPN_ROUTES);
+        final Update update = this.ribSupport.buildUpdate(Collections.emptyList(), routes, ATTRIBUTES);
+        assertEquals(UNREACH_NLRI, update.getAttributes().augmentation(Attributes2.class).getMpUnreachNlri()
+                .getWithdrawnRoutes().getDestinationType());
+        assertNull(update.getAttributes().augmentation(Attributes1.class));
+    }
+
+    @Test
+    public void testBuildMpReachNlriUpdate() {
+        final Collection<MapEntryNode> routes = createRoutes(MCAST_L3VPN_ROUTES);
+        final Update update = this.ribSupport.buildUpdate(routes, Collections.emptyList(), ATTRIBUTES);
+        assertEquals(REACH_NLRI, update.getAttributes().augmentation(Attributes1.class).getMpReachNlri()
+                .getAdvertizedRoutes().getDestinationType());
+        assertNull(update.getAttributes().augmentation(Attributes2.class));
+    }
+
+    @Test
+    public void testCacheableNlriObjects() {
+        Assert.assertEquals(ImmutableSet.of(L3vpnMcastRoutesIpv6Case.class), this.ribSupport.cacheableNlriObjects());
+    }
+
+    @Test
+    public void testCacheableAttributeObjects() {
+        Assert.assertEquals(ImmutableSet.of(), this.ribSupport.cacheableAttributeObjects());
+    }
+
+    @Test
+    public void testRouteIdAddPath() {
+        Assert.assertEquals(ROUTE_KEY, this.ribSupport.createRouteListKey(ROUTE_KEY.getPathId().getValue(),
+                ROUTE_KEY.getRouteKey()));
+    }
+
+    @Test
+    public void testRoutePath() {
+        final YangInstanceIdentifier.NodeIdentifierWithPredicates prefixNii = createRouteNIWP(MCAST_L3VPN_ROUTES);
+        final YangInstanceIdentifier expected = getRoutePath().node(prefixNii);
+        final YangInstanceIdentifier actual = this.ribSupport.routePath(getTablePath().node(Routes.QNAME), prefixNii);
+        assertEquals(expected, actual);
+    }
+
+    @Test
+    public void testRouteAttributesIdentifier() {
+        Assert.assertEquals(new YangInstanceIdentifier.NodeIdentifier(
+                        Attributes.QNAME.withModule(BindingReflections.getQNameModule(L3vpnMcastRoutesIpv6Case.class))),
+                this.ribSupport.routeAttributesIdentifier());
+    }
+
+    @Test
+    public void testRoutesCaseClass() {
+        Assert.assertEquals(L3vpnMcastRoutesIpv6Case.class, this.ribSupport.routesCaseClass());
+    }
+
+    @Test
+    public void testRoutesContainerClass() {
+        Assert.assertEquals(L3vpnMcastRoutesIpv6.class, this.ribSupport.routesContainerClass());
+    }
+
+    @Test
+    public void testRoutesListClass() {
+        Assert.assertEquals(L3vpnMcastRoute.class, this.ribSupport.routesListClass());
+    }
+
+    @Test
+    public void testChangedRoutes() {
+        final Routes emptyCase = new L3vpnMcastRoutesIpv6CaseBuilder().build();
+        DataTreeCandidateNode tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(),
+                createRoutes(emptyCase)).getRootNode();
+        assertTrue(this.ribSupport.changedRoutes(tree).isEmpty());
+
+        final Routes emptyRoutes
+                = new L3vpnMcastRoutesIpv6CaseBuilder()
+                .setL3vpnMcastRoutesIpv6(new L3vpnMcastRoutesIpv6Builder().build()).build();
+        tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(), createRoutes(emptyRoutes)).getRootNode();
+        assertTrue(this.ribSupport.changedRoutes(tree).isEmpty());
+
+        final Routes routes = new L3vpnMcastRoutesIpv6CaseBuilder().setL3vpnMcastRoutesIpv6(MCAST_L3VPN_ROUTES).build();
+        tree = DataTreeCandidates.fromNormalizedNode(getRoutePath(), createRoutes(routes)).getRootNode();
+        final Collection<DataTreeCandidateNode> result = this.ribSupport.changedRoutes(tree);
+        assertFalse(result.isEmpty());
+    }
+}
diff --git a/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializerTest.java b/bgp/l3vpn/src/test/java/org/opendaylight/protocol/bgp/l3vpn/mcast/nlri/L3vpnMcastNlriSerializerTest.java
new file mode 100644 (file)
index 0000000..2e441c1
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.protocol.bgp.l3vpn.mcast.nlri;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.Collections;
+import org.junit.Test;
+import org.opendaylight.protocol.util.ByteArray;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.l3vpn.mcast.rev180417.l3vpn.mcast.destination.L3vpnMcastDestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RdIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.RouteDistinguisher;
+
+public class L3vpnMcastNlriSerializerTest {
+    private static final byte[] EXPECTED = new byte[]{
+        (byte) 0x80,
+        0, 1, 1, 2, 3, 4, 1, 2, //RD
+        32, 1, 13, (byte) 0xb8, 0, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 64, // Prefix
+        0, 0, 0, 0, 0, 0, 0 //trailing bits
+    };
+    private static final RouteDistinguisher RD = new RouteDistinguisher(new RdIpv4("1.2.3.4:258"));
+    private static final IpPrefix IPV6_PREFIX = new IpPrefix(new Ipv6Prefix("2001:db8:1:2::/64"));
+    private static final L3vpnMcastDestination MCAST_L3VPN_DESTINATION = new L3vpnMcastDestinationBuilder()
+            .setRouteDistinguisher(RD)
+            .setPrefix(IPV6_PREFIX)
+            .build();
+
+    @Test
+    public void testL3vpnMcastNlriSerializer() {
+        ByteBuf actual = Unpooled.buffer();
+        L3vpnMcastNlriSerializer.serializeNlri(Collections.singletonList(MCAST_L3VPN_DESTINATION), actual);
+        assertArrayEquals(EXPECTED, ByteArray.getAllBytes(actual));
+    }
+}
\ No newline at end of file