Refactor IT code for re-use... 80/36480/2
authorJosh <jhershbe@redhat.com>
Sun, 20 Mar 2016 11:06:57 +0000 (13:06 +0200)
committerJosh <jhershbe@redhat.com>
Sat, 26 Mar 2016 18:51:46 +0000 (20:51 +0200)
..and to solve race condition in NetvirtIT

1. NotifyingDataChangeListener moved into mdsal-utils
2. NodeInfo moved into new it-utils. The original
thought was to place this in southbound-utils but it
has some dependencies on net-virt code. Instead,
create it-utils which since it is designated for IT
can really depend on everything.
3. Created ItUtils class. Eventually this could and
should hold more generic IT functions, e.g.,
validateFlow, etc. There is more to do here but wanted
to get this code review rolling. This can be just phase
one.

Change-Id: I23645bfbe54762516b182446a93577ee0033e828
Signed-off-by: Josh <jhershbe@redhat.com>
14 files changed:
features/pom.xml
features/src/main/features/features.xml
openstack/net-virt-it/pom.xml
openstack/net-virt-it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/it/NetvirtIT.java
openstack/net-virt-sfc/features/test/pom.xml
openstack/net-virt-sfc/features/test/src/main/features/features.xml
openstack/net-virt-sfc/it/pom.xml
openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/it/NetvirtSfcIT.java
utils/it-utils/pom.xml [new file with mode: 0644]
utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/ItUtils.java [new file with mode: 0644]
utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/NodeInfo.java [new file with mode: 0644]
utils/mdsal-utils/pom.xml
utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/NotifyingDataChangeListener.java [new file with mode: 0644]
utils/pom.xml

index fd0ff8789975f6104367e68b938da70bbb63f181..df01a9c62fd4ec32dd908c520e9e353d75a11cb2 100644 (file)
@@ -250,6 +250,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>utils.mdsal-utils</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>utils.it-utils</artifactId>
+      <version>${project.version}</version>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>ovsdb-ui-bundle</artifactId>
index 3780e5bfd97031fed41141234283376047820633..411f0c8ec55b788459c290845c2ea82fd0ed56a1 100644 (file)
@@ -40,6 +40,7 @@
     <bundle>mvn:org.opendaylight.ovsdb/utils.neutron-utils/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/utils.mdsal-utils/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/utils.southbound-utils/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.ovsdb/utils.it-utils/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/openstack.net-virt/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/openstack.net-virt-providers/{{VERSION}}</bundle>
     <bundle>mvn:commons-net/commons-net/{{VERSION}}</bundle>
index ee99fdf04ec6f83650c49f8f0f191b82c4d4e103..5c8710ef31b59f2f6391e552ad5c186bbeb3bc38 100644 (file)
@@ -99,6 +99,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>sonar-jacoco-listeners</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>utils.it-utils</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
index d8ef43b9309730951bffccb4f1a5e3243940e037..f331c2ba38116d0ecdc1270d0be78a25f28f2d21 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015 Red Hat, Inc. and others.  All rights reserved.
+ * Copyright (c) 2015 - 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,
@@ -56,6 +56,8 @@ import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestrator;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
+import org.opendaylight.ovsdb.utils.it.ItUtils;
+import org.opendaylight.ovsdb.utils.it.NodeInfo;
 import org.opendaylight.ovsdb.utils.mdsal.openflow.FlowUtils;
 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
@@ -93,6 +95,7 @@ import org.slf4j.LoggerFactory;
 public class NetvirtIT extends AbstractMdsalTestBase {
     private static final Logger LOG = LoggerFactory.getLogger(NetvirtIT.class);
     private static DataBroker dataBroker = null;
+    private static ItUtils itUtils;
     private static String addressStr;
     private static String portStr;
     private static String connectionType;
@@ -240,6 +243,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         }
 
         dataBroker = getDatabroker(getProviderContext());
+        itUtils = new ItUtils(dataBroker);
         mdsalUtils = new MdsalUtils(dataBroker);
         assertNotNull("mdsalUtils should not be null", mdsalUtils);
         assertTrue("Did not find " + NETVIRT_TOPOLOGY_ID, getNetvirtTopology());
@@ -350,7 +354,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         LOG.info("testNetVirt: should be connected: {}", ovsdbNode.getNodeId());
 
         assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                + " is not connected", isControllerConnected(connectionInfo));
+                + " is not connected", itUtils.isControllerConnected(connectionInfo));
 
         Assert.assertTrue(southboundUtils.deleteBridge(connectionInfo, NetvirtITConstants.INTEGRATION_BRIDGE_NAME));
         Thread.sleep(1000);
@@ -372,7 +376,7 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         LOG.info("testNetVirt: should be connected: {}", ovsdbNode.getNodeId());
 
         assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                + " is not connected", isControllerConnected(connectionInfo));
+                + " is not connected", itUtils.isControllerConnected(connectionInfo));
 
         // Verify the pipeline flows were installed
         Node bridgeNode = southbound.getBridgeNode(ovsdbNode, NetvirtITConstants.INTEGRATION_BRIDGE_NAME);
@@ -403,38 +407,6 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         LOG.info("testAddDeleteOvsdbNodeWithTableOffset exit");
     }
 
