Add IT for southbound 99/17599/2
authorSam Hague <shague@redhat.com>
Thu, 2 Apr 2015 02:25:40 +0000 (22:25 -0400)
committerSam Hague <shague@redhat.com>
Thu, 2 Apr 2015 21:03:25 +0000 (17:03 -0400)
Change-Id: Iff82a08c08cae8013417031f48f32d6639b94b8c
Signed-off-by: Sam Hague <shague@redhat.com>
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbClientKey.java
southbound/southbound-it/pom.xml [new file with mode: 0644]
southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractConfigTestBase.java [new file with mode: 0644]
southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractMdsalTestBase.java [new file with mode: 0644]
southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/SouthboundIT.java [new file with mode: 0644]

index 91e65f260531e842db8232cd6e0eb4c455d5bcfb..778ba1000bcdd46170da1e89b8b0858c373d6cdc 100644 (file)
@@ -27,7 +27,7 @@ public class OvsdbClientKey {
         port = locator.getPort();
     }
 
-    OvsdbClientKey(IpAddress ip, PortNumber port) {
+    public OvsdbClientKey(IpAddress ip, PortNumber port) {
         this.ipaddress = ip;
         this.port = port;
     }
diff --git a/southbound/southbound-it/pom.xml b/southbound/southbound-it/pom.xml
new file mode 100644 (file)
index 0000000..ced480a
--- /dev/null
@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: --><!--
+Copyright (c) 2014 Cisco Systems, 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
+-->
+<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.3.0-SNAPSHOT</version>
+    <relativePath/>
+  </parent>
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>org.opendaylight.ovsdb</groupId>
+  <artifactId>southbound-it</artifactId>
+  <version>1.1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+  <dependencyManagement>
+    <dependencies>
+      <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>southbound-artifacts</artifactId>
+        <version>${project.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>config-artifacts</artifactId>
+        <version>0.3.0-SNAPSHOT</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+      <dependency>
+        <groupId>org.opendaylight.controller</groupId>
+        <artifactId>mdsal-artifacts</artifactId>
+        <version>${mdsal.version}</version>
+        <type>pom</type>
+        <scope>import</scope>
+      </dependency>
+    </dependencies>
+  </dependencyManagement>
+  <dependencies>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-binding-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>sal-common-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>southbound-features</artifactId>
+      <classifier>features</classifier>
+      <type>xml</type>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.controller</groupId>
+      <artifactId>config-util</artifactId>
+    </dependency>
+    <!-- Dependencies for pax exam karaf container -->
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-container-karaf</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam-junit4</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.exam</groupId>
+      <artifactId>pax-exam</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.ops4j.pax.url</groupId>
+      <artifactId>pax-url-aether</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>javax.inject</groupId>
+      <artifactId>javax.inject</artifactId>
+      <version>1</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.karaf.features</groupId>
+      <artifactId>org.apache.karaf.features.core</artifactId>
+      <version>${karaf.version}</version>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>compile</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.karaf.tooling</groupId>
+      <artifactId>karaf-maven-plugin</artifactId>
+      <version>${karaf.version}</version>
+    </dependency>
+  </dependencies>
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-failsafe-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>integration-test</goal>
+              <goal>verify</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <!-- Needed if you use versionAsInProject() -->
+      <plugin>
+        <groupId>org.apache.servicemix.tooling</groupId>
+        <artifactId>depends-maven-plugin</artifactId>
+        <version>1.2</version>
+        <executions>
+          <execution>
+            <id>generate-depends-file</id>
+            <goals>
+              <goal>generate-depends-file</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-checkstyle-plugin</artifactId>
+        <configuration>
+          <configLocation>
+            ${project.basedir}/../../commons/parent/src/main/resources/ovsdb_checks.xml
+          </configLocation>
+          <includeTestSourceDirectory>true</includeTestSourceDirectory>
+          <failsOnError>true</failsOnError>
+          <includes>**/*.java,**/*.xml,**/*.ini,**/*.sh,**/*.bat</includes>
+          <excludes>**/yang/</excludes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractConfigTestBase.java b/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractConfigTestBase.java
new file mode 100644 (file)
index 0000000..04c5a1f
--- /dev/null
@@ -0,0 +1,141 @@
+package org.opendaylight.ovsdb.southbound.it;
+
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.features;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.karafDistributionConfiguration;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.keepRuntimeFolder;
+
+import com.google.common.collect.ObjectArrays;
+
+import java.io.File;
+import java.lang.management.ManagementFactory;
+import java.util.Calendar;
+
+import javax.management.InstanceNotFoundException;
+
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.rules.TestWatcher;
+import org.junit.runner.Description;
+import org.opendaylight.controller.config.api.ConfigRegistry;
+import org.opendaylight.controller.config.util.ConfigRegistryJMXClient;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
+import org.ops4j.pax.exam.options.MavenArtifactUrlReference;
+import org.ops4j.pax.exam.options.MavenUrlReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractConfigTestBase {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractConfigTestBase.class);
+    public static final String ORG_OPS4J_PAX_LOGGING_CFG = "etc/org.ops4j.pax.logging.cfg";
+    public static final String CUSTOM_PROPERTIES = "etc/custom.properties";
+    private static final String SERVER_IPADDRESS = "ovsdbserver.ipaddress";
+    private static final String SERVER_PORT = "ovsdbserver.port";
+    private static final String CONNECTION_TYPE = "ovsdbserver.connection";
+    private static final String CONNECTION_TYPE_ACTIVE = "active";
+    private static final String CONNECTION_TYPE_PASSIVE = "passive";
+    private static final String DEFAULT_SERVER_IPADDRESS = "127.0.0.1";
+    private static final String DEFAULT_SERVER_PORT = "6640";
+
+    /*
+     * Wait up to 10s for our configured module to come up
+     */
+    private static final int MODULE_TIMEOUT = 10000;
+
+    public abstract String getModuleName();
+
+    public abstract String getInstanceName();
+
+    public abstract MavenUrlReference getFeatureRepo();
+
+    public abstract String getFeatureName();
+
+    public Option[] getLoggingOptions() {
+        Option[] options = new Option[] {
+                editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
+                        logConfiguration(AbstractConfigTestBase.class),
+                        LogLevel.INFO.name()),
+                editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
+                        "log4j.logger.org.opendaylight.ovsdb.southbound-impl",
+                        LogLevel.DEBUG.name())
+        };
+        return options;
+    }
+
+    public String logConfiguration(Class<?> klazz) {
+        return "log4j.logger." + klazz.getPackage().getName();
+    }
+
+    public Option[] getPropertiesOptions() {
+        return null;
+    }
+
+    public MavenArtifactUrlReference getKarafDistro() {
+        MavenArtifactUrlReference karafUrl = maven()
+                .groupId("org.opendaylight.controller")
+                .artifactId("opendaylight-karaf-empty")
+                .version("1.5.0-SNAPSHOT")
+                .type("zip");
+        return karafUrl;
+    }
+
+    @Configuration
+    public Option[] config() {
+        Option[] options = new Option[] {
+                // KarafDistributionOption.debugConfiguration("5005", true),
+                karafDistributionConfiguration()
+                        .frameworkUrl(getKarafDistro())
+                        .unpackDirectory(new File("target/exam"))
+                        .useDeployFolder(false),
+                keepRuntimeFolder(),
+                features(getFeatureRepo() , getFeatureName()),
+        };
+        options = ObjectArrays.concat(options, getLoggingOptions(), Option.class);
+        options = ObjectArrays.concat(options, getPropertiesOptions(), Option.class);
+        return options;
+    }
+
+    public void setup() throws Exception {
+        LOG.info("Module: {} Instance: {} attempting to configure.",
+                getModuleName(),getInstanceName());
+        Calendar start = Calendar.getInstance();
+        ConfigRegistry configRegistryClient = new ConfigRegistryJMXClient(ManagementFactory
+                .getPlatformMBeanServer());
+        for (int timer = 0;timer < MODULE_TIMEOUT;timer++) {
+            try {
+                configRegistryClient.lookupConfigBean(getModuleName(), getInstanceName());
+                Thread.sleep(1);
+            } catch (InstanceNotFoundException e) {
+                if (timer < MODULE_TIMEOUT) {
+                    continue;
+                } else {
+                    throw e;
+                }
+            } catch (InterruptedException e) {
+                LOG.error("Exception: ",e);
+            }
+        }
+        Calendar stop = Calendar.getInstance();
+        LOG.info("Module: {} Instance: {} configured after {} ms",
+                getModuleName(),getInstanceName(),
+                stop.getTimeInMillis() - start.getTimeInMillis());
+    }
+
+    @Rule
+    public TestRule watcher = new TestWatcher() {
+        @Override
+        protected void starting(Description description) {
+            LOG.info("TestWatcher: Starting test: {}",
+                    description.getDisplayName());
+        }
+
+        @Override
+        protected void finished(Description description) {
+            LOG.info("TestWatcher: Finished test: {}", description.getDisplayName());
+        }
+    };
+}
diff --git a/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractMdsalTestBase.java b/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/AbstractMdsalTestBase.java
new file mode 100644 (file)
index 0000000..bfa65aa
--- /dev/null
@@ -0,0 +1,67 @@
+package org.opendaylight.ovsdb.southbound.it;
+
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+
+import java.util.Calendar;
+
+import javax.inject.Inject;
+
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.karaf.options.LogLevelOption.LogLevel;
+import org.ops4j.pax.exam.util.Filter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.ObjectArrays;
+
+public abstract class AbstractMdsalTestBase extends AbstractConfigTestBase implements BindingAwareProvider {
+
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractMdsalTestBase.class);
+    private static final int REGISTRATION_TIMEOUT = 10000;
+    @Inject @Filter(timeout = 60000)
+    private BindingAwareBroker broker;
+    private ProviderContext session = null;
+
+    public ProviderContext getSession() {
+        return session;
+    }
+
+    @Override
+    public void onSessionInitiated(ProviderContext session) {
+        LOG.info("Session Initiated: {}",session);
+        this.session = session;
+    }
+
+    @Override
+    public void setup() throws Exception {
+        super.setup();
+        Calendar start = Calendar.getInstance();
+        broker.registerProvider(this);
+        for (int timer = 0;timer < REGISTRATION_TIMEOUT;timer++) {
+            if (session != null) {
+                Calendar stop = Calendar.getInstance();
+                LOG.info("Registered with the MD-SAL after {} ms",
+                        stop.getTimeInMillis() - start.getTimeInMillis());
+                return;
+            } else {
+                Thread.sleep(1);
+            }
+        }
+        throw new RuntimeException("Session not initiated after " + REGISTRATION_TIMEOUT + " ms");
+    }
+
+    @Override
+    public Option[] getLoggingOptions() {
+        Option[] options = new Option[] {
+                editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
+                        logConfiguration(AbstractMdsalTestBase.class),
+                        LogLevel.INFO.name()),
+        };
+        options = ObjectArrays.concat(options, super.getLoggingOptions(),Option.class);
+        return options;
+    }
+
+}
diff --git a/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/SouthboundIT.java b/southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/SouthboundIT.java
new file mode 100644 (file)
index 0000000..d33475e
--- /dev/null
@@ -0,0 +1,318 @@
+package org.opendaylight.ovsdb.southbound.it;
+
+import static org.junit.Assert.assertFalse;
+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 static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.karaf.options.KarafDistributionOption.editConfigurationFilePut;
+
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Properties;
+import java.util.concurrent.ExecutionException;
+import javax.inject.Inject;
+import org.junit.Before;
+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.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.ovsdb.southbound.OvsdbClientKey;
+import org.opendaylight.ovsdb.southbound.SouthboundMapper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.options.MavenUrlReference;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.osgi.framework.BundleContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class SouthboundIT extends AbstractMdsalTestBase {
+    private static final Logger LOG = LoggerFactory.getLogger(SouthboundIT.class);
+    private static final String SERVER_IPADDRESS = "ovsdbserver.ipaddress";
+    private static final String SERVER_PORT = "ovsdbserver.port";
+    private static final String CONNECTION_TYPE = "ovsdbserver.connection";
+    private static final String CONNECTION_TYPE_ACTIVE = "active";
+    private static final String CONNECTION_TYPE_PASSIVE = "passive";
+    private static final int CONNECTION_INIT_TIMEOUT = 10000;
+    private static final String DEFAULT_SERVER_IPADDRESS = "127.0.0.1";
+    private static final String DEFAULT_SERVER_PORT = "6640";
+    private static Boolean writeStatus = false;
+    private static Boolean readStatus = false;
+    private static Boolean deleteStatus = false;
+    private static DataBroker dataBroker = null;
+    private static String addressStr;
+    private static String portStr;
+    private static String connectionType;
+    private static Boolean setup = false;
+
+    @Inject
+    private BundleContext bc;
+
+    @Configuration
+    public Option[] config() {
+        return super.config();
+    }
+
+    @Override
+    public String getModuleName() {
+        return "southbound-impl";
+    }
+
+    @Override
+    public String getInstanceName() {
+        return "southbound-default";
+    }
+
+    @Override
+    public MavenUrlReference getFeatureRepo() {
+        return maven()
+                .groupId("org.opendaylight.ovsdb")
+                .artifactId("southbound-features")
+                .classifier("features")
+                .type("xml")
+                .versionAsInProject();
+    }
+
+    @Override
+    public String getFeatureName() {
+        return "odl-ovsdb-southbound-impl-ui";
+    }
+
+    protected String usage() {
+        return "Integration Test needs a valid connection configuration as follows :\n"
+                + "active connection : mvn -Dovsdbserver.ipaddress=x.x.x.x -Dovsdbserver.port=yyyy verify\n"
+                + "passive connection : mvn -Dovsdbserver.connection=passive verify\n";
+    }
+
+    @Override
+    public Option[] getPropertiesOptions() {
+        Properties props = new Properties(System.getProperties());
+        String addressStr = props.getProperty(SERVER_IPADDRESS, DEFAULT_SERVER_IPADDRESS);
+        String portStr = props.getProperty(SERVER_PORT, DEFAULT_SERVER_PORT);
+        String connectionType = props.getProperty(CONNECTION_TYPE, CONNECTION_TYPE_ACTIVE);
+
+        LOG.info("1: Using the following properties: mode= {}, ip:port= {}:{}",
+                connectionType, addressStr, portStr);
+
+        Option[] options = new Option[] {
+                editConfigurationFilePut(CUSTOM_PROPERTIES, SERVER_IPADDRESS, addressStr),
+                editConfigurationFilePut(CUSTOM_PROPERTIES, SERVER_PORT, portStr),
+                editConfigurationFilePut(CUSTOM_PROPERTIES, CONNECTION_TYPE, connectionType)
+        };
+        return options;
+    }
+
+    @Before
+    public void setUp() {
+        if (setup == true) {
+            LOG.info("Skipping setUp, already initialized");
+            return;
+        }
+
+        try {
+            super.setup();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        dataBroker = getSession().getSALService(DataBroker.class);
+
+        addressStr = bc.getProperty(SERVER_IPADDRESS);
+        portStr = bc.getProperty(SERVER_PORT);
+        connectionType = bc.getProperty(CONNECTION_TYPE);
+
+        LOG.info("Using the following properties: mode= {}, ip:port= {}:{}",
+                connectionType, addressStr, portStr);
+        if (connectionType.equalsIgnoreCase(CONNECTION_TYPE_ACTIVE)) {
+            if (addressStr == null) {
+                fail(usage());
+            }
+        }
+
+        setup = true;
+    }
+
+    @Test
+    public void testPassiveNode() throws InterruptedException {
+        if (connectionType.equalsIgnoreCase(CONNECTION_TYPE_PASSIVE)) {
+            //Wait for CONNECTION_INIT_TIMEOUT for the Passive connection to be initiated by the ovsdb-server.
+            Thread.sleep(CONNECTION_INIT_TIMEOUT);
+        }
+    }
+
+    @Test
+    public void testAddRemoveOvsdbNode() throws InterruptedException {
+        OvsdbClientKey ovsdbClientKey = getOvsdbClientKey(addressStr, portStr);
+        DataBroker dataBroker = getSession().getSALService(DataBroker.class);
+
+        // Write OVSDB node to configuration
+        final ReadWriteTransaction configNodeTx = dataBroker.newReadWriteTransaction();
+        configNodeTx.put(LogicalDatastoreType.CONFIGURATION, ovsdbClientKey.toInstanceIndentifier(),
+                SouthboundMapper.createNode(ovsdbClientKey));
+        Futures.addCallback(configNodeTx.submit(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(final Void result) {
+                LOG.info("success writing node to configuration: " + configNodeTx);
+                writeStatus = true;
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                fail("failed writing node to configuration: " + configNodeTx);
+            }
+        });
+
+        Thread.sleep(1000);
+
+        assertTrue("Failed to write node to configuration", writeStatus);
+
+        // Read from operational to verify if the OVSDB node is connected
+        final ReadOnlyTransaction readNodeTx = dataBroker.newReadOnlyTransaction();
+        ListenableFuture<Optional<Node>> dataFuture = readNodeTx.read(
+                LogicalDatastoreType.OPERATIONAL, ovsdbClientKey.toInstanceIndentifier());
+        Futures.addCallback(dataFuture, new FutureCallback<Optional<Node>>() {
+            @Override
+            public void onSuccess(final Optional<Node> result) {
+                LOG.info("success reading node from operational: " + readNodeTx);
+                LOG.info("Optional result: {}", result);
+                if (result.isPresent()) {
+                    LOG.info("node: {}", result.get());
+                    readStatus = true;
+                }
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                fail("failed reading node from operational: " + readNodeTx);
+            }
+        });
+
+        Thread.sleep(1000);
+
+        assertTrue("Failed to read node from operational", readStatus);
+
+        // Delete OVSDB node from configuration
+        final ReadWriteTransaction deleteNodeTx = dataBroker.newReadWriteTransaction();
+        deleteNodeTx.delete(LogicalDatastoreType.CONFIGURATION, ovsdbClientKey.toInstanceIndentifier());
+        Futures.addCallback(deleteNodeTx.submit(), new FutureCallback<Void>() {
+            @Override
+            public void onSuccess(final Void result) {
+                LOG.info("success deleting node from configuration: " + deleteNodeTx);
+                deleteStatus = true;
+            }
+
+            @Override
+            public void onFailure(final Throwable throwable) {
+                fail("failed deleting node from configuration: " + deleteNodeTx);
+            }
+        });
+
+        Thread.sleep(1000);
+
+        assertTrue("Failed to delete node from configuration", deleteStatus);
+
+        // Read from operational to verify if the OVSDB node is disconnected
+        // Similar to the earlier read, but this time synchronously
+        final ReadOnlyTransaction readNodeTx2 = dataBroker.newReadOnlyTransaction();
+        Optional<Node> node = Optional.absent();
+        try {
+            node = readNodeTx2.read(LogicalDatastoreType.OPERATIONAL,
+                    ovsdbClientKey.toInstanceIndentifier()).checkedGet();
+            assertFalse("Failed to delete node from configuration and node is still connected",
+                    node.isPresent());
+        } catch (final ReadFailedException e) {
+            LOG.debug("Read Operational/DS for Node fail! {}", ovsdbClientKey.toInstanceIndentifier(), e);
+            fail("failed reading node from operational: " + readNodeTx2 + e);
+        }
+    }
+
+    @Test
+    public void testAddRemoveOvsdbNode2() throws InterruptedException {
+        addNode("192.168.120.31", "6640");
+        Thread.sleep(1000);
+        Node node = readNode("192.168.120.31", "6640", LogicalDatastoreType.OPERATIONAL);
+        assertNotNull(node);
+        LOG.info("Connected node: {}", node);
+        deleteNode("192.168.120.31", "6640");
+        Thread.sleep(1000);
+        node = readNode("192.168.120.31", "6640", LogicalDatastoreType.OPERATIONAL);
+        assertNull(node);
+    }
+
+    private OvsdbClientKey getOvsdbClientKey(String addressStr, String portStr) {
+        InetAddress inetAddress = null;
+        try {
+            inetAddress = InetAddress.getByName(addressStr);
+        } catch (UnknownHostException e) {
+            fail("Could not allocate InetAddress: " + e);
+        }
+
+        IpAddress address = SouthboundMapper.createIpAddress(inetAddress);
+        PortNumber port = new PortNumber(Integer.parseInt(portStr));
+
+        return new OvsdbClientKey(address, port);
+    }
+
+    private void addNode(String addressStr, String portStr) {
+        OvsdbClientKey ovsdbClientKey = getOvsdbClientKey(addressStr, portStr);
+
+        final ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
+        rwTx.put(LogicalDatastoreType.CONFIGURATION, ovsdbClientKey.toInstanceIndentifier(),
+                SouthboundMapper.createNode(ovsdbClientKey));
+        CheckedFuture<Void, TransactionCommitFailedException> commitFuture = rwTx.submit();
+        try {
+            commitFuture.get();
+        } catch (ExecutionException | InterruptedException e) {
+            fail("Failed transaction: " + rwTx + e);
+        }
+    }
+
+    private Node readNode(String addressStr, String portStr, LogicalDatastoreType type) {
+        OvsdbClientKey ovsdbClientKey = getOvsdbClientKey(addressStr, portStr);
+
+        final ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
+        Optional<Node> node = Optional.absent();
+        CheckedFuture<Optional<Node>, ReadFailedException> read;
+        read = rwTx.read(type, ovsdbClientKey.toInstanceIndentifier());
+        try {
+            node = read.checkedGet();
+            if (node.isPresent()) {
+                return node.get();
+            }
+        } catch (ReadFailedException e) {
+            fail("Failed transaction: " + rwTx + e);
+        }
+
+        return null;
+    }
+
+    private void deleteNode(String addressStr, String portStr) {
+        OvsdbClientKey ovsdbClientKey = getOvsdbClientKey(addressStr, portStr);
+
+        final ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction();
+        rwTx.delete(LogicalDatastoreType.CONFIGURATION, ovsdbClientKey.toInstanceIndentifier());
+        CheckedFuture<Void, TransactionCommitFailedException> commitFuture = rwTx.submit();
+        try {
+            commitFuture.get();
+        } catch (ExecutionException | InterruptedException e) {
+            fail("Failed transaction: " + rwTx + e);
+        }
+    }
+}