BUG 6387: Traffic drop in 17 in serviceChain 80/43680/3
authorSuraj Ranjan <suraj.ranjan@ericsson.com>
Thu, 11 Aug 2016 06:19:40 +0000 (11:49 +0530)
committerSuraj Ranjan <suraj.ranjan@ericsson.com>
Thu, 11 Aug 2016 08:39:37 +0000 (14:09 +0530)
 Analysis:
    In a non servicechain scenario, packets get to LFIB and are sent over
    the corresponding port.
    In ServiceChain scenario, there are new Flows in LFIB, with higher priority,
    that sends the packet to 17 which, in turn, is redirecting it to SCF Pipeline.
    However, no SC matches and packets are submitted back to 17. Since packets
    have not changed, they get in a loop between 17 and 70.

 Solution:
    Table-miss in 70-75 tables must change the SI to L3VPN, so that when
    the packet gets back to 17, they are redirected to FIB table.
    Also, entries in 17 that handle packets that match no SC must be
    programmed in all CSS where the VPN has footprint, not just in the
    CSS where the last vSF in the chain is located.

Change-Id: I1cfc2973049746019a310ef3b106a5e416f24f04
Signed-off-by: Suraj Ranjan <suraj.ranjan@ericsson.com>
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/config/default-config.xml
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/CloudServiceChainConstants.java
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/CloudServiceChainProvider.java
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/VPNServiceChainHandler.java
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/NodeListener.java [new file with mode: 0644]
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/VpnToDpnListener.java [new file with mode: 0644]
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/utils/VpnServiceChainUtils.java
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/cloud/servicechain/impl/rev151211/CloudServiceChainImplModule.java
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/yang/cloud-servicechain-impl.yang
vpnservice/cloud-servicechain/cloud-servicechain-impl/src/test/java/org/opendaylight/netvirt/cloudservicechain/VPNServiceChainHandlerTest.java
vpnservice/neutronvpn/neutronvpn-impl/src/main/java/org/opendaylight/netvirt/neutronvpn/NeutronvpnManager.java

index 0c5c4c2828c41e5c042c9ca68c54172619455981..7f33af586bf957401c649d5fd92e6581e899656c 100644 (file)
@@ -31,6 +31,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <type xmlns:mdsalutil="urn:opendaylight:genius:mdsalutil">mdsalutil:odl-mdsalutil</type>
             <name>mdsalutil-service</name>
           </mdsalutil>
+          <notification-service>
+            <type xmlns:bindingimpl="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl">bindingimpl:binding-new-notification-service</type>
+            <name>binding-notification-adapter</name>
+          </notification-service>
         </module>
       </modules>
       <services xmlns="urn:opendaylight:params:xml:ns:yang:controller:config">
index 83969f27ae7c0d6ab7206c41575425344999752b..6889a080b625a94376dbf72792b11ecef82a8398 100644 (file)
@@ -33,5 +33,6 @@ public class CloudServiceChainConstants {
     public static final long SCF_L3VPN_ID_POOL_START = 20000;
     public static final long SCF_L3VPN_ID_POOL_END = 65535;
     public static final long ETHERTYPE_IPV4 = 0x0800L;
+    public static final long INVALID_VPN_TAG = -1;
 
 }
index c0903057334ceca092d78679fd59345b70049ab2..0ddcda56275d169ea8adad4b9c635c5ee019c3f5 100644 (file)
@@ -9,12 +9,15 @@ package org.opendaylight.netvirt.cloudservicechain;
 
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.NotificationService;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
 import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.netvirt.cloudservicechain.api.ICloudServiceChain;
 import org.opendaylight.netvirt.cloudservicechain.listeners.ElanDpnInterfacesListener;
+import org.opendaylight.netvirt.cloudservicechain.listeners.NodeListener;
 import org.opendaylight.netvirt.cloudservicechain.listeners.VpnPseudoPortListener;