-    private boolean isControllerConnected(ConnectionInfo connectionInfo) throws InterruptedException {
-        LOG.info("isControllerConnected enter");
-        Boolean connected = false;
-        ControllerEntry controllerEntry;
-        Node ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
-        assertNotNull("ovsdb node not found", ovsdbNode);
-
-        BridgeConfigurationManager bridgeConfigurationManager =
-                (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
-        assertNotNull("Could not find BridgeConfigurationManager Service", bridgeConfigurationManager);
-        String controllerTarget = bridgeConfigurationManager.getControllersFromOvsdbNode(ovsdbNode).get(0);
-        Assert.assertNotNull("Failed to get controller target", controllerTarget);
-
-        for (int i = 0; i < 10; i++) {
-            LOG.info("isControllerConnected try {}: looking for controller: {}", i, controllerTarget);
-            OvsdbBridgeAugmentation bridge =
-                    southboundUtils.getBridge(connectionInfo, NetvirtITConstants.INTEGRATION_BRIDGE_NAME);
-            Assert.assertNotNull(bridge);
-            Assert.assertNotNull(bridge.getControllerEntry());
-            controllerEntry = bridge.getControllerEntry().iterator().next();
-            Assert.assertEquals(controllerTarget, controllerEntry.getTarget().getValue());
-            if (controllerEntry.isIsConnected()) {
-                Assert.assertTrue("Controller is not connected", controllerEntry.isIsConnected());
-                connected = true;
-                break;
-            }
-            Thread.sleep(1000);
-        }
-        LOG.info("isControllerConnected exit: {} - {}", connected, controllerTarget);
-        return connected;
-    }
-
     @Ignore
     @Test
     public void testOpenVSwitchOtherConfig() throws InterruptedException {
@@ -473,21 +445,9 @@ public class NetvirtIT extends AbstractMdsalTestBase {
     public void testNetVirt() throws InterruptedException {
         LOG.info("testNetVirt: starting test");
         ConnectionInfo connectionInfo = SouthboundUtils.getConnectionInfo(addressStr, portStr);
-        Node ovsdbNode = connectOvsdbNode(connectionInfo);
-        assertNotNull("connection failed", ovsdbNode);
-        LOG.info("testNetVirt: should be connected: {}", ovsdbNode.getNodeId());
-
-        //TODO use controller value rather that ovsdb connectionInfo or change log
-        assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                + " is not connected", isControllerConnected(connectionInfo));
-
-        // Verify the pipeline flows were installed
-        Node bridgeNode = southbound.getBridgeNode(ovsdbNode, NetvirtITConstants.INTEGRATION_BRIDGE_NAME);
-        assertNotNull("bridge " + NetvirtITConstants.INTEGRATION_BRIDGE_NAME + " was not found", bridgeNode);
-        long datapathId = southbound.getDataPathId(bridgeNode);
-        String datapathIdString = southbound.getDatapathId(bridgeNode);
-        LOG.info("testNetVirt: bridgeNode: {}, datapathId: {} - {}", bridgeNode, datapathIdString, datapathId);
-        assertNotEquals("datapathId was not found", datapathId, 0);
+        NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
+        nodeInfo.connect();
+        LOG.info("testNetVirt: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
 
         List<Service> staticPipeline = pipelineOrchestrator.getStaticPipeline();
         List<Service> staticPipelineFound = Lists.newArrayList();
@@ -496,19 +456,18 @@ public class NetvirtIT extends AbstractMdsalTestBase {
                 staticPipelineFound.add(service);
             }
             String flowId = "DEFAULT_PIPELINE_FLOW_" + pipelineOrchestrator.getTable(service);
-            verifyFlow(datapathId, flowId, service);
+            verifyFlow(nodeInfo.datapathId, flowId, service);
         }
         assertEquals("did not find all expected flows in static pipeline",
                 staticPipeline.size(), staticPipelineFound.size());
 
-        southboundUtils.addTerminationPoint(bridgeNode, NetvirtITConstants.PORT_NAME, "internal", null, null, 0L);
+        southboundUtils.addTerminationPoint(nodeInfo.bridgeNode, NetvirtITConstants.PORT_NAME, "internal", null, null, 0L);
         Thread.sleep(1000);
         OvsdbTerminationPointAugmentation ovsdbTerminationPointAugmentation =
-                southbound.getTerminationPointOfBridge(bridgeNode, NetvirtITConstants.PORT_NAME);
+                southbound.getTerminationPointOfBridge(nodeInfo.bridgeNode, NetvirtITConstants.PORT_NAME);
         Assert.assertNotNull("Did not find " + NetvirtITConstants.PORT_NAME, ovsdbTerminationPointAugmentation);
-        Assert.assertTrue(southboundUtils.deleteBridge(connectionInfo, NetvirtITConstants.INTEGRATION_BRIDGE_NAME));
-        Thread.sleep(1000);
-        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
+
+        nodeInfo.disconnect();
     }
 
     @Test
@@ -522,12 +481,12 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         final String dhcpPortId ="521e29d6-67b8-4b3c-8633-027d21195115";
 
         ConnectionInfo connectionInfo = SouthboundUtils.getConnectionInfo(addressStr, portStr);
-        Node ovsdbNode = connectOvsdbNode(connectionInfo);
-        assertNotNull("connection failed", ovsdbNode);
-        LOG.info("testNetVirtFixedSG: should be connected: {}", ovsdbNode.getNodeId());
+        NodeInfo nodeInfo = itUtils.createNodeInfo(connectionInfo, null);
+        nodeInfo.connect();
+        LOG.info("testNetVirtFixedSG: should be connected: {}", nodeInfo.ovsdbNode.getNodeId());
 
         // Verify the minimum version required for this test
-        OvsdbNodeAugmentation ovsdbNodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
+        OvsdbNodeAugmentation ovsdbNodeAugmentation = nodeInfo.ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
         Assert.assertNotNull(ovsdbNodeAugmentation);
         assertNotNull(ovsdbNodeAugmentation.getOvsVersion());
         String ovsVersion = ovsdbNodeAugmentation.getOvsVersion();
@@ -541,16 +500,6 @@ public class NetvirtIT extends AbstractMdsalTestBase {
             return;
         }
 
-        assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                + " is not connected", isControllerConnected(connectionInfo));
-
-        Node bridgeNode = southbound.getBridgeNode(ovsdbNode, NetvirtITConstants.INTEGRATION_BRIDGE_NAME);
-        assertNotNull("bridge " + NetvirtITConstants.INTEGRATION_BRIDGE_NAME + " was not found", bridgeNode);
-        long datapathId = southbound.getDataPathId(bridgeNode);
-        String datapathIdString = southbound.getDatapathId(bridgeNode);
-        LOG.info("testNetVirtFixedSG: bridgeNode: {}, datapathId: {} - {}", bridgeNode, datapathIdString, datapathId);
-        assertNotEquals("datapathId was not found", datapathId, 0);
-
         NeutronNetwork nn = neutronUtils.createNeutronNetwork(networkId, tenantId,
                 NetworkHandler.NETWORK_TYPE_VXLAN, "100");
         NeutronSubnet ns = neutronUtils.createNeutronSubnet(subnetId, tenantId, networkId, "10.0.0.0/24");
@@ -563,23 +512,22 @@ public class NetvirtIT extends AbstractMdsalTestBase {
         Map<String, String> externalIds = Maps.newHashMap();
         externalIds.put("attached-mac", "f6:00:00:0f:00:01");
         externalIds.put("iface-id", portId);
-        southboundUtils.addTerminationPoint(bridgeNode, portName, "internal", null, externalIds, 3L);
-        southboundUtils.addTerminationPoint(bridgeNode, "vm1", "internal", null, null, 0L);
-        southboundUtils.addTerminationPoint(bridgeNode, "vm2", "internal", null, null, 0L);
+        southboundUtils.addTerminationPoint(nodeInfo.bridgeNode, portName, "internal", null, externalIds, 3L);
+        southboundUtils.addTerminationPoint(nodeInfo.bridgeNode, "vm1", "internal", null, null, 0L);
+        southboundUtils.addTerminationPoint(nodeInfo.bridgeNode, "vm2", "internal", null, null, 0L);
         Map<String, String> options = Maps.newHashMap();
         options.put("key", "flow");
         options.put("remote_ip", "192.168.120.32");
-        southboundUtils.addTerminationPoint(bridgeNode, "vx", "vxlan", options, null, 4L);
+        southboundUtils.addTerminationPoint(nodeInfo.bridgeNode, "vx", "vxlan", options, null, 4L);
         Thread.sleep(1000);
 
         String flowId = "Egress_DHCP_Client"  + "_Permit_";
-        verifyFlow(datapathId, flowId, Service.EGRESS_ACL);
+        verifyFlow(nodeInfo.datapathId, flowId, Service.EGRESS_ACL);
 
-        testDefaultSG(nport, datapathId, nn, tenantId, portId);
-        Thread.sleep(1000);
-        Assert.assertTrue(southboundUtils.deleteBridge(connectionInfo, NetvirtITConstants.INTEGRATION_BRIDGE_NAME));
+        testDefaultSG(nport, nodeInfo.datapathId, nn, tenantId, portId);
         Thread.sleep(1000);
-        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
+
+        nodeInfo.disconnect();
     }
 
     private void testDefaultSG(NeutronPort nport, long datapathId, NeutronNetwork nn, String tenantId, String portId)
index 9584ac1f1752a863bb3dcdca2ff4edbb2720f5da..6ed02626e21a015ad0415390c7e28235ceeae471 100644 (file)
@@ -41,5 +41,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
       <artifactId>utils.southbound-utils</artifactId>
       <version>${project.version}</version>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>utils.it-utils</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 </project>
index 11c298ee2e55b757dcefe3c0f1d9a9d5a8d44e59..17f298b990a35479819f4f00124cf388a7fc24b9 100644 (file)
@@ -14,6 +14,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
   <feature name='odl-ovsdb-sfc-test' version='${project.version}' description='OpenDaylight :: ovsdb-sfc-test'>
     <bundle>mvn:org.opendaylight.ovsdb/utils.mdsal-utils/{{VERSION}}</bundle>
     <bundle>mvn:org.opendaylight.ovsdb/utils.southbound-utils/{{VERSION}}</bundle>
+    <bundle>mvn:org.opendaylight.ovsdb/utils.it-utils/{{VERSION}}</bundle>
     <feature version='${project.version}'>odl-ovsdb-sfc-ui</feature>
   </feature>
 </features>
index 6472aafdb7765c10af8a157c9823d59117ddfa7c..b607050ccef9131214fd3ac92cfe7e6c462165f6 100644 (file)
@@ -77,6 +77,12 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>sonar-jacoco-listeners</artifactId>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>utils.it-utils</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <build>
index cdc79119bb456dba39dc0535127a482b08390d19..9be6c8a67b71aeb96aacd48678a68ad1f2ba3da4 100644 (file)
@@ -27,12 +27,10 @@ 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;
@@ -42,9 +40,6 @@ import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 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.AsyncDataBroker;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.mdsal.it.base.AbstractMdsalTestBase;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
@@ -67,9 +62,12 @@ import org.opendaylight.ovsdb.openstack.netvirt.sfc.it.utils.SfcConfigUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.it.utils.NetvirtSfcUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.workaround.services.FlowNames;
 import org.opendaylight.ovsdb.southbound.SouthboundConstants;
+import org.opendaylight.ovsdb.utils.it.ItUtils;
 import org.opendaylight.ovsdb.utils.mdsal.openflow.FlowUtils;
 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
+import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.ovsdb.utils.it.NodeInfo;
 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;
@@ -130,7 +128,6 @@ 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;
@@ -175,6 +172,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
     private static PipelineOrchestrator pipelineOrchestrator;
     private static Southbound southbound;
     private static DataBroker dataBroker;
+    private static ItUtils itUtils;
     public static final String CONTROLLER_IPADDRESS = "ovsdb.controller.address";
     public static final String SERVER_IPADDRESS = "ovsdbserver.ipaddress";
     public static final String SERVER_PORT = "ovsdbserver.port";
@@ -349,6 +347,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         getProperties();
 
         dataBroker = getDatabroker(getProviderContext());
+        itUtils = new ItUtils(dataBroker);
         mdsalUtils = new MdsalUtils(dataBroker);
         assertNotNull("mdsalUtils should not be null", mdsalUtils);
         southboundUtils = new SouthboundUtils(mdsalUtils);
@@ -619,58 +618,6 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         testModel(classifiersBuilder(), Classifiers.class);
     }
 
