Bug 4996 - Wrong flows when using SFC coexistence 76/34076/2
authorSam Hague <shague@redhat.com>
Thu, 4 Feb 2016 02:26:56 +0000 (21:26 -0500)
committerSam Hague <shague@redhat.com>
Thu, 4 Feb 2016 14:13:38 +0000 (14:13 +0000)
Signed-off-by: Sam Hague <shague@redhat.com>
(cherry picked from commit 498fe4df2997839fdf12555eb5961008ce9eab1a)

Change-Id: Ibf950ae9b56317441014497ffd4260580326d802
Signed-off-by: Sam Hague <shague@redhat.com>
14 files changed:
openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/AbstractDataTreeListener.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/INetvirtSfcOF13Provider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcAclListener.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcClassifierListener.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcProvider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/RspListener.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/SfcUtils.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/standalone/openflow13/NetvirtSfcStandaloneOF13Provider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/workaround/NetvirtSfcWorkaroundOF13Provider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/workaround/services/FlowCache.java
openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/NetvirtSfcIT.java
openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/utils/AclUtils.java
openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/utils/RenderedServicePathUtils.java [new file with mode: 0644]

index 0f4c9953167ea1466fe8fb42d3d86ba9b664f374..9ed2f3db66e8eb8640916c2867ae0a1d04a4c32b 100644 (file)
@@ -30,5 +30,9 @@ module netvirt-sfc-acl {
         leaf rsp-name {
             type string;
         }
+        leaf render-rsp {
+            type boolean;
+            default "false";
+        }
     }
 }
index f5b6f2c4ce184b3e923b09fff52fe24efe495abe..11848db2314d60ad221a4c113f4319c881bd465b 100644 (file)
@@ -11,6 +11,8 @@ package org.opendaylight.ovsdb.openstack.netvirt.sfc;
 import java.util.Collection;
 
 import com.google.common.base.Preconditions;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
@@ -27,23 +29,20 @@ public abstract class AbstractDataTreeListener<T extends DataObject> implements
     private static final Logger LOG = LoggerFactory.getLogger(AbstractDataTreeListener.class);
     protected INetvirtSfcOF13Provider provider;
     protected final Class<T> clazz;
+    private final ExecutorService executorService = Executors.newFixedThreadPool(1);
 
     public AbstractDataTreeListener(INetvirtSfcOF13Provider provider, Class<T> clazz) {
         this.provider = Preconditions.checkNotNull(provider, "provider can not be null!");
         this.clazz = Preconditions.checkNotNull(clazz, "Class can not be null!");
     }
 
-    @Override
-    public void onDataTreeChanged(Collection<DataTreeModification<T>> changes) {
-        Preconditions.checkNotNull(changes, "Changes may not be null!");
-
+    private void processChanges(Collection<DataTreeModification<T>> changes) {
         LOG.info("onDataTreeChanged: Received Data Tree Changed ...", changes);
         for (DataTreeModification<T> change : changes) {
             final InstanceIdentifier<T> key = change.getRootPath().getRootIdentifier();
             final DataObjectModification<T> mod = change.getRootNode();
             LOG.info("onDataTreeChanged: Received Data Tree Changed Update of Type={} for Key={}",
                     mod.getModificationType(), key);
-            LOG.info("onDataTreeChanged: mod: {}", mod);
             switch (mod.getModificationType()) {
                 case DELETE:
                     remove(key, mod.getDataBefore());
@@ -63,4 +62,15 @@ public abstract class AbstractDataTreeListener<T extends DataObject> implements
             }
         }
     }
+
+    @Override
+    public void onDataTreeChanged(final Collection<DataTreeModification<T>> changes) {
+        Preconditions.checkNotNull(changes, "Changes may not be null!");
+        executorService.submit(new Runnable() {
+            @Override
+            public void run() {
+                processChanges(changes);
+            }
+        });
+    }
 }
index 984ac47ee0bacff54e354670bedc503cdbe959cd..448ea234e45fac92480b401c3ac26bcd3b887542 100644 (file)
@@ -46,4 +46,5 @@ public interface INetvirtSfcOF13Provider {
     public void setDependencies(ServiceReference serviceReference);
     void updateRsp(RenderedServicePath change);
     void removeRsp(RenderedServicePath change);
+    void addRsp(RenderedServicePath change);
 }
index 7d6169754e6205b9bd741c949ba46b7a65e41e9b..f8cc5703697cd59eca05ccf69c428f903302c554 100644 (file)
@@ -57,8 +57,7 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener<Acl> {
             try {
                 listenerRegistration.close();
             } catch (final Exception e) {
-                LOG.warn("Error while stopping IETF ACL ChangeListener: {}", e.getMessage());
-                LOG.debug("Error while stopping IETF ACL ChangeListener..", e);
+                LOG.warn("Error while stopping IETF ACL ChangeListener", e);
             }
             listenerRegistration = null;
         }