+import org.opendaylight.netvirt.cloudservicechain.listeners.VpnToDpnListener;
 import org.opendaylight.netvirt.cloudservicechain.listeners.VrfListener;
 import org.opendaylight.netvirt.cloudservicechain.utils.VpnPseudoPortCache;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.fib.rpc.rev160121.FibRpcService;
@@ -26,6 +29,7 @@ public class CloudServiceChainProvider implements BindingAwareProvider, ICloudSe
     private static final Logger LOG = LoggerFactory.getLogger(CloudServiceChainProvider.class);
     private IMdsalApiManager mdsalManager;
     private FibRpcService fibRpcService;
+    private NotificationService notificationService;
     VPNServiceChainHandler vpnServiceChainHandler;
     ElanServiceChainHandler elanServiceChainHandler;
 
@@ -33,6 +37,15 @@ public class CloudServiceChainProvider implements BindingAwareProvider, ICloudSe
     VpnPseudoPortListener vpnPseudoPortListener;
     VrfListener labelListener;
     ElanDpnInterfacesListener elanDpnInterfacesListener;
+    VpnToDpnListener vpnToDpnListener;
+    NodeListener nodeListener;
+
+    public CloudServiceChainProvider(NotificationService notificationServiceDependency,
+                                     IMdsalApiManager mdsalManager, FibRpcService fibRpcService) {
+        this.notificationService = notificationServiceDependency;
+        this.mdsalManager = mdsalManager;
+        this.fibRpcService = fibRpcService;
+    }
 
     @Override
     public void onSessionInitiated(ProviderContext session) {
@@ -46,6 +59,10 @@ public class CloudServiceChainProvider implements BindingAwareProvider, ICloudSe
             vpnPseudoPortListener = new VpnPseudoPortListener(dataBroker);
             labelListener = new VrfListener(dataBroker, mdsalManager);
             elanDpnInterfacesListener = new ElanDpnInterfacesListener(dataBroker, mdsalManager);
+            vpnToDpnListener = new VpnToDpnListener(dataBroker, mdsalManager);
+            nodeListener = new NodeListener(dataBroker, mdsalManager);
+
+            notificationService.registerNotificationListener(vpnToDpnListener);
 
             elanServiceChainHandler = new ElanServiceChainHandler(dataBroker, mdsalManager);
 
@@ -54,17 +71,6 @@ public class CloudServiceChainProvider implements BindingAwareProvider, ICloudSe
         }
     }
 