-    private class NodeInfo {
-        private ConnectionInfo connectionInfo;
-        private InstanceIdentifier<Node> ovsdbIid;
-        private String bridgeName = INTEGRATION_BRIDGE_NAME;
-        InstanceIdentifier<Node> bridgeIid;
-        NotifyingDataChangeListener ovsdbOperationalListener;
-        NotifyingDataChangeListener bridgeOperationalListener;
-        long datapathId;
-        Node ovsdbNode;
-        Node bridgeNode;
-
-        NodeInfo(ConnectionInfo connectionInfo) {
-            this.connectionInfo = connectionInfo;
-            ovsdbIid = SouthboundUtils.createInstanceIdentifier(connectionInfo);
-            bridgeIid = SouthboundUtils.createInstanceIdentifier(connectionInfo, bridgeName);
-        }
-
-        private void connect() throws InterruptedException {
-            ovsdbOperationalListener = new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, ovsdbIid);
-            ovsdbOperationalListener.registerDataChangeListener();
-            bridgeOperationalListener = new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, bridgeIid);
-            bridgeOperationalListener.registerDataChangeListener();
-            assertNotNull("connection failed", southboundUtils.addOvsdbNode(connectionInfo, NO_MDSAL_TIMEOUT));
-
-            ovsdbOperationalListener.waitForCreation();
-            ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
-            assertNotNull("node is not connected", ovsdbNode);
-
-            bridgeOperationalListener.waitForCreation();
-            assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                    + " is not connected", isControllerConnected(connectionInfo));
-
-            bridgeNode = southbound.getBridgeNode(ovsdbNode, bridgeName);
-            assertNotNull("bridge " + bridgeName + " was not found", bridgeNode);
-            datapathId = southbound.getDataPathId(bridgeNode);
-            String datapathIdString = southbound.getDatapathId(bridgeNode);
-            LOG.info("testNetVirt: bridgeNode: {}, datapathId: {} - {}", bridgeNode, datapathIdString, datapathId);
-            assertNotEquals("datapathId was not found", datapathId, 0);
-        }
-
-        void disconnect() throws InterruptedException {
-            assertTrue(southboundUtils.deleteBridge(connectionInfo, bridgeName, NO_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();
-            Node ovsdbNode = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, ovsdbIid);
-            assertNull("Ovsdb node should not be found", ovsdbNode);
-        }
-    }
-
     /**
      * 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.
@@ -680,7 +627,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         short netvirtTableOffset = 1;
         testModelPut(netvirtProvidersConfigBuilder(netvirtTableOffset), NetvirtProvidersConfig.class);
 
-        NodeInfo nodeInfo = new NodeInfo(SouthboundUtils.getConnectionInfo(addressStr, portStr));
+        NodeInfo nodeInfo = itUtils.createNodeInfo(SouthboundUtils.getConnectionInfo(addressStr, portStr), waitList);
         nodeInfo.connect();
 
         String flowId = "DEFAULT_PIPELINE_FLOW_" + pipelineOrchestrator.getTable(Service.SFC_CLASSIFIER);
@@ -712,7 +659,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         short egressTable = pipelineOrchestrator.getTable(Service.SFC_CLASSIFIER);
         testModelPut(sfcOfRendererConfigBuilder(sfcTableoffset, egressTable), SfcOfRendererConfig.class);
 
-        NodeInfo nodeInfo = new NodeInfo(SouthboundUtils.getConnectionInfo(addressStr, portStr));
+        NodeInfo nodeInfo = itUtils.createNodeInfo(SouthboundUtils.getConnectionInfo(addressStr, portStr), waitList);
         nodeInfo.connect();
 
         String flowId = "DEFAULT_PIPELINE_FLOW_" + pipelineOrchestrator.getTable(Service.SFC_CLASSIFIER);
@@ -731,13 +678,13 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         InstanceIdentifier<TerminationPoint> tpIid =
                 southboundUtils.createTerminationPointInstanceIdentifier(nodeInfo.bridgeNode, SFFDPL1NAME);
         final NotifyingDataChangeListener portOperationalListener =
-                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, tpIid);
-        portOperationalListener.registerDataChangeListener();
+                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, tpIid, waitList);
+        portOperationalListener.registerDataChangeListener(dataBroker);
 
         InstanceIdentifier<RenderedServicePath> rspIid = sfcUtils.getRspId(RSPNAME);
         final NotifyingDataChangeListener rspOperationalListener =
-                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, rspIid);
-        rspOperationalListener.registerDataChangeListener();
+                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, rspIid, waitList);
+        rspOperationalListener.registerDataChangeListener(dataBroker);
 
         testModelPut(serviceFunctionsBuilder(), ServiceFunctions.class);
         testModelPut(serviceFunctionForwardersBuilder(), ServiceFunctionForwarders.class);
@@ -776,11 +723,11 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
                 pipelineOrchestrator.getTable(Service.CLASSIFIER));
 
         final NotifyingDataChangeListener flowConfigurationListener =
-                new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION, flowIid);
-        flowConfigurationListener.registerDataChangeListener();
+                new NotifyingDataChangeListener(LogicalDatastoreType.CONFIGURATION, flowIid, waitList);
+        flowConfigurationListener.registerDataChangeListener(dataBroker);
         final NotifyingDataChangeListener flowOperationalListener =
-                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, flowIid);
-        flowOperationalListener.registerDataChangeListener();
+                new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, flowIid, waitList);
+        flowOperationalListener.registerDataChangeListener(dataBroker);
 
         deleteRsp(RSPNAME);
         rspOperationalListener.waitForDeletion();
@@ -827,7 +774,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
                 SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null,
                 setControllerEntry, null, "00:00:00:00:00:00:00:01"));
         assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
-                + " is not connected", isControllerConnected(connectionInfo));
+                + " is not connected", itUtils.isControllerConnected(connectionInfo));
 
         Node bridgeNode = southbound.getBridgeNode(ovsdbNode, bridgeName);
         assertNotNull("bridge " + bridgeName + " was not found", bridgeNode);
@@ -939,38 +886,6 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         }
     }
 
-    private boolean isControllerConnected(ConnectionInfo connectionInfo) throws InterruptedException {
-        LOG.info("isControllerConnected enter");
-        Boolean connected = false;
-        ControllerEntry controllerEntry;
-        Node ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
-        assertNotNull("ovsdb node not found", ovsdbNode);
-
-        BridgeConfigurationManager bridgeConfigurationManager =
-                (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
-        assertNotNull("Could not find BridgeConfigurationManager Service", bridgeConfigurationManager);
-        String controllerTarget = bridgeConfigurationManager.getControllersFromOvsdbNode(ovsdbNode).get(0);
-        Assert.assertNotNull("Failed to get controller target", controllerTarget);
-
-        for (int i = 0; i < 10; i++) {
-            LOG.info("isControllerConnected try {}: looking for controller: {}", i, controllerTarget);
-            OvsdbBridgeAugmentation bridge =
-                    southboundUtils.getBridge(connectionInfo, INTEGRATION_BRIDGE_NAME);
-            Assert.assertNotNull(bridge);
-            Assert.assertNotNull(bridge.getControllerEntry());
-            controllerEntry = bridge.getControllerEntry().iterator().next();
-            Assert.assertEquals(controllerTarget, controllerEntry.getTarget().getValue());
-            if (controllerEntry.isIsConnected()) {
-                Assert.assertTrue("Controller is not connected", controllerEntry.isIsConnected());
-                connected = true;
-                break;
-            }
-            Thread.sleep(1000);
-        }
-        LOG.info("isControllerConnected exit: {} - {}", connected, controllerTarget);
-        return connected;
-    }
-
     private List<NotifyingDataChangeListener> waitList = new ArrayList<>();
 
     private void closeWaitFors() {
@@ -986,122 +901,4 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         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 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: 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());
-            synchronized(this) {
-                notifyAll();
-            }
-        }
-
-        public boolean isCreated(InstanceIdentifier<?> iid) {
-            return createdIids.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();
-            updatedIids.clear();
-        }
-
-        public void registerDataChangeListener() {
-            listenerRegistration = dataBroker.registerDataChangeListener(type, iid, this,
-                    AsyncDataBroker.DataChangeScope.SUBTREE);
-        }
-
-        public void waitForCreation() throws InterruptedException {
-            waitForCreation(MDSAL_TIMEOUT);
-        }
-
-        public void waitForCreation(long timeout) throws InterruptedException {
-            synchronized (this) {
-                long _start = System.currentTimeMillis();
-                LOG.info("Waiting for {} DataChanged creation on {}", type, iid);
-                while (!isCreated(iid) && (System.currentTimeMillis() - _start) < timeout) {
-                    wait(RETRY_WAIT);
-                }
-                LOG.info("Woke up, waited {}ms for creation of {}", (System.currentTimeMillis() - _start), iid);
-            }
-        }
-
-        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();
-                LOG.info("Waiting for {} DataChanged deletion on {}", type, iid);
-                while (!isRemoved(iid) && (System.currentTimeMillis() - _start) < timeout) {
-                    wait(RETRY_WAIT);
-                }
-                LOG.info("Woke up, waited {}ms for deletion of {}", (System.currentTimeMillis() - _start), iid);
-            }
-        }
-
-        @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);
-                }
-            }
-            waitList.remove(this);
-            listenerRegistration = null;
-        }
-    }
 }
diff --git a/utils/it-utils/pom.xml b/utils/it-utils/pom.xml
new file mode 100644 (file)
index 0000000..cde19cd
--- /dev/null
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+  <parent>
+    <groupId>org.opendaylight.controller</groupId>
+    <artifactId>config-parent</artifactId>
+    <version>0.5.0-SNAPSHOT</version>
+    <relativePath/>
+  </parent>
+
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.opendaylight.ovsdb</groupId>
+  <artifactId>utils.it-utils</artifactId>
+  <version>1.3.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+
+  <scm>
+    <connection>scm:git:ssh://git.opendaylight.org:29418/ovsdb.git</connection>
+    <developerConnection>scm:git:ssh://git.opendaylight.org:29418/ovsdb.git</developerConnection>
+    <tag>HEAD</tag>
+    <url>https://wiki.opendaylight.org/view/OVSDB_Integration:Main</url>
+  </scm>
+
+  <dependencies>
+      <dependency>
+          <groupId>org.opendaylight.controller</groupId>
+          <artifactId>sal-binding-api</artifactId>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.ovsdb</groupId>
+          <artifactId>southbound-api</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.ovsdb</groupId>
+          <artifactId>utils.mdsal-utils</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.ovsdb</groupId>
+          <artifactId>utils.southbound-utils</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.ovsdb</groupId>
+          <artifactId>openstack.net-virt</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>org.opendaylight.ovsdb</groupId>
+          <artifactId>utils.servicehelper</artifactId>
+          <version>${project.version}</version>
+      </dependency>
+      <dependency>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+          <scope>compile</scope>
+      </dependency>
+  </dependencies>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+        <configuration>
+          <instructions>
+            <Export-Package>
+              org.opendaylight.ovsdb.utils.it
+            </Export-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/ItUtils.java b/utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/ItUtils.java
new file mode 100644 (file)
index 0000000..b08f2d7
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 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.utils.it;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.ovsdb.openstack.netvirt.api.BridgeConfigurationManager;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
+import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
+import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
+import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.bridge.attributes.ControllerEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class contains various utility methods used in OVSDB integration tests (IT).
+ */
+public class ItUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(ItUtils.class);
+
+    MdsalUtils mdsalUtils;
+    SouthboundUtils southboundUtils;
+    DataBroker dataBroker;
+    Southbound southbound;
+
+    /**
+     * Create a new ItUtils instance
+     * @param dataBroker  md-sal data broker
+     */
+    public ItUtils(DataBroker dataBroker) {
+        this.dataBroker = dataBroker;
+        mdsalUtils = new MdsalUtils(dataBroker);
+        southboundUtils = new SouthboundUtils(mdsalUtils);
+        southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
+    }
+
+    /**
+     * Get a NodeInfo instance initialized with this ItUtil's DataBroker
+     * @param connectionInfo ConnectionInfo for the OVSDB server
+     * @param waitList For tracking outstanding md-sal events notifications
+     * @return
+     */
+    public NodeInfo createNodeInfo(ConnectionInfo connectionInfo, List<NotifyingDataChangeListener> waitList) {
+        return new NodeInfo(connectionInfo, this, waitList);
+    }
+
+    /**
+     * Checks whether the OVSDB controller is connected. This method will retry 10 times and will through an
+     * AssertionError for any number of unexpected states.
+     * @param connectionInfo
+     * @return true if connected
+     * @throws InterruptedException
+     */
+    public boolean isControllerConnected(ConnectionInfo connectionInfo) throws InterruptedException {
+        LOG.info("isControllerConnected enter");
+        ControllerEntry controllerEntry;
+        Node ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
+        assertNotNull("ovsdb node not found", ovsdbNode);
+
+        BridgeConfigurationManager bridgeConfigurationManager =
+                (BridgeConfigurationManager) ServiceHelper.getGlobalInstance(BridgeConfigurationManager.class, this);
+        assertNotNull("Could not find BridgeConfigurationManager Service", bridgeConfigurationManager);
+        String controllerTarget = bridgeConfigurationManager.getControllersFromOvsdbNode(ovsdbNode).get(0);
+        Assert.assertNotNull("Failed to get controller target", controllerTarget);
+
+        for (int i = 0; i < 10; i++) {
+            LOG.info("isControllerConnected try {}: looking for controller: {}", i, controllerTarget);
+            OvsdbBridgeAugmentation bridge =
+                    southboundUtils.getBridge(connectionInfo, "br-int");
+            if (bridge != null && bridge.getControllerEntry() != null) {
+                controllerEntry = bridge.getControllerEntry().iterator().next();
+                Assert.assertEquals(controllerTarget, controllerEntry.getTarget().getValue());
+                if (controllerEntry.isIsConnected()) {
+                    LOG.info("isControllerConnected exit: true {}", controllerTarget);
+                    return true;
+                }
+            }
+            Thread.sleep(1000);
+        }
+        LOG.info("isControllerConnected exit: false {}", controllerTarget);
+        return false;
+    }
+}
diff --git a/utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/NodeInfo.java b/utils/it-utils/src/main/java/org/opendaylight/ovsdb/utils/it/NodeInfo.java
new file mode 100644 (file)
index 0000000..f145bb5
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 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.utils.it;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.List;
+
+import org.junit.Assert;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.ovsdb.utils.mdsal.utils.NotifyingDataChangeListener;
+import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for connections to an OVSDB node. Contains various info for the node
+ * as public data members.
+ */
+public class NodeInfo {
+    private static final Logger LOG = LoggerFactory.getLogger(NodeInfo.class);
+    public static final String INTEGRATION_BRIDGE_NAME = "br-int";
+
+    private ConnectionInfo connectionInfo;
+    private InstanceIdentifier<Node> ovsdbIid;
+    InstanceIdentifier<Node> bridgeIid;
+    public long datapathId;
+    public Node ovsdbNode;
+    public Node bridgeNode;
+    NotifyingDataChangeListener ovsdbWaiter;
+    NotifyingDataChangeListener bridgeWaiter;
+    List<NotifyingDataChangeListener> waitList;
+    ItUtils itUtils;
+
+    /**
+     * Create a new NodeInfo object
+     * @param connectionInfo of the OVSDB node
+     * @param itUtils ItUtils instance
+     * @param waitList for tracking outstanding md-sal events
+     */
+    NodeInfo(ConnectionInfo connectionInfo, ItUtils itUtils, List<NotifyingDataChangeListener> waitList) {
+        this.connectionInfo = connectionInfo;
+        this.itUtils = itUtils;
+        this.waitList = waitList;
+        ovsdbIid = SouthboundUtils.createInstanceIdentifier(connectionInfo);
+        bridgeIid = SouthboundUtils.createInstanceIdentifier(connectionInfo, INTEGRATION_BRIDGE_NAME);
+    }
+
+    /**
+     * Connect to the OVSDB node, wait for the connection to be established and for the integration bridge
+     * to be successfully created. Contains assertions for unexpected states
+     * @throws InterruptedException
+     */
+    public void connect() throws InterruptedException {
+        ovsdbWaiter = new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, ovsdbIid, waitList);
+        ovsdbWaiter.registerDataChangeListener(itUtils.dataBroker);
+        bridgeWaiter = new NotifyingDataChangeListener(LogicalDatastoreType.OPERATIONAL, bridgeIid, waitList);
+        bridgeWaiter.registerDataChangeListener(itUtils.dataBroker);
+
+        Assert.assertNotNull("connection failed", itUtils.southboundUtils.addOvsdbNode(connectionInfo, 0));
+
+        ovsdbWaiter.waitForCreation();
+        ovsdbNode = itUtils.southboundUtils.getOvsdbNode(connectionInfo);
+        Assert.assertNotNull("node is not connected", ovsdbNode);
+
+        bridgeWaiter.waitForCreation();
+        Assert.assertTrue("Controller " + SouthboundUtils.connectionInfoToString(connectionInfo)
+                + " is not connected", itUtils.isControllerConnected(connectionInfo));
+
+        bridgeNode = itUtils.southbound.getBridgeNode(ovsdbNode, INTEGRATION_BRIDGE_NAME);
+        Assert.assertNotNull("bridge " + INTEGRATION_BRIDGE_NAME + " was not found", bridgeNode);
+        datapathId = itUtils.southbound.getDataPathId(bridgeNode);
+        String datapathIdString = itUtils.southbound.getDatapathId(bridgeNode);
+        LOG.info("testNetVirt: bridgeNode: {}, datapathId: {} - {}", bridgeNode, datapathIdString, datapathId);
+        Assert.assertNotEquals("datapathId was not found", datapathId, 0);
+    }
+
+    /**
+     * Remove integration bridge and teardown connection. Contains assertions for unexpected states.
+     * @throws InterruptedException
+     */
+    public void disconnect() throws InterruptedException {
+        Assert.assertTrue(itUtils.southboundUtils.deleteBridge(connectionInfo, INTEGRATION_BRIDGE_NAME, 0));
+        itUtils.southboundUtils.deleteBridge(connectionInfo, INTEGRATION_BRIDGE_NAME, 0);
+        bridgeWaiter.waitForDeletion();
+        Node bridgeNode = itUtils.mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, bridgeIid);
+        Assert.assertNull("Bridge should not be found", bridgeNode);
+        Assert.assertTrue(itUtils.southboundUtils.disconnectOvsdbNode(connectionInfo, 0));
+        itUtils.southboundUtils.disconnectOvsdbNode(connectionInfo, 0);
+        ovsdbWaiter.waitForDeletion();
+        Node ovsdbNode = itUtils.mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, ovsdbIid);
+        Assert.assertNull("Ovsdb node should not be found", ovsdbNode);
+    }
+
+}
index f0c0610db1a26846ab95c7053ebf29d9f99b9612..ef5b52c89e3199057b33a6df69951d27fd506674 100644 (file)
   </scm>
 
   <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.ovsdb</groupId>