@@ -66,22 +65,25 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener<Acl> {
 
     @Override
     public void remove(final InstanceIdentifier<Acl> identifier,
-                       final Acl removeDataObj) {
-        Preconditions.checkNotNull(removeDataObj, "Removed object can not be null!");
-        provider.removeClassifierRules(removeDataObj);
+                       final Acl change) {
+        Preconditions.checkNotNull(change, "Removed object can not be null!");
+        provider.removeClassifierRules(change);
     }
 
     @Override
     public void update(final InstanceIdentifier<Acl> identifier,
-                       final Acl original, final Acl update) {
+                       final Acl original, final Acl change) {
+        Preconditions.checkNotNull(original, "Updated original object can not be null!");
+        Preconditions.checkNotNull(original, "Updated update object can not be null!");
+        remove(identifier, original);
+        provider.addClassifierRules(change);
     }
 
     @Override
     public void add(final InstanceIdentifier<Acl> identifier,
-                    final Acl addDataObj) {
-        Preconditions.checkNotNull(addDataObj, "Added object can not be null!");
-        LOG.debug("Adding accesslist iid = {}, dataObj = {}", identifier, addDataObj);
-        provider.addClassifierRules(addDataObj);
+                    final Acl change) {
+        Preconditions.checkNotNull(change, "Added object can not be null!");
+        provider.addClassifierRules(change);
     }
 
     public InstanceIdentifier<Acl> getIetfAclIid() {
index 1562a41f0fd461505fc4366bd949982255c93c50..066c5a7d73d677163d51c9265ef8d4ce6ca5dc25 100644 (file)
@@ -72,9 +72,9 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener<Class
 
     @Override
     public void remove(final InstanceIdentifier<Classifier> identifier,
-                       final Classifier removeDataObj) {
-        Preconditions.checkNotNull(removeDataObj, "Added object can not be null!");
-        String aclName = removeDataObj.getAcl();
+                       final Classifier change) {
+        Preconditions.checkNotNull(change, "Added object can not be null!");
+        String aclName = change.getAcl();
         // Read the ACL information from data store and make sure it exists.
         Acl acl = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getIetfAclIid(aclName));
         if (acl == null) {
@@ -87,17 +87,16 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener<Class
 
     @Override
     public void update(final InstanceIdentifier<Classifier> identifier,
-                       final Classifier original, final Classifier update) {
+                       final Classifier original, final Classifier change) {
         //TODO
 
     }
 
     @Override
     public void add(final InstanceIdentifier<Classifier> identifier,
-                    final Classifier addDataObj) {
-        Preconditions.checkNotNull(addDataObj, "Added object can not be null!");
-        String aclName = addDataObj.getAcl();
-        LOG.debug("Adding classifier iid = {}, dataObj = {}", identifier, addDataObj);
+                    final Classifier change) {
+        Preconditions.checkNotNull(change, "Added object can not be null!");
+        String aclName = change.getAcl();
         // Read the ACL information from data store and make sure it exists.
         Acl acl = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, getIetfAclIid(aclName));
         if (acl == null) {
index 7b7c8baf2281d054e91794bdac3dfadd553bcb92..973596671d5aa61901624504ddeec98613a51b29 100644 (file)
@@ -79,6 +79,7 @@ public class NetvirtSfcProvider implements BindingAwareProvider, AutoCloseable {
         LOG.info("NetvirtSfcProvider Closed");
         aclListener.close();
         classifierListener.close();
+        rspListener.close();
     }
 
     private void addToPipeline(INetvirtSfcOF13Provider provider) {
index 0fff3d3c01583c21171a146cab8c4b50db2093b1..56787c9a84b65c54addc901a9ab100be1649777c 100644 (file)
@@ -49,24 +49,24 @@ public class RspListener extends AbstractDataTreeListener<RenderedServicePath> {
     }
 
     @Override
-    public void remove(InstanceIdentifier<RenderedServicePath> identifier, RenderedServicePath change) {
-        Preconditions.checkNotNull(change, "Object can not be null!");
-        LOG.debug("remove RenderedServicePath iid = {}, change = {}", identifier, change);
+    public void remove(final InstanceIdentifier<RenderedServicePath> identifier, final RenderedServicePath change) {
+        Preconditions.checkNotNull(change, "Removed object can not be null!");
         provider.removeRsp(change);
     }
 
     @Override
-    public void update(InstanceIdentifier<RenderedServicePath> identifier, RenderedServicePath original,
+    public void update(final InstanceIdentifier<RenderedServicePath> identifier, final RenderedServicePath original,
                        RenderedServicePath change) {
-        Preconditions.checkNotNull(change, "Object can not be null!");
-        LOG.debug("Update RenderedServicePath iid = {}, change = {}", identifier, change);
-        //provider.addClassifierRules(update);
+        Preconditions.checkNotNull(original, "Updated original object can not be null!");
+        Preconditions.checkNotNull(original, "Updated update object can not be null!");
+        remove(identifier, original);
+        provider.addRsp(change);
     }
 
     @Override
-    public void add(InstanceIdentifier<RenderedServicePath> identifier, RenderedServicePath change) {
-        Preconditions.checkNotNull(change, "Object can not be null!");
-        LOG.debug("Add RenderedServicePath iid = {}, change = {}", identifier, change);
+    public void add(final InstanceIdentifier<RenderedServicePath> identifier, final RenderedServicePath change) {
+        Preconditions.checkNotNull(change, "Created object can not be null!");
+        provider.addRsp(change);
     }
 
     @Override
index 04b9578dcf1f97c77aeb8b3b32827fec328044e7..642a7574d33121d9eca1e63129725369a8cd5655 100644 (file)
@@ -104,24 +104,31 @@ public class SfcUtils {
         return mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, path);
     }
 
-    public Ace getAce(String name) {
+    public Ace getAce(RenderedServicePath rsp) {
+        return getAce(rsp.getName().getValue(), rsp.getParentServiceFunctionPath().getValue(),
+                rsp.getServiceChainName().getValue());
+    }
+
+    // TODO: optimize this by adding a ACL to RSP mapping in the netvirt-classifier when the ACL is processed
+    public Ace getAce(String rspName, String sfpName, String sfcName) {
         Ace aceFound = null;
         AccessLists accessLists = readAccessLists();
         if (accessLists != null) {
             List<Acl> acls = accessLists.getAcl();
             if (acls != null) {
-                for (Acl acl :acls) {
+                for (Acl acl : acls) {
                     AccessListEntries accessListEntries = acl.getAccessListEntries();
                     if (accessListEntries != null) {
                         List<Ace> aces = accessListEntries.getAce();
                         for (Ace ace : aces) {
                             RedirectToSfc sfcRedirect = ace.getActions().getAugmentation(RedirectToSfc.class);
-                            if ((sfcRedirect != null) &&
-                                    (sfcRedirect.getRspName().equals(name) ||
-                                    (sfcRedirect.getSfcName().equals(name)) ||
-                                    (sfcRedirect.getSfpName().equals(name)))) {
-                                aceFound = ace;
-                                break;
+                            if (sfcRedirect != null) {
+                                if ((sfcRedirect.getRspName() != null && sfcRedirect.getRspName().equals(rspName)) ||
+                                    (sfcRedirect.getSfcName() != null && sfcRedirect.getSfcName().equals(sfcName)) ||
+                                    (sfcRedirect.getSfpName() != null && sfcRedirect.getSfpName().equals(sfpName))) {
+                                    aceFound = ace;
+                                    break;
+                                }
                             }
                         }
                     }
index 4f79f3f86aa0b0ecccc91d65eb9f046933ad5c5f..c40abc4e02a9d68e5e70c80c7f7924921c1988ca 100644 (file)
@@ -453,10 +453,16 @@ public class NetvirtSfcStandaloneOF13Provider implements INetvirtSfcOF13Provider
 
     @Override
     public void removeRsp(RenderedServicePath change) {
+        LOG.warn("removeRsp is not implemented yet");
+    }
 
+    @Override
+    public void addRsp(RenderedServicePath change) {
+        LOG.warn("addRsp is not implemented yet");
     }
+
     @Override
     public void updateRsp(RenderedServicePath change) {
-
+        LOG.warn("updateRsp is not implemented yet");
     }
 }
index f8bae2d45ef718e6cd60423e075feb259661cc52..df315e1d54209aa78897c8dfca13b235fe80c1ff 100644 (file)
@@ -96,34 +96,33 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
 
     @Override
     public void addClassifierRules(Acl acl) {
-        String aclName = acl.getAclName();
-        Classifiers classifiers = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, sfcUtils.getClassifierIid());
-        if (classifiers == null) {
-            LOG.debug("addClassifierRules: No Classifiers found");
-            return;
-        }
-
-        LOG.debug("addClassifierRules: Classifiers: {}", classifiers);
-        for (Classifier classifier : classifiers.getClassifier()) {
-            if (classifier.getAcl().equals(aclName)) {
-                for (Ace ace : acl.getAccessListEntries().getAce()) {
-                    processAclEntry(ace);
-                }
-            }
+        for (Ace ace : acl.getAccessListEntries().getAce()) {
+            processAclEntry(ace);
         }
     }
 
     @Override
     public void removeClassifierRules(Acl acl) {
-
+        for (Ace ace : acl.getAccessListEntries().getAce()) {
+            RenderedServicePath rsp = getRenderedServicePath(ace);
+            if (rsp == null) {
+                LOG.warn("Failed to get renderedServicePatch for entry: {}", ace);
+                return;
+            }
+            sfcClassifierService.clearFlows(dataBroker, rsp.getName().getValue());
+        }
     }
 
     @Override
     public void removeRsp(RenderedServicePath change) {
-        LOG.info("removeRsp not implemented yet");
         sfcClassifierService.clearFlows(dataBroker, change.getName().getValue());
     }
 
+    @Override
+    public void addRsp(RenderedServicePath change) {
+        handleRenderedServicePath(change);
+    }
+
     @Override
     public void updateRsp(RenderedServicePath change) {
         LOG.info("updateRsp not implemented yet");
@@ -346,12 +345,13 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
 
     private Ace getAceFromRenderedServicePath(RenderedServicePath rsp) {
         Preconditions.checkNotNull(rsp, "RSP cannot be null");
-        Ace ace = null;
-        String rspName = rsp.getName().getValue();
-        String rspNameSuffix = "_rsp";
-        String sfcName = rspName.substring(0, rspName.length() - rspNameSuffix.length());
-        LOG.info("getAceFromRenderedServicePath: rsp: {}, sfcName: {}", rsp, sfcName);
-        ace = sfcUtils.getAce(sfcName);
+        Ace ace;
+        //String rspName = rsp.getName().getValue();
+        //String rspNameSuffix = "_rsp";
+        //String sfcName = rspName.substring(0, rspName.length() - rspNameSuffix.length());
+        //String sfcName = rsp.getServiceChainName().getValue()
+        //LOG.info("getAceFromRenderedServicePath: rsp: {}, sfcName: {}", rsp, sfcName);
+        ace = sfcUtils.getAce(rsp);
 
         return ace;
     }
@@ -359,16 +359,17 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
     private RenderedServicePath getRenderedServicePath (Ace entry) {
         RenderedServicePath rsp = null;
         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
-        LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
+        LOG.debug("getRenderedServicePath: Processing ACL entry = {} sfcRedirect = {}",
+                entry.getRuleName(), sfcRedirect);
         if (sfcRedirect == null) {
-            LOG.warn("processAClEntry: sfcRedirect is null");
+            LOG.warn("getRenderedServicePath: sfcRedirect is null");
             return null;
         }
 
         if (sfcRedirect.getRspName() != null) {
             rsp = getRenderedServicePathFromRsp(sfcRedirect.getRspName());
         } else if (sfcRedirect.getSfpName() != null) {
-            LOG.warn("getRenderedServicePath: sfp not handled yet");
+            LOG.warn("getRenderedServicePath: by sfp not handled yet");
         } else {
             rsp = getRenderedServicePathFromSfc(entry);
         }
@@ -382,9 +383,10 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
 
     private RenderedServicePath getRenderedServicePathFromSfc (Ace entry) {
         RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
-        LOG.debug("Processing ACL entry = {} sfcRedirect = {}", entry.getRuleName(), sfcRedirect);
+        LOG.debug("getRenderedServicePathFromSfc: Processing ACL entry = {} sfcRedirect = {}",
+                entry.getRuleName(), sfcRedirect);
         if (sfcRedirect == null) {
-            LOG.warn("processAClEntry: sfcRedirect is null");
+            LOG.warn("getRenderedServicePathFromSfc: sfcRedirect is null");
             return null;
         }
 
@@ -395,13 +397,18 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
             return null;
         }
 
-        LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcName, sfp);
+        LOG.debug("getRenderedServicePathFromSfc: Processing Redirect to SFC = {}, SFP = {}", sfcName, sfp);
         // If RSP doesn't exist, create an RSP.
         String sfpName = sfp.getName().getValue();
         RenderedServicePath rsp = sfcUtils.getRspforSfp(sfpName);
         String rspName = sfp.getName().getValue() + "_rsp";
         if (rsp == null) {
-            LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}", sfpName, rspName);
+            if (!sfcRedirect.isRenderRsp()) {
+                LOG.info("getRenderedServicePathFromSfc: will not create RSP");
+                return null;
+            }
+            LOG.info("getRenderedServicePathFromSfc: No configured RSP corresponding to SFP = {}, "
+                    + "Creating new RSP = {}", sfpName, rspName);
             CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
                     .setParentServiceFunctionPath(sfpName)
                     .setName(rspName)
@@ -409,13 +416,14 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
                     .build();
             rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
             if (rsp == null) {
-                LOG.warn("failed to add RSP");
+                LOG.warn("getRenderedServicePathFromSfc: failed to add RSP");
                 return null;
             }
 
             // If SFP is symmetric, create RSP in the reverse direction.
             if (sfp.isSymmetric()) {
-                LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
+                LOG.warn("getRenderedServicePathFromSfc: symmetric RSP is not supported yet");
+                /*LOG.info("SFP = {} is symmetric, installing RSP in the reverse direction!!", sfpName);
                 String rspNameRev = rspName + "-Reverse";
                 RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
                         sfcUtils.getRspId(rspNameRev));
@@ -425,7 +433,7 @@ public class NetvirtSfcWorkaroundOF13Provider implements INetvirtSfcOF13Provider
                         LOG.warn("failed to add reverse RSP");
                         return null;
                     }
-                }
+                }*/
             }
         }
         return rsp;
index e569eb1185d0d354d8a9390fc219bd33d216aa00..d00b6361186babb0da43851705fc64821bb02700 100644 (file)
@@ -41,13 +41,13 @@ public class FlowCache {
             flowMap.remove(flowId);
             if (flowMap.isEmpty()) {
                 flowCache.remove(rspName);
-                LOG.info("removeFlow: removed flowMap {}({}) from cache size {} - {}", rspName, flowId,
-                        flowCache.size(), flowCache);
+                LOG.info("removeFlow: removed flowMap {}({}) from cache size {}", rspName, flowId,
+                        flowCache.size());
             } else {
                 flowCache.put(rspName, flowMap);
             }
         }
-        LOG.info("removeFlow: removed {}({}) from cache size {} - {}", rspName, flowId, flowCache.size(), flowCache);
+        LOG.info("removeFlow: removed {}({}) from cache size {}", rspName, flowId, flowCache.size());
     }
 
     public Map<Integer, InstanceIdentifier<Flow>> getFlows(String rspName) {
index 54ea0ac772265ebbaa13e6631ec4e61733a9da7b..c98d8e089d131287f0dd578c1a75451fb8e0db8e 100644 (file)
@@ -28,12 +28,14 @@ import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRunti
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Ignore;
@@ -52,6 +54,7 @@ import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrc
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.NshUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.SfcUtils;
+import org.opendaylight.ovsdb.openstack.netvirt.sfc.it.utils.RenderedServicePathUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.standalone.openflow13.SfcClassifier;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.it.utils.AclUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.it.utils.ClassifierUtils;
@@ -68,8 +71,10 @@ import org.opendaylight.ovsdb.utils.mdsal.openflow.FlowUtils;
 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
+import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.RspName;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.common.rev151017.SftType;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.RenderedServicePaths;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePath;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.RenderedServicePathKey;
@@ -125,6 +130,7 @@ import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.node.TerminationPoint;
 import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.ops4j.pax.exam.Configuration;
@@ -153,6 +159,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
     private static ServiceFunctionForwarderUtils serviceFunctionForwarderUtils = new ServiceFunctionForwarderUtils();
     private static ServiceFunctionChainUtils serviceFunctionChainUtils = new ServiceFunctionChainUtils();
     private static ServiceFunctionPathUtils serviceFunctionPathUtils = new ServiceFunctionPathUtils();
+    private static RenderedServicePathUtils renderedServicePathUtils = new RenderedServicePathUtils();
     private static SfcConfigUtils sfcConfigUtils = new SfcConfigUtils();
     private static NetvirtConfigUtils netvirtConfigUtils = new NetvirtConfigUtils();
     private static MdsalUtils mdsalUtils;
@@ -355,6 +362,11 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         setup.set(true);
     }
 
+    @After
+    public void teardown() {
+        closeWaitFors();
+    }
+
     private ProviderContext getProviderContext() {
         ProviderContext providerContext = null;
         for (int i=0; i < 60; i++) {
@@ -410,11 +422,15 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
     }
 
     private AccessListsBuilder accessListsBuilder() {
+        return accessListsBuilder(false);
+    }
+
+    private AccessListsBuilder accessListsBuilder(boolean renderRsp) {
         String ruleName = RULENAME;
         String sfcName = SFCNAME;
         MatchesBuilder matchesBuilder = aclUtils.matchesBuilder(new MatchesBuilder(), 80);
         LOG.info("Matches: {}", matchesBuilder.build());
-        ActionsBuilder actionsBuilder = aclUtils.actionsBuilder(new ActionsBuilder(), sfcName);
+        ActionsBuilder actionsBuilder = aclUtils.actionsBuilder(new ActionsBuilder(), sfcName, renderRsp);
         AceBuilder accessListEntryBuilder =
                 aclUtils.aceBuilder(new AceBuilder(), ruleName, matchesBuilder, actionsBuilder);
         AccessListEntriesBuilder accessListEntriesBuilder =
@@ -626,11 +642,11 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
             bridgeOperationalListener.registerDataChangeListener();
             assertNotNull("connection failed", southboundUtils.addOvsdbNode(connectionInfo, NO_MDSAL_TIMEOUT));
 
-            ovsdbOperationalListener.waitForCreation(MDSAL_TIMEOUT);
+            ovsdbOperationalListener.waitForCreation();
             ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
             assertNotNull("node is not connected", ovsdbNode);
 
-            bridgeOperationalListener.waitForCreation(MDSAL_TIMEOUT);
+            bridgeOperationalListener.waitForCreation();
             assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
                     + " is not connected", isControllerConnected(connectionInfo));
 
@@ -644,11 +660,11 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
 
         void disconnect() throws InterruptedException {
             assertTrue(southboundUtils.deleteBridge(connectionInfo, bridgeName, NO_MDSAL_TIMEOUT));
-            bridgeOperationalListener.waitForDeletion(MDSAL_TIMEOUT);
+            bridgeOperationalListener.waitForDeletion();
             Node bridgeNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, bridgeIid);
             assertNull("Bridge should not be found", bridgeNode);
             assertTrue(southboundUtils.disconnectOvsdbNode(connectionInfo, NO_MDSAL_TIMEOUT));
-            ovsdbOperationalListener.waitForDeletion(MDSAL_TIMEOUT);
+            ovsdbOperationalListener.waitForDeletion();
             Node ovsdbNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, ovsdbIid);
             assertNull("Ovsdb node should not be found", ovsdbNode);
         }
@@ -657,7 +673,6 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
     /**
      * Test that the NetvirtSfc SfcClassifierService is added to the Netvirt pipeline. The test
      * sets the table offset and verifies the correct flow is programmed with the offset.
-     * @throws InterruptedException
      */
     @Test
     public void testNetvirtSfcPipeline() throws InterruptedException {
@@ -680,12 +695,16 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
      * @throws InterruptedException
      */
     @Test
-    public void testNetvirtSfcAll() throws InterruptedException {
+    public void testNetvirtSfcAll() throws Exception {
         if (userSpaceEnabled.equals("yes")) {
             LOG.info("testNetvirtSfcAll: skipping test because userSpaceEnabled {}", userSpaceEnabled);
             return;
         }
 
+        String sfpName = SFCPATH;
+        String sfcName = SFCNAME;
+        short startingIndex = 255;
+
         short netvirtTableOffset = 1;
         testModelPut(netvirtProvidersConfigBuilder(netvirtTableOffset), NetvirtProvidersConfig.class);
         short sfcTableoffset = 150;
@@ -724,33 +743,57 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         testModelPut(serviceFunctionChainsBuilder(), ServiceFunctionChains.class);
         testModelPut(serviceFunctionPathsBuilder(), ServiceFunctionPaths.class);
 
-        testModelPut(accessListsBuilder(), AccessLists.class);
+        testModelPut(accessListsBuilder(false), AccessLists.class);
         testModelPut(classifiersBuilder(), Classifiers.class);
+        ServiceFunctionPathBuilder serviceFunctionPathBuilder =
+                serviceFunctionPathUtils.serviceFunctionPathBuilder(
+                        new ServiceFunctionPathBuilder(), sfpName, sfcName, startingIndex, false);
+        SfcProviderRenderedPathAPI.createRenderedServicePathAndState(serviceFunctionPathBuilder.build(),
+                renderedServicePathUtils.createRenderedPathInputBuilder(new CreateRenderedPathInputBuilder(),
+                        SFCPATH, RSPNAME).build());
 
-        portOperationalListener.waitForCreation(MDSAL_TIMEOUT);
+        portOperationalListener.waitForCreation();
         long vxGpeOfPort = southbound.getOFPort(nodeInfo.bridgeNode, SFFDPL1NAME);
         assertNotEquals("vxGpePort was not found", 0, vxGpeOfPort);
 
-        rspOperationalListener.waitForCreation(MDSAL_TIMEOUT);
+        rspOperationalListener.waitForCreation();
         RenderedServicePath rsp = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, rspIid);
         assertNotNull("RSP was not found", rsp);
 
         flowId = FlowNames.getSfcIngressClass(RULENAME, rsp.getPathId(), rsp.getStartingIndex());
         verifyFlow(nodeInfo.datapathId, flowId, Service.SFC_CLASSIFIER);
-        flowId = FlowNames.getArpResponder(SF1IP);
-        verifyFlow(nodeInfo.datapathId, flowId, Service.ARP_RESPONDER);
         RenderedServicePathHop lastHop = sfcUtils.getLastHop(rsp);
         short lastServiceindex = (short)((lastHop.getServiceIndex()).intValue() - 1);
         flowId = FlowNames.getSfcEgressClass(vxGpeOfPort, rsp.getPathId(), lastServiceindex);
         verifyFlow(nodeInfo.datapathId, flowId, Service.SFC_CLASSIFIER);
         flowId = FlowNames.getSfcEgressClassBypass(rsp.getPathId(), lastServiceindex, 1);
         verifyFlow(nodeInfo.datapathId, flowId, Service.CLASSIFIER);
+        flowId = FlowNames.getArpResponder(SF1IP);
+        verifyFlow(nodeInfo.datapathId, flowId, Service.ARP_RESPONDER);
+
+        InstanceIdentifier<Flow> flowIid = createFlowIid(nodeInfo.datapathId, flowId,
+                pipelineOrchestrator.getTable(Service.CLASSIFIER));
+
+        final NotifyingDataChangeListener flowConfigurationListener =
+                new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION, flowIid);
+        flowConfigurationListener.registerDataChangeListener();
+        final NotifyingDataChangeListener flowOperationalListener =
+                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, flowIid);
+        flowOperationalListener.registerDataChangeListener();
 
         deleteRsp(RSPNAME);
-        rspOperationalListener.waitForDeletion(MDSAL_TIMEOUT);
+        rspOperationalListener.waitForDeletion();
         rsp = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, rspIid);
         assertNull("RSP should not be found", rsp);
 
+        flowConfigurationListener.waitForDeletion();
+        Flow flow = mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, flowIid);
+        assertNull("Flow should not be found in CONFIGURATION " + flowIid, flow);
+
+        flowOperationalListener.waitForDeletion();
+        flow = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, flowIid);
+        assertNull("Flow should not be found in OPERATIONAL " + flowIid, flow);
+
         nodeInfo.disconnect();
     }
 
@@ -841,6 +884,14 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         assertTrue(southboundUtils.disconnectOvsdbNode(connectionInfo));
     }
 
+    private InstanceIdentifier<Flow> createFlowIid(long datapathId, String flowId, short table) {
+        org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder nodeBuilder =
+                FlowUtils.createNodeBuilder(datapathId);
+        FlowBuilder flowBuilder =
+                FlowUtils.initFlowBuilder(new FlowBuilder(), flowId, table);
+        return FlowUtils.createFlowPath(flowBuilder, nodeBuilder);
+    }
+
     private Flow getFlow (
             FlowBuilder flowBuilder,
             org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder nodeBuilder,
@@ -919,25 +970,50 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         return connected;
     }
 
-    public class NotifyingDataChangeListener implements DataChangeListener {
-        private final LogicalDatastoreType type;
+    private List<NotifyingDataChangeListener> waitList = new ArrayList<>();
+
+    private void closeWaitFors() {
+        for (Iterator<NotifyingDataChangeListener> iterator = waitList.iterator(); iterator.hasNext();) {
+            NotifyingDataChangeListener listener = iterator.next();
+            iterator.remove();
+            try {
+                listener.close();
+            } catch (Exception ex) {
+                LOG.warn("Failed to close registration {}, iid {}", listener, ex);
+            }
+        }
+        LOG.info("waitList size {}", waitList.size());
+    }
+
+    public class NotifyingDataChangeListener implements AutoCloseable, DataChangeListener {
+        private LogicalDatastoreType type;
         private final Set<InstanceIdentifier<?>> createdIids = new HashSet<>();
         private final Set<InstanceIdentifier<?>> removedIids = new HashSet<>();
         private final Set<InstanceIdentifier<?>> updatedIids = new HashSet<>();
-        private final InstanceIdentifier<?> iid;
+        private InstanceIdentifier<?> iid;
         private final int RETRY_WAIT = 100;
+        private final int MDSAL_TIMEOUT = 1000;
+        private ListenerRegistration<?> listenerRegistration;
 
         private NotifyingDataChangeListener(LogicalDatastoreType type, InstanceIdentifier<?> iid) {
             this.type = type;
             this.iid = iid;
+            waitList.add(this);
+        }
+
+        private void modify(LogicalDatastoreType type, InstanceIdentifier<?> iid) throws Exception {
+            this.close();
+            this.clear();
+            this.type = type;
+            this.iid = iid;
         }
 
         @Override
         public void onDataChanged(
                 AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> asyncDataChangeEvent) {
             LOG.info("{} DataChanged: created {}", type, asyncDataChangeEvent.getCreatedData().keySet());
-            LOG.info("{} DataChanged: removed {}", type, asyncDataChangeEvent.getRemovedPaths());
             LOG.info("{} DataChanged: updated {}", type, asyncDataChangeEvent.getUpdatedData().keySet());
+            LOG.info("{} DataChanged: removed {}", type, asyncDataChangeEvent.getRemovedPaths());
             createdIids.addAll(asyncDataChangeEvent.getCreatedData().keySet());
             removedIids.addAll(asyncDataChangeEvent.getRemovedPaths());
             updatedIids.addAll(asyncDataChangeEvent.getUpdatedData().keySet());
@@ -950,14 +1026,14 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
             return createdIids.remove(iid);
         }
 
-        public boolean isRemoved(InstanceIdentifier<?> iid) {
-            return removedIids.remove(iid);
-        }
-
         public boolean isUpdated(InstanceIdentifier<?> iid) {
             return updatedIids.remove(iid);
         }
 
+        public boolean isRemoved(InstanceIdentifier<?> iid) {
+            return removedIids.remove(iid);
+        }
+
         public void clear() {
             createdIids.clear();
             removedIids.clear();
@@ -965,7 +1041,12 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         }
 
         public void registerDataChangeListener() {
-            dataBroker.registerDataChangeListener(type, iid, this, AsyncDataBroker.DataChangeScope.SUBTREE);
+            listenerRegistration = dataBroker.registerDataChangeListener(type, iid, this,
+                    AsyncDataBroker.DataChangeScope.SUBTREE);
+        }
+
+        public void waitForCreation() throws InterruptedException {
+            waitForCreation(MDSAL_TIMEOUT);
         }
 
         public void waitForCreation(long timeout) throws InterruptedException {
@@ -979,6 +1060,25 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
             }
         }
 
+        public void waitForUpdate() throws InterruptedException {
+            waitForUpdate(MDSAL_TIMEOUT);
+        }
+
+        public void waitForUpdate(long timeout) throws InterruptedException {
+            synchronized (this) {
+                long _start = System.currentTimeMillis();
+                LOG.info("Waiting for {} DataChanged update on {}", type, iid);
+                while (!isUpdated(iid) && (System.currentTimeMillis() - _start) < timeout) {
+                    wait(RETRY_WAIT);
+                }
+                LOG.info("Woke up, waited {}ms for update of {}", (System.currentTimeMillis() - _start), iid);
+            }
+        }
+
+        public void waitForDeletion() throws InterruptedException {
+            waitForDeletion(MDSAL_TIMEOUT);
+        }
+
         public void waitForDeletion(long timeout) throws InterruptedException {
             synchronized (this) {
                 long _start = System.currentTimeMillis();
@@ -990,15 +1090,17 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
             }
         }
 
-        public void waitForUpdate(long timeout) throws InterruptedException {
-            synchronized (this) {
-                long _start = System.currentTimeMillis();
-                LOG.info("Waiting for {} DataChanged update on {}", type, iid);
-                while (!isUpdated(iid) && (System.currentTimeMillis() - _start) < timeout) {
-                    wait(RETRY_WAIT);
+        @Override
+        public void close() throws Exception {
+            if (listenerRegistration != null) {
+                try {
+                    listenerRegistration.close();
+                } catch (final Exception ex) {
+                    LOG.warn("Failed to close registration {}, iid {}", listenerRegistration, iid, ex);
                 }
-                LOG.info("Woke up, waited {}ms for update of {}", (System.currentTimeMillis() - _start), iid);
             }
+            waitList.remove(this);
+            listenerRegistration = null;
         }
     }
 }
index a376cd13f524ec2cd18ea7ad2b9ed728ddc0705a..760bf6152d9af4eb39ce52ffd9dc30e0106e496a 100644 (file)
@@ -51,8 +51,10 @@ public class AclUtils extends AbstractUtils {
         return actionsBuilder.setPacketHandling(new PermitBuilder().setPermit(permit).build());
     }
 
-    public ActionsBuilder actionsBuilder(ActionsBuilder actionsBuilder, String sfcName) {
-        RedirectToSfcBuilder redirectToSfcBuilder = new RedirectToSfcBuilder().setSfcName(sfcName);
+    public ActionsBuilder actionsBuilder(ActionsBuilder actionsBuilder, String sfcName, boolean renderRsp) {
+        RedirectToSfcBuilder redirectToSfcBuilder = new RedirectToSfcBuilder()
+                .setSfcName(sfcName)
+                .setRenderRsp(renderRsp);
 
         return actionsBuilder.addAugmentation(RedirectToSfc.class, redirectToSfcBuilder.build());
     }
diff --git a/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/utils/RenderedServicePathUtils.java b/openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/utils/RenderedServicePathUtils.java
new file mode 100644 (file)
index 0000000..ebd6884
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2016 Red Hat, Inc. 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.ovsdb.openstack.netvirt.sfc.it.utils;
+
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInputBuilder;
+
+public class RenderedServicePathUtils extends AbstractUtils {
+
+    public CreateRenderedPathInputBuilder createRenderedPathInputBuilder(
+            CreateRenderedPathInputBuilder createRenderedPathInputBuilder, String sfpName, String rspName) {
+        return createRenderedPathInputBuilder
+                .setName(rspName)
+                .setParentServiceFunctionPath(sfpName)
+                .setSymmetric(false);
+    }
+}