-    public void setMdsalManager(IMdsalApiManager mdsalManager) {
-        this.mdsalManager = mdsalManager;
-    }
-
-    public void setFibRpcService(FibRpcService fibManager) {
-        this.fibRpcService = fibManager;
-        if ( this.vpnServiceChainHandler != null ) {
-            this.vpnServiceChainHandler.setFibRpcService(fibManager);
-        }
-    }
-
     @Override
     public void close() throws Exception {
 
index a739f73c2fe027e815211fa75302eea36239cae5..2a45412242b1f11db3ea9386d3daf9f20fdcaee6 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.netvirt.cloudservicechain;
 
 import java.math.BigInteger;;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
@@ -187,9 +188,9 @@ public class VPNServiceChainHandler implements AutoCloseable {
      * @param scfTag Service Function tag
      * @param scsTag Service Chain tag
      * @param lportTag Lport tag
-     * @return fake VpnPseudoPort interface name
+     * @return the fake VpnPseudoPort interface name
      */
-    private String getVpnPseudoPortIfName(Long dpId, int scfTag, int scsTag, int lportTag) {
+    private String buildVpnPseudoPortIfName(Long dpId, int scfTag, int scsTag, int lportTag) {
         return new StringBuilder("VpnPseudo.").append(dpId).append(NwConstants.FLOWID_SEPARATOR)
                                               .append(lportTag).append(NwConstants.FLOWID_SEPARATOR)
                                               .append(scfTag).append(NwConstants.FLOWID_SEPARATOR)
@@ -203,7 +204,6 @@ public class VPNServiceChainHandler implements AutoCloseable {
     }
 
 
-
     /**
      * L3VPN Service chaining: It moves traffic from a ServiceChain to a L3VPN
      *
@@ -220,6 +220,7 @@ public class VPNServiceChainHandler implements AutoCloseable {
         // These Flows must be installed in the DPN where the last SF in the ServiceChain is located
         //   + ScForwardingTable (75):  (This one is created and maintained by ScHopManager)
         //       - Match:  scfTag + servChainId + lportTagOfvVSF    Instr: VpnPseudoPortTag + SI=L3VPN + GOTO LPortDisp
+        // And these 2 flows must be installed in all Dpns where the Vpn is present:
         //   + LPortDisp (17):
         //       - Match:  VpnPseudoPortTag + SI==L3VPN    Instr: setVpnTag + GOTO FIB
         //   + FIB (21): (one entry per VrfEntry, and it is maintained by FibManager)
@@ -239,21 +240,34 @@ public class VPNServiceChainHandler implements AutoCloseable {
             LOG.debug("Router distinguisher (rd): {}, lportTag: {} ", rd, vpnPseudoLportTag);
             // Find out the set of DPNs for the given VPN ID
             if (vpnInstance != null) {
-                Long vpnId = vpnInstance.getVpnId();
-                BigInteger dpId = BigInteger.valueOf(dpnId);
-                Flow flow = VpnServiceChainUtils.buildLPortDispFromScfToL3VpnFlow(vpnId, dpId, vpnPseudoLportTag,
-                                                                                  addOrRemove);
-                if ( addOrRemove == NwConstants.ADD_FLOW ) {
-                    mdsalManager.installFlow(BigInteger.valueOf(dpnId), flow);
-                } else {
-                    // Only remove if it is the LastServiceChain using it.
-                    if ( isLastServiceChain ) {
-                        mdsalManager.removeFlow(BigInteger.valueOf(dpnId), flow);
+
+                if ( addOrRemove == NwConstants.ADD_FLOW
+                        || (addOrRemove == NwConstants.DEL_FLOW && isLastServiceChain) ) {
+
+                    Long vpnId = vpnInstance.getVpnId();
+                    List<VpnToDpnList> vpnToDpnList = vpnInstance.getVpnToDpnList();
+                    if ( vpnToDpnList != null ) {
+                        List<BigInteger> dpns = new ArrayList<BigInteger>();
+                        for (VpnToDpnList dpnInVpn : vpnToDpnList ) {
+                            dpns.add(dpnInVpn.getDpnId());
+                        }
+                        if ( !dpns.contains(dpnId) ) {
+                            LOG.debug("Dpn {} is not included in the current VPN Footprint", dpnId);
+                            dpns.add(BigInteger.valueOf(dpnId));
+                        }
+                        for ( BigInteger dpn : dpns ) {
+                            VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalManager, vpnId, dpn,
+                                    vpnPseudoLportTag, addOrRemove);
+                        }
+                    } else {
+                        LOG.debug("Could not find VpnToDpn list for VPN {} with rd {}", vpnName, rd);
                     }
                 }
 
-                // Update the Vpn footprint by adding or removing the fake interfaces
-                String intfName = getVpnPseudoPortIfName(dpnId, scfTag, servChainTag, vpnPseudoLportTag);
+                // We need to keep a fake VpnInterface in the DPN where the last vSF (before the VpnPseudoPort) is
+                // located, because in case the last real VpnInterface is removed from that DPN, we still need
+                // the Fib table programmed there
+                String intfName = buildVpnPseudoPortIfName(dpnId, scfTag, servChainTag, vpnPseudoLportTag);
                 if (addOrRemove == NwConstants.ADD_FLOW ) {
                     // Including this DPN in the VPN footprint even if there is no VpnInterface here.
                     // This is needed so that FibManager maintains the FIB table in this DPN
diff --git a/vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/NodeListener.java b/vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/NodeListener.java
new file mode 100644 (file)
index 0000000..43699aa
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  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.netvirt.cloudservicechain.listeners;
+
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
+import org.opendaylight.genius.mdsalutil.AbstractDataChangeListener;
+import org.opendaylight.genius.mdsalutil.MDSALUtil;
+import org.opendaylight.genius.mdsalutil.MatchInfo;
+import org.opendaylight.genius.mdsalutil.MatchFieldType;
+import org.opendaylight.genius.mdsalutil.MetaDataUtil;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// Rationale: for vpn-servicechain and elan-servicechain to coexist in the same deployment, it is necessary a flow
+// in LPortDispatcher that sets SI to ELAN in case that VPN does not apply
+/**
+ * Listens for Node Up events in order to install the L2 to L3 default
+ * fallback flow. This flow, with minimum priority, consists on matching on
+ * SI=2 and sets SI=3.
+ *
+ */
+public class NodeListener extends AbstractDataChangeListener<Node> implements AutoCloseable {
+
+    private ListenerRegistration<DataChangeListener> listenerRegistration;
+    private final DataBroker broker;
+    private final IMdsalApiManager mdsalMgr;
+
+    private static final String L3_TO_L2_DEFAULT_FLOW_REF = "L3VPN_to_Elan_Fallback_Default_Rule";
+
+    private static final Logger logger = LoggerFactory.getLogger(NodeListener.class);
+
+    public NodeListener(final DataBroker db, final IMdsalApiManager mdsalManager) {
+        super(Node.class);
+        this.broker = db;
+        this.mdsalMgr = mdsalManager;
+        registerListener(db);
+    }
+
+    private void registerListener(final DataBroker db) {
+        try {
+            listenerRegistration =
+                    db.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
+                            InstanceIdentifier.create(Nodes.class).child(Node.class),
+                            NodeListener.this, DataChangeScope.SUBTREE);
+        } catch (final Exception e) {
+            logger.error("vpnmanager's NodeListener registration Listener failed.", e);
+            throw new IllegalStateException("vpnmanager's NodeListener registration Listener failed.", e);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (listenerRegistration != null) {
+            try {
+                listenerRegistration.close();
+            } catch (final Exception e) {
+                logger.error("Error when cleaning up NodeListener in VpnManager.", e);
+            }
+            listenerRegistration = null;
+        }
+        logger.debug("VpnManager's NodeListener Closed");
+    }
+
+    @Override
+    protected void remove(InstanceIdentifier<Node> identifier, Node del) {
+        BigInteger dpnId = getDpnIdFromNodeId(del.getId());
+        if ( dpnId == null ) {
+            return;
+        }
+        logger.debug("Removing L3VPN to ELAN default Fallback flow in LPortDispatcher table");
+        Flow flowToRemove = new FlowBuilder().setFlowName(L3_TO_L2_DEFAULT_FLOW_REF)
+                .setId(new FlowId(L3_TO_L2_DEFAULT_FLOW_REF))
+                .setTableId(NwConstants.LPORT_DISPATCHER_TABLE).build();
+        mdsalMgr.removeFlow(dpnId, flowToRemove);
+    }
+
+    @Override
+    protected void update(InstanceIdentifier<Node> identifier, Node original, Node update) {
+    }
+
+    @Override
+    protected void add(InstanceIdentifier<Node> identifier, Node add) {
+        BigInteger dpnId = getDpnIdFromNodeId(add.getId());
+        if ( dpnId == null ) {
+            return;
+        }
+
+        logger.debug("Installing L3VPN to ELAN default Fallback flow in LPortDispatcher table");
+        BigInteger[] metadataToMatch = new BigInteger[] {
+                MetaDataUtil.getServiceIndexMetaData(NwConstants.L3VPN_SERVICE_INDEX),
+                MetaDataUtil.METADATA_MASK_SERVICE_INDEX
+        };
+        List<MatchInfo> matches = Arrays.asList(new MatchInfo(MatchFieldType.metadata, metadataToMatch));
+
+        BigInteger metadataToWrite = MetaDataUtil.getServiceIndexMetaData(NwConstants.ELAN_SERVICE_INDEX);
+        int instructionKey = 0;
+        List<Instruction> instructions =
+                Arrays.asList(MDSALUtil.buildAndGetWriteMetadaInstruction(metadataToWrite,
+                        MetaDataUtil.METADATA_MASK_SERVICE_INDEX,
+                        ++instructionKey),
+                        MDSALUtil.buildAndGetGotoTableInstruction(NwConstants.L3_INTERFACE_TABLE, ++instructionKey));
+
+        Flow flow = MDSALUtil.buildFlowNew(NwConstants.LPORT_DISPATCHER_TABLE, L3_TO_L2_DEFAULT_FLOW_REF,
+                NwConstants.TABLE_MISS_PRIORITY, L3_TO_L2_DEFAULT_FLOW_REF,
+                0, 0, CloudServiceChainConstants.COOKIE_L3_BASE,
+                matches, instructions);
+        mdsalMgr.installFlow(dpnId, flow);
+    }
+
+
+    private BigInteger getDpnIdFromNodeId(NodeId nodeId) {
+        String[] node =  nodeId.getValue().split(":");
+        if(node.length < 2) {
+            logger.warn("Unexpected nodeId {}", nodeId.getValue());
+            return null;
+        }
+        return new BigInteger(node[1]);
+    }
+}
diff --git a/vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/VpnToDpnListener.java b/vpnservice/cloud-servicechain/cloud-servicechain-impl/src/main/java/org/opendaylight/netvirt/cloudservicechain/listeners/VpnToDpnListener.java
new file mode 100644 (file)
index 0000000..2b7e744
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  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.netvirt.cloudservicechain.listeners;
+
+import java.math.BigInteger;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.genius.mdsalutil.NwConstants;
+import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
+import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
+import org.opendaylight.netvirt.cloudservicechain.utils.VpnServiceChainUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AddDpnEvent;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.OdlL3vpnListener;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.RemoveDpnEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Optional;
+
+public class VpnToDpnListener implements OdlL3vpnListener {
+
+    private final DataBroker broker;
+    private final IMdsalApiManager mdsalMgr;
+
+    private static final Logger logger = LoggerFactory.getLogger(VpnToDpnListener.class);
+
+    public VpnToDpnListener(final DataBroker db, final IMdsalApiManager mdsalManager) {
+        this.broker = db;
+        this.mdsalMgr = mdsalManager;
+    }
+
+    @Override
+    public void onAddDpnEvent(AddDpnEvent notification) {
+
+        programLPortDispatcherFlowForScfToVpn(notification.getAddEventData().getDpnId(),
+                notification.getAddEventData().getVpnName(),
+                notification.getAddEventData().getRd(),
+                NwConstants.ADD_FLOW);
+
+    }
+
+    @Override
+    public void onRemoveDpnEvent(RemoveDpnEvent notification) {
+
+        programLPortDispatcherFlowForScfToVpn(notification.getRemoveEventData().getDpnId(),
+                notification.getRemoveEventData().getVpnName(),
+                notification.getRemoveEventData().getRd(),
+                NwConstants.DEL_FLOW);
+
+    }
+
+    private void programLPortDispatcherFlowForScfToVpn(BigInteger dpnId, String vpnName, String rd, int addOrRemove) {
+        String addedOrRemovedTxt = addOrRemove == NwConstants.ADD_FLOW ? " added " : " removed";
+        logger.debug("DpnToVpn {}event received: dpn={}  vpn={}  rd={}", addedOrRemovedTxt, dpnId, vpnName, rd);
+        if ( dpnId == null ) {
+            logger.warn("Dpn to Vpn {} event received, but no DPN specified in event", addedOrRemovedTxt);
+            return;
+        }
+
+        if ( vpnName == null ) {
+            logger.warn("Dpn to Vpn {} event received, but no VPN specified in event", addedOrRemovedTxt);
+            return;
+        }
+
+        if ( rd == null ) {
+            logger.warn("Dpn to Vpn {} event received, but no RD specified in event", addedOrRemovedTxt);
+            return;
+        }
+
+        Optional<Long> vpnPseudoLportTag = VpnServiceChainUtils.getVpnPseudoLportTag(broker, rd);
+        if ( !vpnPseudoLportTag.isPresent() || vpnPseudoLportTag.get() == null ) {
+            logger.debug("Dpn to Vpn {} event received: Could not find VpnPseudoLportTag for VPN name={}  rd={}",
+                    addedOrRemovedTxt, vpnName, rd);
+            return;
+        }
+        long vpnId = (addOrRemove == NwConstants.ADD_FLOW ) ? VpnServiceChainUtils.getVpnId(broker, vpnName)
+                : CloudServiceChainConstants.INVALID_VPN_TAG;
+        VpnServiceChainUtils.programLPortDispatcherFlowForScfToVpn(mdsalMgr, vpnId, dpnId,
+                vpnPseudoLportTag.get().intValue(), addOrRemove);
+    }
+}
index f897bf0dca51e415c2b82c925fa0d6fac48b5281..f6dba1d553d192a6cefc89b316e0fd95946e5c04 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.genius.mdsalutil.MetaDataUtil;
 import org.opendaylight.genius.mdsalutil.NwConstants;
 import org.opendaylight.genius.mdsalutil.interfaces.IMdsalApiManager;
 import org.opendaylight.netvirt.cloudservicechain.CloudServiceChainConstants;
+import org.opendaylight.netvirt.vpnmanager.VpnConstants;
 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
@@ -138,6 +139,25 @@ public class VpnServiceChainUtils {
                                  .child(VpnInstance.class, new VpnInstanceKey(vpnName)).build();
     }
 
+    /**
+     *      * Retrieves the VpnId searching by VpnInstanceName
+     *
+     * @param broker
+     * @param vpnName
+     * @return
+     */
+    public static long getVpnId(DataBroker broker, String vpnName) {
+
+        InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
+                .l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> id =
+                getVpnInstanceToVpnIdIdentifier(vpnName);
+        Optional<org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt
+                .l3vpn.rev130911.vpn.instance.to.vpn.id.VpnInstance> vpnInstance =
+                MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, id);
+
+        return vpnInstance.isPresent() ? vpnInstance.get().getVpnId() : VpnConstants.INVALID_ID;
+    }
+
     /**
      * Retrieves the VPN's Route Distinguisher out from the VpnName
      * @param broker
@@ -172,6 +192,27 @@ public class VpnServiceChainUtils {
         return vpnVrfEntries;
     }
 
+
+    /**
+     * Installs/removes a flow in LPortDispatcher table that is in charge of
+     * handling packets that falls back from SCF Pipeline to L3Vpn.
+     *
+     * @param mdsalManager  MDSAL Util API accessor
+     * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
+     * @param dpId The DPN where the flow must be installed/removed
+     * @param vpnPseudoLportTag Dataplane identifier for the VpnPseudoPort
+     * @param addOrRemove States if the flow must be created or removed
+     */
+    public static void programLPortDispatcherFlowForScfToVpn(IMdsalApiManager mdsalManager, long vpnId, BigInteger dpId,
+                                                             Integer vpnPseudoLportTag, int addOrRemove) {
+        Flow flow = buildLPortDispFromScfToL3VpnFlow(vpnId, dpId, vpnPseudoLportTag, addOrRemove);
+        if (addOrRemove == NwConstants.ADD_FLOW) {
+            mdsalManager.installFlow(dpId, flow);
+        } else {
+            mdsalManager.removeFlow(dpId, flow);
+        }
+    }
+
     /**
      * Build the flow that must be inserted when there is a ScHop whose egressPort is a VPN Pseudo Port. In that case,
      * packets must be moved from the SCF to VPN Pipeline
@@ -179,11 +220,13 @@ public class VpnServiceChainUtils {
      * Flow matches:  VpnPseudo port lPortTag + SI=L3VPN
      * Actions: Write vrfTag in Metadata + goto FIB Table
 
-     * @param vpnId Id of the Vpn that is the target of the packet
+     * @param vpnId Dataplane identifier of the VPN, the Vrf Tag.
      * @param dpId The DPN where the flow must be installed/removed
-     * @param addOrRemove
-     * @param lportTag lportTag of the VpnPseudoLPort
-     * @return
+     * @param lportTag Dataplane identifier for the VpnPseudoPort
+     * @param addOrRemove States if it must build a Flow to be created or
+     *     removed
+     *
+     * @return the Flow object
      */
     public static Flow buildLPortDispFromScfToL3VpnFlow(Long vpnId, BigInteger dpId, Integer lportTag,
                                                         int addOrRemove) {
@@ -212,7 +255,7 @@ public class VpnServiceChainUtils {
     /**
      * @param lportTag
      * @param serviceIndex
-     * @return
+     * @return the list of Matches
      */
     public static List<MatchInfo> buildMatchOnLportTagAndSI(Integer lportTag, short serviceIndex) {
         List<MatchInfo> matches = new ArrayList<MatchInfo>();
@@ -225,9 +268,10 @@ public class VpnServiceChainUtils {
     }
 
     /**
-     * Builds a Flow entry that sets the VpnTag in metadata and sends to FIB table.
-     * @param vpnTag
-     * @return
+     * Builds a The Instructions that sets the VpnTag in metadata and sends to FIB table.
+     *
+     * @param vpnTag Dataplane identifier of the VPN.
+     * @return the list of Instructions
      */
     public static List<Instruction> buildSetVrfTagAndGotoFibInstructions(Integer vpnTag) {
         List<Instruction> result = new ArrayList<Instruction>();
@@ -357,6 +401,17 @@ public class VpnServiceChainUtils {
 
     }
 
+    public static Optional<Long> getVpnPseudoLportTag(DataBroker broker, String rd) {
+        VpnToPseudoPortTagKey key = new VpnToPseudoPortTagKey(rd);
+        InstanceIdentifier<VpnToPseudoPortTag> path = InstanceIdentifier.builder(VpnToPseudoPortTagData.class)
+                .child(VpnToPseudoPortTag.class, key)
+                .build();
+        Optional<VpnToPseudoPortTag> lPortTagOpc = MDSALUtil.read(broker, LogicalDatastoreType.CONFIGURATION, path);
+
+        return lPortTagOpc.isPresent() ? Optional.fromNullable(lPortTagOpc.get().getLportTag())
+                : Optional.<Long>absent();
+    }
+
     /**
      * Updates the mapping between VPNs and their respective VpnPseudoLport Tags
      *
index 6be15d93de7dcf4ce5d19b523fdf768c4eb5d83f..7e5d3d345fbff06ac8cb15956eb29b7ca410235b 100644 (file)
@@ -20,9 +20,9 @@ public class CloudServiceChainImplModule extends org.opendaylight.yang.gen.v1.ur
     @Override
     public java.lang.AutoCloseable createInstance() {
         FibRpcService fibRpcService = getRpcregistryDependency().getRpcService(FibRpcService.class);
-        CloudServiceChainProvider provider = new CloudServiceChainProvider();
-        provider.setMdsalManager(getMdsalutilDependency());
-        provider.setFibRpcService(fibRpcService);
+        CloudServiceChainProvider provider = new CloudServiceChainProvider(getNotificationServiceDependency(),
+                getMdsalutilDependency(),
+                fibRpcService );
         getBrokerDependency().registerProvider(provider);
         return provider;
     }
index c62141a938732677e5e4824613d21e545233b2f1..6b712841cf6ea033b1acc5a10074b3f5ce6d1811 100644 (file)
@@ -50,6 +50,14 @@ module cloud-servicechain-impl {
                     }
                 }
             }
+            container notification-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity md-sal-binding-impl:binding-new-notification-service;
+                    }
+                }
+            }
         }
     }
 }
index 1077cf90fef85fe5ef5f806e85f09ea12aba3d7a..a352052e21ce1320e0f2d7aec3d4ae7e4f09d49e 100644 (file)
@@ -302,10 +302,10 @@ public class VPNServiceChainHandlerTest {
         VrfEntry ve = stubCreateDCGWVrfEntry();
 
         ArgumentCaptor<Flow> argumentCaptor = ArgumentCaptor.forClass(Flow.class);
-        verify(mdsalMgr, times(1)).installFlow((BigInteger)anyObject(), argumentCaptor.capture());
+        verify(mdsalMgr, times(2)).installFlow((BigInteger)anyObject(), argumentCaptor.capture());
 
         List<Flow> installedFlowsCaptured = argumentCaptor.getAllValues();
-        assert (installedFlowsCaptured.size() == 1);
+        assert (installedFlowsCaptured.size() == 2);
 
         Flow expectedLportDispatcherFlowEntity = VpnServiceChainUtils.buildLPortDispFromScfToL3VpnFlow(vpnId,
                 new BigInteger(String.valueOf(dpnId)), dpnId, NwConstants.ADD_FLOW);
index 8d8df40993eecdeeb7d22344efc8f12971a691c3..bb4bfad86abc264ed91371710929192898a4f6fc 100644 (file)
@@ -1200,20 +1200,23 @@ public class NeutronvpnManager implements NeutronvpnService, AutoCloseable, Even
             sn = optSn.get();
             oldVpnId = sn.getVpnId();
             List<String> ips = sn.getRouterInterfaceFixedIps();
-            for (String ipValue : ips) {
-                IpAddress ip = new IpAddress(ipValue.toCharArray());
-                if (oldVpnId != null) {
-                    InstanceIdentifier<VpnPortipToPort> id = NeutronvpnUtils.buildVpnPortipToPortIdentifier(oldVpnId
-                            .getValue(), ipValue);
-                    Optional<VpnPortipToPort> optionalVpnPort = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType
-                            .CONFIGURATION, id);
-                    if (optionalVpnPort.isPresent()) {
-                        removeVpnPortFixedIpToPort(oldVpnId.getValue(), ipValue);
+            if ( ips != null ) {
+                for (String ipValue : ips) {
+                    IpAddress ip = new IpAddress(ipValue.toCharArray());
+                    if (oldVpnId != null) {
+                        InstanceIdentifier<VpnPortipToPort> id = NeutronvpnUtils.buildVpnPortipToPortIdentifier(oldVpnId.getValue(), ipValue);
+                        Optional<VpnPortipToPort> optionalVpnPort = NeutronvpnUtils.read(dataBroker, LogicalDatastoreType.CONFIGURATION, id);
+                        if (optionalVpnPort.isPresent()) {
+                            removeVpnPortFixedIpToPort(oldVpnId.getValue(), ipValue);
+                        }
                     }
+                    createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, sn.getRouterInterfaceName().getValue(),
+                            sn.getRouterIntfMacAddress(), true, true, false);
                 }
-                createVpnPortFixedIpToPort(vpnId.getValue(), ipValue, sn.getRouterInterfaceName().getValue(),
-                        sn.getRouterIntfMacAddress(), true, true, false);
+            } else {
+                LOG.warn("No fixed IPs configured for router interface. VpnId={}  subnetId={}", vpnId, subnet);
             }
+
         }
         sn = updateSubnetNode(subnet, null, null, null, null, vpnId);
         boolean isLockAcquired = false;