+      <artifactId>southbound-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
     <!-- testing dependencies -->
     <dependency>
       <groupId>junit</groupId>
@@ -59,4 +69,4 @@
       </plugin>
     </plugins>
   </build>
-</project>
\ No newline at end of file
+</project>
diff --git a/utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/NotifyingDataChangeListener.java b/utils/mdsal-utils/src/main/java/org/opendaylight/ovsdb/utils/mdsal/utils/NotifyingDataChangeListener.java
new file mode 100644 (file)
index 0000000..2bf689a
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 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.utils.mdsal.utils;
+
+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.AsyncDataBroker;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class provides methods for checking or waiting for various md-sal operations to complete.
+ * Once an instance is created one must invoke the registerDataChangeListener method
+ * with a DataBroker.
+ */
+public class NotifyingDataChangeListener implements AutoCloseable, DataChangeListener {
+    private static final Logger LOG = LoggerFactory.getLogger(NotifyingDataChangeListener.class);
+    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 InstanceIdentifier<?> iid;
+    private final int RETRY_WAIT = 100;
+    private final int MDSAL_TIMEOUT = 1000;
+    private ListenerRegistration<?> listenerRegistration;
+    private List<NotifyingDataChangeListener> waitList = null;
+
+    /**
+     * Create a new NotifyingDataChangeListener
+     * @param type
+     * @param iid of the md-sal object we're waiting for
+     * @param waitList for tracking outstanding changes
+     */
+    public NotifyingDataChangeListener(LogicalDatastoreType type,
+                                        InstanceIdentifier<?> iid, List<NotifyingDataChangeListener> waitList) {
+        this.type = type;
+        this.iid = iid;
+        this.waitList = waitList;
+        if(this.waitList != null) {
+            this.waitList.add(this);
+        }
+    }
+
+    /**
+     * Completely reset the state of this NotifyingDataChangeListener.
+     * @param type
+     * @param iid of the md-sal object we're waiting for
+     * @throws Exception
+     */
+    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: 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());
+        synchronized (this) {
+            notifyAll();
+        }
+    }
+
+    public boolean isCreated(InstanceIdentifier<?> iid) {
+        return createdIids.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();
+        updatedIids.clear();
+    }
+
+    public void registerDataChangeListener(DataBroker dataBroker) {
+        listenerRegistration = dataBroker.registerDataChangeListener(type, iid, this,
+                AsyncDataBroker.DataChangeScope.SUBTREE);
+    }
+
+    public void waitForCreation() throws InterruptedException {
+        waitForCreation(MDSAL_TIMEOUT);
+    }
+
+    public void waitForCreation(long timeout) throws InterruptedException {
+        synchronized (this) {
+            long _start = System.currentTimeMillis();
+            LOG.info("Waiting for {} DataChanged creation on {}", type, iid);
+            while (!isCreated(iid) && (System.currentTimeMillis() - _start) < timeout) {
+                wait(RETRY_WAIT);
+            }
+            LOG.info("Woke up, waited {}ms for creation of {}", (System.currentTimeMillis() - _start), iid);
+        }
+    }
+
+    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();
+            LOG.info("Waiting for {} DataChanged deletion on {}", type, iid);
+            while (!isRemoved(iid) && (System.currentTimeMillis() - _start) < timeout) {
+                wait(RETRY_WAIT);
+            }
+            LOG.info("Woke up, waited {}ms for deletion of {}", (System.currentTimeMillis() - _start), iid);
+        }
+    }
+
+    @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);
+            }
+        }
+        if (waitList != null) {
+            waitList.remove(this);
+        }
+        listenerRegistration = null;
+    }
+}
index ce6acfb639c57d4a44cf088b366ffc2bd4eefd14..19ba103fe6213ffbe121d8070735e5fed4b6bdaf 100644 (file)
@@ -57,7 +57,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
     <module>servicehelper</module>
     <module>southbound-utils</module>
     <module>hwvtepsouthbound-utils</module>
-    <module>yang-utils</module>
+    <module>it-utils</module>
   </modules>
 
 <!-- DO NOT install or deploy the repo root pom as it's only needed to initiate a build -->