Merge "Added support for remote security group update. 1)When a new port is created...
authorSam Hague <shague@redhat.com>
Sun, 1 Nov 2015 01:18:53 +0000 (01:18 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Sun, 1 Nov 2015 01:18:53 +0000 (01:18 +0000)
23 files changed:
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepConnectionInstance.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepConnectionManager.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepDataChangeListener.java [new file with mode: 0644]
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepMonitorCallback.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepSouthboundMapper.java
hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepSouthboundProvider.java
openstack/net-virt-providers/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/providers/openflow13/services/ClassifierService.java
openstack/net-virt-sfc/api/src/main/yang/netvirt-acl.yang
openstack/net-virt-sfc/api/src/main/yang/netvirt-classifier.yang
openstack/net-virt-sfc/impl/pom.xml
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/openflow13/INetvirtSfcOF13Provider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NetvirtSfcOF13Provider.java
openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NshUtils.java [new file with mode: 0644]
openstack/net-virt-sfc/it/pom.xml
openstack/net-virt-sfc/it/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/NetvirtSfcIT.java
southbound/southbound-impl/pom.xml
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/transactions/md/OvsdbNodeRemoveCommand.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/transactions/md/TransactionInvokerImpl.java
southbound/southbound-impl/src/test/java/org/opendaylight/ovsdb/southbound/transactions/md/TransactionInvokerImplTest.java
southbound/southbound-it/src/test/java/org/opendaylight/ovsdb/southbound/it/SouthboundIT.java
utils/southbound-utils/src/main/java/org/opendaylight/ovsdb/utils/southbound/utils/SouthboundUtils.java

index b4055764ddeb5b99a17d60178f6f06e7b15d1467..45843d443aaa16fd11a0a6ec945055c5538f4311 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
 import org.opendaylight.ovsdb.lib.schema.TableSchema;
 import org.opendaylight.ovsdb.lib.schema.typed.TypedBaseTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
@@ -63,6 +64,7 @@ public class HwvtepConnectionInstance implements OvsdbClient{
     private volatile boolean hasDeviceOwnership = false;
     private Entity connectedEntity;
     private EntityOwnershipCandidateRegistration deviceOwnershipCandidateRegistration;
+    private HwvtepGlobalAugmentation initialCreatedData = null;
 
 
     HwvtepConnectionInstance (ConnectionInfo key,OvsdbClient client,
@@ -81,13 +83,17 @@ public class HwvtepConnectionInstance implements OvsdbClient{
 
     public void registerCallbacks() {
         if ( this.callback == null) {
+            if(this.initialCreatedData != null) {
+                this.updateConnectionAttributes();
+            }
+
             try {
                 List<String> databases = getDatabases().get();
                 this.callback = new HwvtepMonitorCallback(this,txInvoker);
                 for (String database : databases) {
                     DatabaseSchema dbSchema = getSchema(database).get();
                     if (dbSchema != null) {
-                        monitorAllTables(database, dbSchema);
+                        monitorAllTables(database, dbSchema, HwvtepSchemaConstants.databaseName);
                     } else {
                         LOG.warn("No schema reported for database {} for key {}",database,connectionInfo);
                     }
@@ -115,11 +121,16 @@ public class HwvtepConnectionInstance implements OvsdbClient{
         }
     }
 
-    private void monitorAllTables(String database, DatabaseSchema dbSchema) {
+    private void monitorAllTables(String database, DatabaseSchema dbSchema, String filter) {
+        if((filter != null) && (!dbSchema.getName().equals(filter))) {
+            LOG.debug("Not monitoring tables in {}, filter: {}", dbSchema.getName(), filter);
+            return;
+        }
         Set<String> tables = dbSchema.getTables();
         if (tables != null) {
             List<MonitorRequest<GenericTableSchema>> monitorRequests = Lists.newArrayList();
             for (String tableName : tables) {
+                LOG.debug("HwvtepSouthbound monitoring table {} in {}", tableName, dbSchema.getName());
                 GenericTableSchema tableSchema = dbSchema.table(tableName, GenericTableSchema.class);
                 Set<String> columns = tableSchema.getColumns();
                 MonitorRequestBuilder<GenericTableSchema> monitorBuilder = MonitorRequestBuilder.builder(tableSchema);
@@ -134,6 +145,17 @@ public class HwvtepConnectionInstance implements OvsdbClient{
         }
     }
 
+    private void updateConnectionAttributes() {
+        LOG.debug("Update attributes of ovsdb node ip: {} port: {}",
+                    this.initialCreatedData.getConnectionInfo().getRemoteIp(),
+                    this.initialCreatedData.getConnectionInfo().getRemotePort());
+        /*
+         * TODO: Do we have anything to update?
+         * Hwvtep doesn't have other_config or external_ids like
+         * Open_vSwitch. What else will be needed?
+         */
+    }
+
     public ListenableFuture<List<String>> getDatabases() {
         return client.getDatabases();
     }
@@ -270,4 +292,8 @@ public class HwvtepConnectionInstance implements OvsdbClient{
             setHasDeviceOwnership(Boolean.FALSE);
         }
     }
+
+    public void setHwvtepGlobalAugmentation(HwvtepGlobalAugmentation hwvtepGlobalData) {
+        this.initialCreatedData = hwvtepGlobalData;
+    }
 }
index 3a117bdccd1eb01ed1b649671f5ff280a0b0a496..fd6ebb5186b8f52a562e9a38d9c74e3983b9b007 100644 (file)
@@ -10,6 +10,8 @@ package org.opendaylight.ovsdb.hwvtepsouthbound;
 
 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
 
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
@@ -30,6 +32,7 @@ import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipS
 import org.opendaylight.ovsdb.hwvtepsouthbound.transactions.md.TransactionInvoker;
 import org.opendaylight.ovsdb.lib.OvsdbClient;
 import org.opendaylight.ovsdb.lib.OvsdbConnectionListener;
+import org.opendaylight.ovsdb.lib.impl.OvsdbConnectionService;
 import org.opendaylight.ovsdb.lib.operations.Operation;
 import org.opendaylight.ovsdb.lib.operations.OperationResult;
 import org.opendaylight.ovsdb.lib.operations.Select;
@@ -37,6 +40,7 @@ import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.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;
@@ -84,7 +88,7 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
     public void connected(@Nonnull final OvsdbClient client) {
         HwvtepConnectionInstance hwClient = connectedButCallBacksNotRegistered(client);
         registerEntityForOwnership(hwClient);
-        LOG.trace("connected client: {}", hwClient);
+        LOG.trace("connected client: {}", client);
     }
 
     @Override
@@ -106,6 +110,32 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
         LOG.trace("disconnected client: {}", client);
     }
 
+    public OvsdbClient connect(InstanceIdentifier<Node> iid, HwvtepGlobalAugmentation hwvtepGlobal) throws UnknownHostException {
+        InetAddress ip = HwvtepSouthboundMapper.createInetAddress(hwvtepGlobal.getConnectionInfo().getRemoteIp());
+        OvsdbClient client = OvsdbConnectionService.getService()
+                        .connect(ip, hwvtepGlobal.getConnectionInfo().getRemotePort().getValue());
+        if(client != null) {
+            putInstanceIdentifier(hwvtepGlobal.getConnectionInfo(), iid.firstIdentifierOf(Node.class));
+            HwvtepConnectionInstance hwvtepConnectionInstance = connectedButCallBacksNotRegistered(client);
+            hwvtepConnectionInstance.setHwvtepGlobalAugmentation(hwvtepGlobal);
+
+            // Register Cluster Ownership for ConnectionInfo
+            registerEntityForOwnership(hwvtepConnectionInstance);
+        } else {
+            LOG.warn("Failed to connect to OVSDB node: {}", hwvtepGlobal.getConnectionInfo());
+        }
+        return client;
+    }
+    public void disconnect(HwvtepGlobalAugmentation ovsdbNode) throws UnknownHostException {
+        HwvtepConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo());
+        if (client != null) {
+            client.disconnect();
+            // Unregister Cluster Ownership for ConnectionInfo
+            unregisterEntityForOwnership(client);
+            removeInstanceIdentifier(ovsdbNode.getConnectionInfo());
+        }
+    }
+
     public HwvtepConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) {
         LOG.info("OVSDB Connection from {}:{}",externalClient.getConnectionInfo().getRemoteAddress(),
                 externalClient.getConnectionInfo().getRemotePort());
@@ -180,6 +210,10 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
         instanceIdentifiers.remove(connectionInfo);
     }
 
+    public OvsdbClient getClient(ConnectionInfo connectionInfo) {
+        return getConnectionInstance(connectionInfo);
+    }
+
     private void registerEntityForOwnership(HwvtepConnectionInstance hwvtepConnectionInstance) {
 
         Entity candidateEntity = getEntityFromConnectionInstance(hwvtepConnectionInstance);
@@ -255,7 +289,7 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
 
     private Entity getEntityFromConnectionInstance(@Nonnull HwvtepConnectionInstance hwvtepConnectionInstance) {
         YangInstanceIdentifier entityId = null;
-        InstanceIdentifier<Node> iid = hwvtepConnectionInstance.getInstanceIdentifier();;
+        InstanceIdentifier<Node> iid = hwvtepConnectionInstance.getInstanceIdentifier();
         if ( iid == null ) {
             //TODO: Is Global the right one?
             Global hwvtepGlobalRow = getHwvtepGlobalTableEntry(hwvtepConnectionInstance);
@@ -296,7 +330,7 @@ public class HwvtepConnectionManager implements OvsdbConnectionListener, AutoClo
             // If entity has no owner, clean up the operational data store (it's possible because owner controller
             // might went down abruptly and didn't get a chance to clean up the operational data store.
             if (!ownershipChange.hasOwner()) {
-                LOG.debug("{} has no onwer, cleaning up the operational data store", ownershipChange.getEntity());
+                LOG.debug("{} has no owner, cleaning up the operational data store", ownershipChange.getEntity());
                 // Below code might look weird but it's required. We want to give first opportunity to the
                 // previous owner of the device to clean up the operational data store if there is no owner now.
                 // That way we will avoid lot of nasty md-sal exceptions because of concurrent delete.
diff --git a/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepDataChangeListener.java b/hwvtepsouthbound/hwvtepsouthbound-impl/src/main/java/org/opendaylight/ovsdb/hwvtepsouthbound/HwvtepDataChangeListener.java
new file mode 100644 (file)
index 0000000..f207895
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2015 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.ovsdb.hwvtepsouthbound;
+
+import java.net.UnknownHostException;
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.ovsdb.lib.OvsdbClient;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.HwvtepGlobalAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.hwvtep.rev150901.hwvtep.global.attributes.ConnectionInfo;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.TopologyKey;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class HwvtepDataChangeListener implements DataTreeChangeListener<Node>, AutoCloseable {
+
+    private ListenerRegistration<HwvtepDataChangeListener> registration;
+    private HwvtepConnectionManager hcm;
+    private DataBroker db;
+    private static final Logger LOG = LoggerFactory.getLogger(HwvtepDataChangeListener.class);
+
+    HwvtepDataChangeListener(DataBroker db, HwvtepConnectionManager hcm) {
+        LOG.info("Registering HwvtepDataChangeListener");
+        this.db = db;
+        this.hcm = hcm;
+        registerListener(db);
+    }
+
+    private void registerListener(final DataBroker db) {
+        final DataTreeIdentifier<Node> treeId =
+                        new DataTreeIdentifier<Node>(LogicalDatastoreType.CONFIGURATION, getWildcardPath());
+        try {
+            LOG.trace("Registering on path: {}", treeId);
+            registration = db.registerDataTreeChangeListener(treeId, HwvtepDataChangeListener.this);
+        } catch (final Exception e) {
+            LOG.warn("HwvtepDataChangeListener registration failed");
+            //TODO: Should we throw an exception here?
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        if(registration != null) {
+            registration.close();
+        }
+    }
+
+    @Override
+    public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
+        LOG.trace("onDataTreeChanged: {}", changes);
+
+        /* TODO:
+         * Currently only handling changes to Global.
+         * Rest will be added later.
+         */
+        connect(changes);
+        
+        updateConnections(changes);
+        
+        updateData(changes);
+        
+        disconnect(changes);
+        /*
+        for (DataTreeModification<Node> change : changes) {
+            final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<Node> mod = change.getRootNode();
+                switch (mod.getModificationType()) {
+                case DELETE:
+                    LOG.trace("Data deleted: {}", mod.getDataBefore());
+                    //disconnect(mod);
+                    break;
+                case SUBTREE_MODIFIED:
+                    LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
+                    updateConnections(mod);
+                    break;
+                case WRITE:
+                    if (mod.getDataBefore() == null) {
+                        LOG.trace("Data added: {}", mod.getDataAfter());
+                        connect(mod.getDataAfter());
+                    } else {
+                        LOG.trace("Data modified: {} to {}", mod.getDataBefore(),mod.getDataAfter());
+                        updateConnections(mod);
+                    }
+                    break;
+                default:
+                    throw new IllegalArgumentException("Unhandled modification type " + mod.getModificationType());
+                }
+        }
+        */
+    }
+
+    private void connect(Collection<DataTreeModification<Node>> changes) {
+        for (DataTreeModification<Node> change : changes) {
+            final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<Node> mod = change.getRootNode();
+            Node node = getCreated(mod);
+            if (node != null) {
+                HwvtepGlobalAugmentation hwvtepGlobal = node.getAugmentation(HwvtepGlobalAugmentation.class);
+                ConnectionInfo connection = hwvtepGlobal.getConnectionInfo();
+                InstanceIdentifier<Node> iid = hcm.getInstanceIdentifier(connection);
+                if (iid != null) {
+                    LOG.warn("Connection to device {} already exists. Plugin does not allow multiple connections "
+                                    + "to same device, hence dropping the request {}", connection, hwvtepGlobal);
+                } else {
+                    try {
+                        hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(node.getNodeId()), hwvtepGlobal);
+                    } catch (UnknownHostException e) {
+                        LOG.warn("Failed to connect to OVSDB node", e);
+                    }
+                }
+            }
+        }
+
+    }
+
+    private void updateConnections(Collection<DataTreeModification<Node>> changes) {
+        for (DataTreeModification<Node> change : changes) {
+            final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<Node> mod = change.getRootNode();
+            Node updated = getUpdated(mod);
+            if (updated != null) {
+                Node original = getOriginal(mod);
+                HwvtepGlobalAugmentation hgUpdated = updated.getAugmentation(HwvtepGlobalAugmentation.class);
+                HwvtepGlobalAugmentation hgOriginal = original.getAugmentation(HwvtepGlobalAugmentation.class);
+                OvsdbClient client = hcm.getClient(hgUpdated.getConnectionInfo());
+                if (client == null) {
+                    try {
+                        hcm.disconnect(hgOriginal);
+                        hcm.connect(HwvtepSouthboundMapper.createInstanceIdentifier(original.getNodeId()), hgUpdated);
+                    } catch (UnknownHostException e) {
+                        LOG.warn("Failed to update connection on OVSDB Node", e);
+                    }
+                }
+            }
+        }
+
+    }
+
+    private void updateData(Collection<DataTreeModification<Node>> changes) {
+        /* TODO: 
+         * Get connection instances for each change
+         * Update data for each connection
+         * Requires Command patterns. TBD.
+         */
+        
+    }
+
+    private void disconnect(Collection<DataTreeModification<Node>> changes) {
+        for (DataTreeModification<Node> change : changes) {
+            final InstanceIdentifier<Node> key = change.getRootPath().getRootIdentifier();
+            final DataObjectModification<Node> mod = change.getRootNode();
+            Node deleted = getRemoved(mod);
+            if (deleted != null) {
+                HwvtepGlobalAugmentation hgDeleted = deleted.getAugmentation(HwvtepGlobalAugmentation.class);
+                try {
+                    hcm.disconnect(hgDeleted);
+                } catch (UnknownHostException e) {
+                    LOG.warn("Failed to disconnect OVSDB Node", e);
+                }
+            }
+        }
+    }
+
+    private Node getCreated(DataObjectModification<Node> mod) {
+        if((mod.getModificationType() == ModificationType.WRITE) 
+                        && (mod.getDataBefore() == null)){
+            return mod.getDataAfter();
+        }
+        return null;
+    }
+
+    private Node getRemoved(DataObjectModification<Node> mod) {
+        if(mod.getModificationType() == ModificationType.DELETE){
+            return mod.getDataBefore();
+        }
+        return null;
+    }
+
+    private Node getUpdated(DataObjectModification<Node> mod) {
+        Node node = null;
+        switch(mod.getModificationType()) {
+            case SUBTREE_MODIFIED:
+                node = mod.getDataAfter();
+                break;
+            case WRITE:
+                if(mod.getDataBefore() !=  null) {
+                    node = mod.getDataAfter();
+                }
+                break;
+            default:
+                break;
+        }
+        return node;
+    }
+
+    private Node getOriginal(DataObjectModification<Node> mod) {
+        Node node = null;
+        switch(mod.getModificationType()) {
+            case SUBTREE_MODIFIED:
+                node = mod.getDataBefore();
+                break;
+            case WRITE:
+                if(mod.getDataBefore() !=  null) {
+                    node = mod.getDataBefore();
+                }
+                break;
+            case DELETE:
+                node = mod.getDataBefore();
+                break;
+            default:
+                break;
+        }
+        return node;
+    }
+
+    private InstanceIdentifier<Node> getWildcardPath() {
+        InstanceIdentifier<Node> path = InstanceIdentifier
+                        .create(NetworkTopology.class)
+                        .child(Topology.class, new TopologyKey(HwvtepSouthboundConstants.HWVTEP_TOPOLOGY_ID))
+                        .child(Node.class);
+        return path;
+    }
+}
index 104ee10438cd4dcf7385f0b60cba4c2136492b77..f73a0be23c60cfd66c0b2bab4d7f4fc23e2bc6ae 100644 (file)
@@ -28,7 +28,7 @@ public class HwvtepMonitorCallback implements MonitorCallBack {
 
     @Override
     public void update(TableUpdates result, DatabaseSchema dbSchema) {
-        LOG.debug("result: {} dbSchema: {}",result,dbSchema);
+        LOG.trace("result: {} dbSchema: {}",result,dbSchema.getName());
         txInvoker.invoke(new HwvtepOperationalCommandAggregator(key, result, dbSchema));
         LOG.trace("update exit");
     }
index 77cbcdb8a4a7be6aa96beb8dd54a3b6c5fd2bf8b..32be9a22d54146b51f12005e69589e0f9f0a292b 100644 (file)
@@ -11,6 +11,8 @@ package org.opendaylight.ovsdb.hwvtepsouthbound;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
+import java.net.UnknownHostException;
+
 import org.opendaylight.ovsdb.lib.OvsdbClient;
 import org.opendaylight.ovsdb.schema.hardwarevtep.Global;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
@@ -106,6 +108,16 @@ public class HwvtepSouthboundMapper {
         return connectionInfoBuilder.build();
     }
 
+    public static InetAddress createInetAddress(IpAddress ip) throws UnknownHostException {
+        if (ip.getIpv4Address() != null) {
+            return InetAddress.getByName(ip.getIpv4Address().getValue());
+        } else if (ip.getIpv6Address() != null) {
+            return InetAddress.getByName(ip.getIpv6Address().getValue());
+        } else {
+            throw new UnknownHostException("IP Address has no value");
+        }
+    }
+
     public static InstanceIdentifier<Node> getInstanceIdentifier(Global global) {
         InstanceIdentifier<Node> iid = null;
         if (global.getManagersColumn() != null
index 75f944637a7958fe7edc89cc9ec5f56526c72802..622ca0364d4fba724d73d68dad5e121a49f4611a 100644 (file)
@@ -16,6 +16,7 @@ import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipC
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
@@ -52,6 +53,7 @@ public class HwvtepSouthboundProvider implements BindingAwareProvider, AutoClose
     private EntityOwnershipService entityOwnershipService;
     private EntityOwnershipCandidateRegistration registration;
     private HwvtepsbPluginInstanceEntityOwnershipListener providerOwnershipChangeListener;
+    private HwvtepDataChangeListener hwvtepDTListener;
 
     public HwvtepSouthboundProvider(
             EntityOwnershipService entityOwnershipServiceDependency) {
@@ -65,7 +67,7 @@ public class HwvtepSouthboundProvider implements BindingAwareProvider, AutoClose
         db = session.getSALService(DataBroker.class);
         txInvoker = new TransactionInvokerImpl(db);
         cm = new HwvtepConnectionManager(db, txInvoker, entityOwnershipService);
-        //TODO: Add DataChange Listener
+        hwvtepDTListener = new HwvtepDataChangeListener(db, cm);
 
         //Register listener for entityOnwership changes
         providerOwnershipChangeListener =
@@ -75,7 +77,18 @@ public class HwvtepSouthboundProvider implements BindingAwareProvider, AutoClose
         //register instance entity to get the ownership of the provider
         Entity instanceEntity = new Entity(ENTITY_TYPE, ENTITY_TYPE);
         try {
+            Optional<EntityOwnershipState> ownershipStateOpt = entityOwnershipService.getOwnershipState(instanceEntity);
             registration = entityOwnershipService.registerCandidate(instanceEntity);
+            if (ownershipStateOpt.isPresent()) {
+                EntityOwnershipState ownershipState = ownershipStateOpt.get();
+                if (ownershipState.hasOwner() && !ownershipState.isOwner()) {
+                    if (ovsdbConnection == null) {
+                        ovsdbConnection = new OvsdbConnectionService();
+                        ovsdbConnection.registerConnectionListener(cm);
+                        ovsdbConnection.startOvsdbManager(HwvtepSouthboundConstants.DEFAULT_OVSDB_PORT);
+                    }
+                }
+            }
         } catch (CandidateAlreadyRegisteredException e) {
             LOG.warn("HWVTEP Southbound Provider instance entity {} was already "
                     + "registered for {} ownership", instanceEntity, e);
@@ -97,6 +110,10 @@ public class HwvtepSouthboundProvider implements BindingAwareProvider, AutoClose
             providerOwnershipChangeListener.close();
             providerOwnershipChangeListener = null;
         }
+        if(hwvtepDTListener != null) {
+            hwvtepDTListener.close();
+            hwvtepDTListener = null;
+        }
     }
 
     private void initializeHwvtepTopology(LogicalDatastoreType type) {
index 126db01610f634c0e3c93be3623879994811f292..e9ee40045016ddc607b1401ebda15c14553178c9 100644 (file)
@@ -108,6 +108,7 @@ public class ClassifierService extends AbstractServiceInstance implements Classi
                     BigInteger.valueOf(REG_VALUE_FROM_LOCAL)));
             ab.setOrder(1);
             ab.setKey(new ActionKey(1));
+
             actionList.add(ab.build());
 
             ib.setOrder(0);
index 3d1a813e155f39a79751643c8c000736e2441921..31d1e1d867e9b727f259cb1daab7ae3e9269056e 100644 (file)
@@ -14,6 +14,7 @@ module netvirt-sfc-acl {
   //augment "/ietf-acl:access-lists/ietf-acl:access-list/ietf-acl:access-list-entries/ietf-acl:access-list-entry/ietf-acl:matches" {
     augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:matches" {
         description "Neutron network uuid";
+        ext:augment-identifier "neutron-network";
         leaf network-uuid {
             type string;
         }
@@ -22,6 +23,7 @@ module netvirt-sfc-acl {
     // TODO: Add choice for Neutron and add fields there instead of at the root of matches
     augment "/ietf-acl:access-lists/ietf-acl:acl/ietf-acl:access-list-entries/ietf-acl:ace/ietf-acl:actions" {
         description "Redirect traffic to SFC identified by SFC Path ID";
+        ext:augment-identifier "redirect-to-sfc";
         leaf redirect-sfc {
             type string;
         }
index c0aec3a3698c1e7c8b6d242b21ac235c47f18ac5..df4dd39e8d6351807116f2baf8a56bd54702960b 100644 (file)
@@ -32,6 +32,14 @@ module netvirt-sfc-classifier {
                     }
                 }
             }
+            container bridges {
+                list bridge {
+                    key "name";
+                    leaf name {
+                        type string;
+                    }
+                }
+            }
         }
     }
 }
index 80f9a173c98dee73663f28263858bb078b49d53e..690626c9b060fb9350f9e07a8474b08e0f79b320 100644 (file)
@@ -46,6 +46,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <groupId>${project.groupId}</groupId>
       <artifactId>openstack.net-virt-providers</artifactId>
       <version>${project.version}</version>
+    </dependency>
+       <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>southbound-api</artifactId>
+      <version>${project.version}</version>
     </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
@@ -158,9 +163,9 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-checkstyle-plugin</artifactId>
-        <configuration>
+        <!--<configuration>
           <propertyExpansion>checkstyle.violationSeverity=error</propertyExpansion>
-        </configuration>
+        </configuration>-->
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
index f263c0f5dfd45a559755163592c0297b2f9bab40..b3e50bd6d68131572ed0d55af13e7bbed07e3403 100644 (file)
@@ -105,9 +105,10 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener<Acl> {
                     final Acl addDataObj) {
         Preconditions.checkNotNull(addDataObj, "Added object can not be null!");
         String aclName = addDataObj.getAclName();
-        LOG.debug("Adding accesslist = {}", identifier);
+        LOG.debug("Adding accesslist iid = {}, dataObj = {}", identifier, addDataObj);
         Classifiers classifiers = dbutils.read(LogicalDatastoreType.CONFIGURATION, getClassifierIid());
         if (classifiers != null) {
+            LOG.debug("add: Classifiers: {}", classifiers);
             for (Classifier classifier : classifiers.getClassifier()) {
                 if (classifier.getAcl().equalsIgnoreCase(aclName)) {
                     if (classifier.getSffs() != null) {
@@ -117,6 +118,8 @@ public class NetvirtSfcAclListener extends AbstractDataTreeListener<Acl> {
                     }
                 }
             }
+        } else {
+            LOG.debug("add: No Classifiers found");
         }
     }
 
index af8c8968f335bd3f5fbc73dce5413efb0c5ad2f5..b3d770f1c636d480ffbb8555b007c745a233757e 100644 (file)
@@ -19,6 +19,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.AclKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.Classifiers;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.Classifier;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
 import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
@@ -107,6 +108,7 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener<Class
                     final Classifier addDataObj) {
         Preconditions.checkNotNull(addDataObj, "Added object can not be null!");
         String aclName = addDataObj.getAcl();
+        LOG.debug("Adding classifier iid = {}, dataObj = {}", identifier, addDataObj);
         // Read the ACL information from data store and make sure it exists.
         Acl acl = dbutils.read(LogicalDatastoreType.CONFIGURATION,getIetfAclIid(aclName));
         if (acl == null) {
@@ -114,12 +116,12 @@ public class NetvirtSfcClassifierListener extends AbstractDataTreeListener<Class
             return;
         }
 
-        if (addDataObj.getSffs() != null) {
-            for (Sff sff : addDataObj.getSffs().getSff()) {
+        if (addDataObj.getBridges() != null) {
+            for (Bridge bridge : addDataObj.getBridges().getBridge()) {
                 // Netvirt classifier binds an ACL with service function forwarder that is identified by SFF name.
                 // SFF validation can be done with SFC Provider APIs, as SFF is configured within SFC project.  
                 // Netvirt SFC provider will validate the SFF using SFC provider APIs.
-                provider.addClassifierRules(sff, acl);
+                provider.addClassifierRules(bridge, acl);
             }
         }
     }
index 43ff43f02175361e8c952bbcd11ff90302bc86d6..270a1d0689872152e67594814afacb466cc1d1af 100644 (file)
@@ -9,6 +9,7 @@
 package org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13;
 
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
 
 /**
@@ -25,6 +26,7 @@ public interface INetvirtSfcOF13Provider {
      * @param acl - Access list includes rules that need to be installed in a SFF.
      */
     public void addClassifierRules(Sff sff, Acl acl);
+    public void addClassifierRules(Bridge bridge, Acl acl);
 
     /**
      * Method removes the OF rules corresponding to rules within ACL
index 0fabf9e6899dd21a93e23e1bb3d9a212b7acde6b..acebecbdbc420b3378f282cdebc0a10153f52fe4 100644 (file)
@@ -8,37 +8,59 @@
 
 package org.opendaylight.ovsdb.openstack.netvirt.sfc.openflow13;
 
+import com.google.common.util.concurrent.CheckedFuture;
+import java.math.BigInteger;
 import java.util.Iterator;
 import java.util.List;
+import java.util.StringTokenizer;
+
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg0;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
+import com.google.common.net.InetAddresses;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
 import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
 import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
-import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.PipelineOrchestrator;
 import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.Service;
+import org.opendaylight.ovsdb.utils.mdsal.openflow.ActionUtils;
 import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils;
 import org.opendaylight.ovsdb.utils.mdsal.openflow.MatchUtils;
 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.sfc.provider.api.SfcProviderRenderedPathAPI;
 import org.opendaylight.sfc.provider.api.SfcProviderServiceForwarderAPI;
+import org.opendaylight.sfc.provider.api.SfcProviderServicePathAPI;
 import org.opendaylight.sfc.sfc_ovs.provider.SfcOvsUtil;
+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.SffName;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.CreateRenderedPathInput;
+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.path.first.hop.info.RenderedServicePathFirstHop;
+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;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.rsp.rev140701.rendered.service.paths.rendered.service.path.RenderedServicePathHop;
 import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sff.rev140701.service.function.forwarders.ServiceFunctionForwarder;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.ServiceFunctionPaths;
+import org.opendaylight.yang.gen.v1.urn.cisco.params.xml.ns.yang.sfc.sfp.rev140701.service.function.paths.ServiceFunctionPath;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.Acl;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.Ace;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Actions;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.Matches;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.actions.packet.handling.Deny;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev150317.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
@@ -48,6 +70,9 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.ta
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
@@ -55,13 +80,19 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.acl.rev150105.RedirectToSfc;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.bridges.Bridge;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.netvirt.sfc.classifier.rev150105.classifiers.classifier.sffs.Sff;
+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.OvsdbTerminationPointAugmentation;
 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.osgi.framework.ServiceReference;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+
 /**
  * Open vSwitch OpenFlow 1.3 Networking Provider for Netvirt SFC
  * @author Arun Yerra
@@ -69,10 +100,26 @@ import org.slf4j.LoggerFactory;
 public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
     private static final Logger LOG = LoggerFactory.getLogger(NetvirtSfcOF13Provider.class);
     private static final int DEFAULT_FLOW_PRIORITY = 32768;
+    private static final short TABLE_0_CLASSIFIER = 0;
+    //private static final short TABLE_1_L2FORWARD = 30;
+    //private static final short TABLE_2_L3FORWARD = 40;
+    private static final short TABLE_3_INGR_ACL = 50;
+    private static final String OPENFLOW = "openflow:";
+
+    public static final long REG_VALUE_FROM_LOCAL = 0x1L;
+    public static final long REG_VALUE_FROM_REMOTE = 0x2L;
+    public static final Class<? extends NxmNxReg> REG_FIELD = NxmNxReg0.class;
     private volatile NodeCacheManager nodeCacheManager;
     private volatile Southbound southbound;
-    private MdsalUtils dbutils;
-    private PipelineOrchestrator orchestrator;
+    private MdsalUtils mdsalUtils;
+    private DataBroker dataBroker;
+
+    // TBD:: Remove these constants after integrating with openstack.
+    //private static final String NETWORK_TYPE_VXLAN = "vxlan";
+    private static final String NETWORK_SEGMENT_ID = "10";
+    private static final String LOCAL_TP_ID = "vethl-h35_2";
+    private static final String INTERFACE_TYPE_VXLAN_GPE = "vxlangpe";
+    private static final String GPE_IFACE_ID = "sw1-vxlangpe-0";
 
     /**
      * {@link NetvirtSfcOF13Provider} constructor.
@@ -80,10 +127,8 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
      */
     public NetvirtSfcOF13Provider(final DataBroker dataBroker) {
         Preconditions.checkNotNull(dataBroker, "Input dataBroker cannot be NULL!");
-
-        //this.dataService = dataBroker;
-        dbutils = new MdsalUtils(dataBroker);
-
+        this.dataBroker = dataBroker;
+        mdsalUtils = new MdsalUtils(dataBroker);
         this.setDependencies(null);
     }
 
@@ -102,62 +147,248 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
 
         // If a service function forwarder exists, then get the corresponding OVS Bridge details and Openflow NodeId.
         // If OVS Bridge augmentation is configured, the following API returns NULL.
-        String datapathId = SfcOvsUtil.getOpenFlowNodeIdForSff(serviceForwarder);
+        String datapathId = null;
+        Node node = null;
+        // TODO: Replace with OVSDB methods and not use the SFC API
+        datapathId = SfcOvsUtil.getOpenFlowNodeIdForSff(serviceForwarder);
         if (datapathId == null) {
-            LOG.debug("Service Function Forwarder = {} is not augemented with "
+            LOG.debug("Service Function Forwarder = {} is not augmented with "
                     + "OVS Bridge Information. Skip processing!!", sff.getName());
         }
         // If openflow Node Id is NULL, get all the bridge nodes using southbound apis and fetch
         // SFF with matching name. From this bridge name, get the openflow data path ID.
-        if (datapathId == null) {
-            Node node = null;
-            final List<Node> nodes = nodeCacheManager.getBridgeNodes();
-            if (nodes.isEmpty()) {
-                LOG.debug("Noop with Classifier Creation on SFF={}. No Bridges configured YET!!", sff.getName());
-            } else {
-                for (Node dstNode : nodes) {
-                    LOG.debug("Processing Node={}, sff={}", dstNode.getNodeId().getValue(), sff.getName());
-                    if (dstNode.getNodeId().getValue().equalsIgnoreCase(sff.getName())) {
-                        LOG.debug("Found matching OVSDB Bridge Name!!= {}", dstNode.getNodeId().getValue());
-                        node = dstNode;
-                        break;
-                    }
-                }
-            }
+
+        node = getBridgeNode(serviceForwarder.getName().toString());
+        if (node == null) {
+            LOG.warn("Node doesn't exist for corresponding SFF={}", sff.getName());
+            return;
         }
 
+        datapathId = getDpid(node);
         LOG.debug("Processing the Classifier rules on Node={}", datapathId);
         if (datapathId != null) {
             // Program the OF flow on the corresponding open flow node.
             Iterator<Ace> itr = acl.getAccessListEntries().getAce().iterator();
             while (itr.hasNext()) {
                 Ace entry = itr.next();
-                programOfRules(entry, datapathId, true);
+                processAclEntry(entry, node, true);
             }
+        } else {
+            LOG.warn("Skipping ACL processing on Node={} as DatapathID is NULL!!", sff.getName());
         }
     }
 
-    private void programOfRules(Ace entry, String datapathId, boolean write) {
-        NodeBuilder nodeBuilder = new NodeBuilder();
-        nodeBuilder.setId(new NodeId(Constants.OPENFLOW_NODE_PREFIX + datapathId));
-        nodeBuilder.setKey(new NodeKey(nodeBuilder.getId()));
+    @Override
+    public void addClassifierRules(Bridge bridge, Acl acl) {
+        Preconditions.checkNotNull(bridge, "Input bridge cannot be NULL!");
+        Preconditions.checkNotNull(acl, "Input accesslist cannot be NULL!");
 
-        //Create the match using match builder, by parsing the Accesslist Entry Match.
-        MatchBuilder matchBuilder = null;
-        matchBuilder = buildMatch(entry.getRuleName(), entry.getMatches(), datapathId);
+        Node bridgeNode = getBridgeNode(bridge.getName());
+        if (bridgeNode == null) {
+            LOG.debug("bridge {} not yet configured. Skip processing !!", bridge.getName());
+            return;
+        }
 
-        InstructionsBuilder isb = null;
-        isb = buildActions(entry.getRuleName(), entry.getActions(), datapathId);
+        // Program the OF flow on the corresponding open flow node.
+        for (Ace ace : acl.getAccessListEntries().getAce()) {
+            processAclEntry(ace, bridgeNode, true);
+        }
+    }
 
-        String flowId = "NETVIRT_SFC_FLOW" + "_" + entry.getRuleName();
+    private void processAclEntry(Ace entry, Node srcNode, boolean write) {
+        RedirectToSfc sfcRedirect = entry.getActions().getAugmentation(RedirectToSfc.class);
+        LOG.debug("Processing ACL entry = {} on Node = {} sfcRedirect = {}", entry.getRuleName(),
+                srcNode.getNodeId(), sfcRedirect);
+        if (sfcRedirect != null) {
+            // Given SFP find the corresponding RSP.
+            String sfcName = sfcRedirect.getRedirectSfc();
+            LOG.debug("Processing Redirect to SFC = {}", sfcRedirect.getRedirectSfc());
+            ServiceFunctionPath sfp = getSfp(sfcName);
+            if (sfp == null || sfp.getName() == null) {
+                LOG.warn("There is no configured SFP with sfcName = {}; so skip installing the ACL entry!!", sfcName);
+                return;
+            }
 
+            LOG.debug("Processing Redirect to SFC = {}, SFP = {}", sfcRedirect.getRedirectSfc(), sfp);
+            // If RSP doesn't exist, create an RSP.
+            String sfpName = sfp.getName().getValue();
+            RenderedServicePath rsp = getRspforSfp(sfpName);
+            String rspName = sfp.getName().getValue() + "_rsp";
+            if (rsp == null) {
+                LOG.info("No configured RSP corresponding to SFP = {}, Creating new RSP = {}!!", sfpName, rspName);
+                // Create RSP.
+                CreateRenderedPathInput rspInput = new CreateRenderedPathInputBuilder()
+                        .setParentServiceFunctionPath(sfpName)
+                        .setName(rspName)
+                        .setSymmetric(sfp.isSymmetric())
+                        .build();
+                rsp = SfcProviderRenderedPathAPI.createRenderedServicePathAndState(sfp, rspInput);
+                if (rsp == null) {
+                    LOG.warn("failed to add RSP");
+                    return;
+                }
+
+                // 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);
+                    String rspNameRev = rspName + "-Reverse";
+                    RenderedServicePath rspReverse = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL,
+                                                                 this.getRspId(rspNameRev));
+                    if (rspReverse == null) {
+                        rspReverse = SfcProviderRenderedPathAPI.createSymmetricRenderedServicePathAndState(rsp);
+                    }
+                }
+            }
+            LOG.info("rsp: {}", rsp);
+
+            // Find the first Hop within a RSP.
+            List<RenderedServicePathHop> pathHopList = rsp.getRenderedServicePathHop();
+            if (pathHopList.isEmpty()) {
+                LOG.info("Service Path = {} has empty hops!!", sfpName);
+                return;
+            }
+
+            RenderedServicePathFirstHop firstRspHop = SfcProviderRenderedPathAPI
+                    .readRenderedServicePathFirstHop(new RspName(rspName));
+            //String firstSff = firstHop.getServiceFunctionForwarderLocator();
+
+            LOG.debug("First Hop IPAddress = {}, Port = {}", firstRspHop.getIp().getIpv4Address().getValue(),
+                    firstRspHop.getPort().getValue().intValue());
+            OvsdbTerminationPointAugmentation tunnelPort = southbound
+                    .getTerminationPointOfBridge(srcNode, GPE_IFACE_ID);
+            if (tunnelPort != null) {
+                long tunnelOfPort = southbound.getOFPort(tunnelPort);
+                LOG.debug("Tunnel Port = {}, OF Port Number = {}", tunnelPort.getName(), tunnelOfPort);
+                if (tunnelOfPort == 0) {
+                    LOG.error("programTunnelRules: Could not Identify Tunnel port {} -> OF ({}) on {}",
+                            tunnelPort.getName(), tunnelOfPort, srcNode);
+                    return;
+                }
+            }
+
+            Matches match = entry.getMatches();
+
+            NshUtils header = new NshUtils();
+            // C1 is the normal overlay dest ip
+            header.setNshMetaC1(convertToLongIp(getDestIp(match)));
+            header.setNshMetaC2(Long.parseLong(NETWORK_SEGMENT_ID));
+            header.setNshNsp(rsp.getPathId());
+
+            RenderedServicePathHop firstHop = pathHopList.get(0);
+            header.setNshNsi(firstHop.getServiceIndex());
+            header.setNshTunIpDst(firstRspHop.getIp().getIpv4Address());
+            header.setNshTunUdpPort(firstRspHop.getPort());
+
+            LOG.debug("The Nsh Header = {}", header);
+            OvsdbTerminationPointAugmentation localPort = getTerminationPoint(srcNode, LOCAL_TP_ID);
+
+           /* String attachedMac = southbound.getInterfaceExternalIdsValue(localPort, Constants.EXTERNAL_ID_VM_MAC);
+            if (attachedMac == null) {
+                LOG.warn("No AttachedMac seen in {}", localPort);
+                // return;
+            }
+            */
+            LOG.debug("LocalPort ID={}, IP Address = {}", localPort.getName(), getSourceIp(match));
+            handleLocalInPort(southbound.getDataPathId(srcNode), TABLE_0_CLASSIFIER, TABLE_3_INGR_ACL,
+                    NETWORK_SEGMENT_ID, localPort.getOfport(), getSourceIp(match), true);
+
+            // L2 Dst MAC forwarding flows.
+            // L2 Flood Flows.
+            // Set the Tunnel Destination IP, Dest MAC based on the destination IP address.
+            // Replace SMAC & decrement TTL.
+            // handleL3Flows(southbound.getDataPathId(srcNode), TABLE_1_ISOLATE_TENANT,
+            // TABLE_2_INGRESS_ACL, destIp, l3SegmentId, destMac);
+            // Set NSP & NSI values based on the Classifier Match actions.
+            OvsdbTerminationPointAugmentation outPort = getTerminationPoint(srcNode, INTERFACE_TYPE_VXLAN_GPE);
+            // TODO: commented out for test
+            //handleSfcClassiferFlows(entry.getRuleName(), srcNode, match,
+            //              TABLE_3_INGR_ACL, header, outPort.getOfport(), true);
+            // Set NSHC1, NSHC2, Tunnel DestIP, port toward SFF1, output to Tunnel Port.
+
+        }
+    }
+
+    private OvsdbTerminationPointAugmentation getTerminationPoint(Node srcNode, String localTpId) {
+        List<OvsdbTerminationPointAugmentation> ports = southbound.extractTerminationPointAugmentations(srcNode);
+        if (ports != null && !ports.isEmpty()) {
+            for (OvsdbTerminationPointAugmentation port : ports) {
+                // TBD :: For Demo, use the Tunnel ID as 10. Once openstack is integrated,
+                // tunnel ID is created through Network Creation.
+                if (port.getName().contains(localTpId)) {
+                    return port;
+                }
+            }
+        }
+        return null;
+    }
+
+    private String getTunnelName(String networkTypeVxlan, Ipv4Address ipv4Address) {
+        return networkTypeVxlan + "-" + ipv4Address.getValue();
+    }
+
+    /*
+     * (TABLE:50) EGRESS VM TRAFFIC TOWARDS TEP with NSH header
+     * MATCH: Match fields passed through ACL entry
+     * INSTRUCTION: SET TUNNELID AND GOTO TABLE TUNNEL TABLE (N)
+     * TABLE=0,IN_PORT=2,DL_SRC=00:00:00:00:00:01 \
+     * ACTIONS=SET_FIELD:5->TUN_ID,GOTO_TABLE=1"
+     */
+    private void handleSfcClassiferFlows(String ruleName, Node node, Matches match,
+                 short table3IngrAcl, NshUtils header, long ofPort, boolean write) {
+        // Build the Actions to Add the NSH Header
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action nshC1Load =
+                        NshUtils.nxLoadNshc1RegAction(header.getNshMetaC1());
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action nshC2Load =
+                        NshUtils.nxLoadNshc2RegAction(header.getNshMetaC2());
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action nspLoad =
+                        NshUtils.nxSetNspAction(header.getNshNsp());
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action nsiLoad =
+                        NshUtils.nxSetNsiAction(header.getNshNsi());
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunVnid =
+                        NshUtils.nxLoadTunIdAction(BigInteger.valueOf(header.getNshMetaC2()), false);
+        org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action loadChainTunDest =
+                        NshUtils.nxLoadTunIPv4Action(header.getNshTunIpDst().getValue(), false);
+
+        int count = 0;
+        List<Action> actionList = Lists.newArrayList();
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(nshC1Load).build());
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(nshC2Load).build());
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(nspLoad).build());
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(nsiLoad).build());
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(loadChainTunDest).build());
+        actionList.add(new ActionBuilder().setOrder(Integer.valueOf(count++)).setAction(loadChainTunVnid).build());
+
+        ApplyActionsBuilder aab = new ApplyActionsBuilder();
+        aab.setAction(actionList);
+
+        InstructionBuilder ib = new InstructionBuilder();
+        ib.setInstruction(new ApplyActionsCaseBuilder().setApplyActions(aab.build()).build());
+        ib.setOrder(0);
+        ib.setKey(new InstructionKey(0));
+
+        List<Instruction> instructions = Lists.newArrayList();
+        instructions.add(ib.build());
+
+     // Set the Output Port/Iface
+        InstructionUtils.createOutputPortInstructions(ib, southbound.getDataPathId(node), ofPort);
+        ib.setOrder(1);
+        ib.setKey(new InstructionKey(1));
+        instructions.add(ib.build());
+
+     // Add InstructionBuilder to the Instruction(s)Builder List
+        InstructionsBuilder isb = new InstructionsBuilder();
+        isb.setInstruction(instructions);
+
+        String flowId = "NETVIRT_SFC_FLOW" + "_" + ruleName + "_" + header.getNshNsp();
         FlowBuilder flowBuilder = new FlowBuilder();
         flowBuilder.setId(new FlowId(flowId));
         FlowKey key = new FlowKey(new FlowId(flowId));
-        flowBuilder.setMatch(matchBuilder.build());
+
+        MatchBuilder mb = buildMatch(match);
+        flowBuilder.setMatch(mb.build());
         flowBuilder.setPriority(DEFAULT_FLOW_PRIORITY);
         flowBuilder.setBarrier(true);
-        flowBuilder.setTableId(this.getTable());
+        flowBuilder.setTableId(table3IngrAcl);
         flowBuilder.setKey(key);
         flowBuilder.setFlowName(flowId);
         flowBuilder.setHardTimeout(0);
@@ -165,6 +396,175 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
 
         flowBuilder.setInstructions(isb.build());
 
+        if (write) {
+            writeFlow(flowBuilder, createNodeBuilder(node.getNodeId().getValue()));
+        } else {
+            removeFlow(flowBuilder, createNodeBuilder(node.getNodeId().getValue()));
+        }
+    }
+
+    /*
+     * (TABLE:0) EGRESS VM TRAFFIC TOWARDS TEP
+     * MATCH: DESTINATION ETHERNET ADDR AND OPENFLOW INPORT
+     * INSTRUCTION: SET TUNNELID AND GOTO TABLE TUNNEL TABLE (N)
+     * TABLE=0,IN_PORT=2,DL_SRC=00:00:00:00:00:01 \
+     * ACTIONS=SET_FIELD:5->TUN_ID,GOTO_TABLE=1"
+     */
+    private void handleLocalInPort(long dpidLong, short writeTable, short goToTableId,
+            String segmentationId, Long inPort, String sourceIp,
+            boolean write) {
+        programDropSrcIface(dpidLong, inPort, true);
+        programLocalInPort(dpidLong, segmentationId, inPort, sourceIp, goToTableId, write);
+    }
+
+    private String getDestIp(Matches match) {
+        if (match.getAceType() instanceof AceIp) {
+            AceIp aceIp = (AceIp)match.getAceType();
+            if (aceIp.getAceIpVersion() instanceof AceIpv4) {
+                AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
+                if (aceIpv4.getDestinationIpv4Network() != null) {
+                    String ipAddrPrefix = aceIpv4.getDestinationIpv4Network().getValue();
+                    String ipAddr = new StringTokenizer(ipAddrPrefix, "/").nextToken();
+                    return ipAddr;
+                }
+            }
+        }
+        return null;
+    }
+
+    private String getSourceIp(Matches match) {
+        if (match.getAceType() instanceof AceIp) {
+            AceIp aceIp = (AceIp)match.getAceType();
+            if (aceIp.getAceIpVersion() instanceof AceIpv4) {
+                AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
+                if (aceIpv4.getSourceIpv4Network() != null) {
+                    String ipAddrPrefix = aceIpv4.getSourceIpv4Network().getValue();
+                    //String ipAddr = new StringTokenizer(ipAddrPrefix, "/").nextToken();
+                    return ipAddrPrefix;
+                }
+            }
+        }
+        return null;
+    }
+
+    private long convertToLongIp(String ipaddr) {
+        LOG.debug("Converting String={} to Long", ipaddr);
+        return InetAddresses.coerceToInteger(InetAddresses.forString(ipaddr)) & 0xFFFFFFFFL;
+    }
+
+    private void programLocalInPort(Long dpidLong, String segmentationId, Long inPort,
+                                    String ipAddr, short goToTableId, boolean write) {
+        String nodeName = OPENFLOW + dpidLong;
+
+        MatchBuilder matchBuilder = new MatchBuilder();
+        NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
+        FlowBuilder flowBuilder = new FlowBuilder();
+
+        // Create the OF Match using MatchBuilder
+        //flowBuilder.setMatch(MatchUtils.createEthSrcMatch(matchBuilder, new MacAddress(attachedMac)).build());
+        // TODO Broken In_Port Match
+        flowBuilder.setMatch(MatchUtils.createInPortMatch(matchBuilder, dpidLong, inPort).build());
+        //flowBuilder.setMatch(MatchUtils.createSrcL3IPv4Match(matchBuilder, new Ipv4Prefix(ipAddr)).build());
+        String flowId = "LocalSfc_" + segmentationId + "_" + inPort;// + "_" + ipAddr.split("/",2)[0];
+        // Add Flow Attributes
+        flowBuilder.setId(new FlowId(flowId));
+        FlowKey key = new FlowKey(new FlowId(flowId));
+        flowBuilder.setStrict(true);
+        flowBuilder.setBarrier(false);
+        flowBuilder.setTableId((short)0);//getTable());
+        flowBuilder.setKey(key);
+        flowBuilder.setFlowName(flowId);
+        flowBuilder.setPriority(8192);
+        flowBuilder.setHardTimeout(0);
+        flowBuilder.setIdleTimeout(0);
+
+        if (write) {
+            // Instantiate the Builders for the OF Actions and Instructions
+            InstructionBuilder ib = new InstructionBuilder();
+            InstructionsBuilder isb = new InstructionsBuilder();
+
+            // Instructions List Stores Individual Instructions
+            List<Instruction> instructions = Lists.newArrayList();
+
+            // TODO Broken SetTunID
+            //InstructionUtils.createSetTunnelIdInstructions(ib, new BigInteger(segmentationId));
+            //ApplyActionsCase aac = (ApplyActionsCase) ib.getInstruction();
+            //List<Action> actionList = aac.getApplyActions().getAction();
+
+            //ActionBuilder ab = new ActionBuilder();
+            //ab.setAction(ActionUtils.nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(REG_FIELD).build(),
+            //        BigInteger.valueOf(REG_VALUE_FROM_LOCAL)));
+            //ab.setOrder(1);
+            //ab.setKey(new ActionKey(1));
+
+            //actionList.add(ab.build());
+
+            //ib.setOrder(0);
+            //ib.setKey(new InstructionKey(0));
+            //instructions.add(ib.build());
+
+            // Next service GOTO Instructions Need to be appended to the List
+            ib = InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), goToTableId);
+            ib.setOrder(0);//1);
+            ib.setKey(new InstructionKey(0));//1));
+            instructions.add(ib.build());
+
+            // Add InstructionBuilder to the Instruction(s)Builder List
+            isb.setInstruction(instructions);
+
+            // Add InstructionsBuilder to FlowBuilder
+            flowBuilder.setInstructions(isb.build());
+
+            writeFlow(flowBuilder, nodeBuilder);
+        } else {
+            removeFlow(flowBuilder, nodeBuilder);
+        }
+    }
+
+    public void programDropSrcIface(Long dpidLong, Long inPort, boolean write) {
+
+        String nodeName = OPENFLOW + dpidLong;
+
+        MatchBuilder matchBuilder = new MatchBuilder();
+        NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
+        FlowBuilder flowBuilder = new FlowBuilder();
+
+        // Create the OF Match using MatchBuilder
+        flowBuilder.setMatch(MatchUtils.createInPortMatch(matchBuilder, dpidLong, inPort).build());
+
+        if (write) {
+            // Instantiate the Builders for the OF Actions and Instructions
+            InstructionBuilder ib = new InstructionBuilder();
+            InstructionsBuilder isb = new InstructionsBuilder();
+
+            // Instructions List Stores Individual Instructions
+            List<Instruction> instructions = Lists.newArrayList();
+
+            // Call the InstructionBuilder Methods Containing Actions
+            InstructionUtils.createDropInstructions(ib);
+            ib.setOrder(0);
+            ib.setKey(new InstructionKey(0));
+            instructions.add(ib.build());
+
+            // Add InstructionBuilder to the Instruction(s)Builder List
+            isb.setInstruction(instructions);
+
+            // Add InstructionsBuilder to FlowBuilder
+            flowBuilder.setInstructions(isb.build());
+        }
+
+        String flowId = "DropFilter_"+inPort;
+        // Add Flow Attributes
+        flowBuilder.setId(new FlowId(flowId));
+        FlowKey key = new FlowKey(new FlowId(flowId));
+        flowBuilder.setStrict(true);
+        flowBuilder.setBarrier(false);
+        flowBuilder.setTableId((short)50);//getTable());
+        flowBuilder.setKey(key);
+        flowBuilder.setFlowName(flowId);
+        flowBuilder.setPriority(8192);
+        flowBuilder.setHardTimeout(0);
+        flowBuilder.setIdleTimeout(0);
         if (write) {
             writeFlow(flowBuilder, nodeBuilder);
         } else {
@@ -172,6 +572,28 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
         }
     }
 
+    private Node getBridgeNode(String bridgeName) {
+        //SffOvsBridgeAugmentation sffBridge = sff.getAugmentation(SffOvsBridgeAugmentation.class);
+        //String brName = sffBridge.getOvsBridge().getBridgeName();
+
+        final List<Node> nodes = nodeCacheManager.getBridgeNodes();
+        if (nodes.isEmpty()) {
+            LOG.debug("Noop with Classifier Creation on SFF={}. No Bridges configured YET!!", bridgeName);
+        } else {
+            for (Node node : nodes) {
+                OvsdbBridgeAugmentation bridgeAugmentation = southbound.extractBridgeAugmentation(node);
+                if (bridgeAugmentation != null) {
+                    if (bridgeAugmentation.getBridgeName().getValue().equalsIgnoreCase(bridgeName)) {
+                        LOG.info("Found matching OVSDB Bridge Name {}", node.getNodeId().getValue());
+                        return node;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+/*
     private InstructionsBuilder buildActions(String ruleName, Actions actions, String datapathId) {
         InstructionBuilder ib = new InstructionBuilder();
 
@@ -199,9 +621,37 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
         InstructionsBuilder isb = new InstructionsBuilder();
         isb.setInstruction(instructions);
         return isb;
+    }*/
+
+    private ServiceFunctionPath getSfp(String redirectSfc) {
+        ServiceFunctionPaths sfps = SfcProviderServicePathAPI.readAllServiceFunctionPaths();
+        if (sfps != null) {
+            for (ServiceFunctionPath sfp: sfps.getServiceFunctionPath()) {
+                if (sfp.getServiceChainName().getValue().equalsIgnoreCase(redirectSfc)) {
+                    return sfp;
+                }
+            }
+        }
+        return null;
+    }
+
+    private RenderedServicePath getRspforSfp(String sfpName) {
+        RenderedServicePaths rsps = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, this.getRspsId());
+        if (rsps == null) {
+            LOG.debug("RSP has not been configured yet for SFP = {}", sfpName);
+            return null;
+        }
+        for (RenderedServicePath rsp : rsps.getRenderedServicePath()) {
+            if (rsp.getParentServiceFunctionPath() !=  null) {
+                if (rsp.getParentServiceFunctionPath().getValue().equalsIgnoreCase(sfpName)) {
+                    return rsp;
+                }
+            }
+        }
+        return null;
     }
 
-    private MatchBuilder buildMatch(String ruleName, Matches matches, String dpId) {
+    private MatchBuilder buildMatch(Matches matches) {
         MatchBuilder matchBuilder = new MatchBuilder();
 
         if (matches.getAceType() instanceof AceIp) {
@@ -209,10 +659,10 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
             if (aceIp.getAceIpVersion() instanceof AceIpv4) {
                 AceIpv4 aceIpv4 = (AceIpv4) aceIp.getAceIpVersion();
                 MatchUtils.createSrcL3IPv4Match(matchBuilder, aceIpv4.getSourceIpv4Network());
-                MatchUtils.createDstL3IPv4Match(matchBuilder, aceIpv4.getDestinationIpv4Network());
-                MatchUtils.createIpProtocolMatch(matchBuilder, aceIp.getProtocol());
+                //MatchUtils.createDstL3IPv4Match(matchBuilder, aceIpv4.getDestinationIpv4Network());
+                //MatchUtils.createIpProtocolMatch(matchBuilder, aceIp.getProtocol());
                 MatchUtils.addLayer4Match(matchBuilder, aceIp.getProtocol().intValue(),
-                        aceIp.getSourcePortRange().getLowerPort().getValue().intValue(),
+                        0,
                         aceIp.getDestinationPortRange().getLowerPort().getValue().intValue());
             }
         } else if (matches.getAceType() instanceof AceEth) {
@@ -229,19 +679,37 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
     @Override
     public void removeClassifierRules(Sff sff, Acl acl) {
         // TODO Auto-generated method stub
-
     }
 
-
+    /*
     protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
         LOG.debug("writeFlow: flowBuilder: {}, nodeBuilder: {}",
                 flowBuilder.build(), nodeBuilder.build());
-        dbutils.merge(LogicalDatastoreType.CONFIGURATION, createNodePath(nodeBuilder), nodeBuilder.build());
-        dbutils.put(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder), flowBuilder.build());
+        mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, createNodePath(nodeBuilder), nodeBuilder.build());
+        mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder), flowBuilder.build());
+    }
+*/
+    protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
+        LOG.debug("writeFlow: flowBuilder: {}, nodeBuilder: {}",
+                flowBuilder.build(), nodeBuilder.build());
+        WriteTransaction modification = dataBroker.newWriteOnlyTransaction();
+        modification.put(LogicalDatastoreType.CONFIGURATION, createNodePath(nodeBuilder),
+                nodeBuilder.build(), true /*createMissingParents*/);
+        modification.put(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder),
+                flowBuilder.build(), true /*createMissingParents*/);
+
+        CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
+        try {
+            commitFuture.get();  // TODO: Make it async (See bug 1362)
+            LOG.debug("Transaction success for write of Flow {}", flowBuilder.getFlowName());
+        } catch (Exception e) {
+            LOG.error(e.getMessage(), e);
+            modification.cancel();
+        }
     }
 
     protected void removeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
-        dbutils.delete(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder));
+        mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder));
     }
 
     private String getDpid(Node node) {
@@ -268,22 +736,28 @@ public class NetvirtSfcOF13Provider implements INetvirtSfcOF13Provider{
                         nodeBuilder.getKey()).build();
     }
 
-    private short getTable() {
-        return Service.INGRESS_ACL.getTable();
+    private NodeBuilder createNodeBuilder(String nodeId) {
+        NodeBuilder builder = new NodeBuilder();
+        builder.setId(new NodeId(nodeId));
+        builder.setKey(new NodeKey(builder.getId()));
+        return builder;
     }
 
-    private final InstructionBuilder getMutablePipelineInstructionBuilder() {
-        Service nextService = orchestrator.getNextServiceInPipeline(Service.INGRESS_ACL);
-        if (nextService != null) {
-            return InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), nextService.getTable());
-        } else {
-            return InstructionUtils.createDropInstructions(new InstructionBuilder());
-        }
+    private InstanceIdentifier<RenderedServicePaths> getRspsId() {
+        return InstanceIdentifier.builder(RenderedServicePaths.class).build();
+    }
+
+    private InstanceIdentifier<RenderedServicePath> getRspId(String rspName) {
+        return InstanceIdentifier.builder(RenderedServicePaths.class)
+                .child(RenderedServicePath.class, new RenderedServicePathKey(new RspName(rspName))).build();
+    }
+
+    private short getTable() {
+        return Service.INGRESS_ACL.getTable();
     }
 
     private void setDependencies(ServiceReference serviceReference) {
         nodeCacheManager = (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
         southbound = (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
-        orchestrator = (PipelineOrchestrator) ServiceHelper.getGlobalInstance(PipelineOrchestrator.class, this);
     }
 }
diff --git a/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NshUtils.java b/openstack/net-virt-sfc/impl/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/sfc/openflow13/NshUtils.java
new file mode 100644 (file)
index 0000000..5265235
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2015 Dell, 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.openflow13;
+
+import java.math.BigInteger;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowjava.nx.match.rev140421.NxmNxReg;
+
+import com.google.common.net.InetAddresses;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.Action;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.DstChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxRegCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxTunIdCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.dst.choice.grouping.dst.choice.DstNxTunIpv4DstCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.group.buckets.bucket.action.action.NxActionRegLoadNodesNodeGroupBucketsBucketActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionOutputRegNodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionRegLoadNodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNshc1NodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNshc2NodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNshc3NodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNshc4NodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNsiNodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nodes.node.table.flow.instructions.instruction.instruction.apply.actions._case.apply.actions.action.action.NxActionSetNspNodesNodeTableFlowApplyActionsCaseBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.output.reg.grouping.NxOutputReg;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.output.reg.grouping.NxOutputRegBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoad;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.NxRegLoadBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.reg.load.grouping.nx.reg.load.DstBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._1.grouping.NxSetNshc1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._1.grouping.NxSetNshc1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._2.grouping.NxSetNshc2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._2.grouping.NxSetNshc2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._3.grouping.NxSetNshc3;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._3.grouping.NxSetNshc3Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._4.grouping.NxSetNshc4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nshc._4.grouping.NxSetNshc4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nsi.grouping.NxSetNsi;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nsi.grouping.NxSetNsiBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nsp.grouping.NxSetNsp;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.nx.action.set.nsp.grouping.NxSetNspBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.src.choice.grouping.SrcChoice;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension.nicira.action.rev140714.src.choice.grouping.src.choice.SrcNxRegCaseBuilder;
+
+/**
+ * Open Vswitch DB OpenFlow 1.3 Networking Provider for Netvirt SFC Utilities.
+ * @author Arun Yerra
+ */
+public class NshUtils {
+    private Ipv4Address nshTunIpDst;
+    private PortNumber nshTunUdpPort;
+    private long nshNsp;
+    private short nshNsi;
+    private long nshMetaC1;
+    private long nshMetaC2;
+
+    public NshUtils() {
+        super();
+    }
+
+    /**
+     * {@link NshUtils} constructor.
+     * @param nshTunIpDst Tunnel Destination IP
+     * @param nshTunUdpPort Tunnel Transport Port
+     * @param nshNsp Service Path Id
+     * @param nshNsi Service Path Index
+     * @param nshMetaC1 End point ID
+     * @param nshMetaC2 Tunnel Id.
+     */
+    public NshUtils(Ipv4Address nshTunIpDst, PortNumber nshTunUdpPort,
+            long nshNsp, short nshNsi, long nshMetaC1,
+            long nshMetaC2) {
+        super();
+        this.nshTunIpDst = nshTunIpDst;
+        this.nshTunUdpPort = nshTunUdpPort;
+        this.nshNsp = nshNsp;
+        this.nshNsi = nshNsi;
+        this.nshMetaC1 = nshMetaC1;
+        this.nshMetaC2 = nshMetaC2;
+    }
+
+    /*
+     * @return the nshTunIpDst
+     */
+    public Ipv4Address getNshTunIpDst() {
+        return nshTunIpDst;
+    }
+
+    /*
+     * @param nshTunIpDst the nshTunIpDst to set
+     */
+    public void setNshTunIpDst(Ipv4Address nshTunIpDst) {
+        this.nshTunIpDst = nshTunIpDst;
+    }
+
+    /*
+     * @return the nshTunUdpPort
+     */
+    public PortNumber getNshTunUdpPort() {
+        return nshTunUdpPort;
+    }
+
+    /*
+     * @param nshTunUdpPort the nshTunUdpPort to set
+     */
+    public void setNshTunUdpPort(PortNumber nshTunUdpPort) {
+        this.nshTunUdpPort = nshTunUdpPort;
+    }
+
+    /*
+     * @return the nshNsp
+     */
+    public long getNshNsp() {
+        return nshNsp;
+    }
+
+    /*
+     * @param nshNsp the nshNsp to set
+     */
+    public void setNshNsp(long nshNsp) {
+        this.nshNsp = nshNsp;
+    }
+
+    /*
+     * @return the nshNsi
+     */
+    public short getNshNsi() {
+        return nshNsi;
+    }
+
+    /*
+     * @param nshNsi the nshNsi to set
+     */
+    public void setNshNsi(short nshNsi) {
+        this.nshNsi = nshNsi;
+    }
+
+    /*
+     * @return the nshMetaC1
+     */
+    public long getNshMetaC1() {
+        return nshMetaC1;
+    }
+
+    /*
+     * @param nshMetaC1 the nshMetaC1 to set
+     */
+    public void setNshMetaC1(long nshMetaC1) {
+        this.nshMetaC1 = nshMetaC1;
+    }
+
+    /*
+     * @return the nshMetaC2
+     */
+    public long getNshMetaC2() {
+        return nshMetaC2;
+    }
+
+    /*
+     * @param nshMetaC2 the nshMetaC2 to set
+     */
+    public void setNshMetaC2(long nshMetaC2) {
+        this.nshMetaC2 = nshMetaC2;
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "NshUtils [nshTunIpDst=" + nshTunIpDst + ", nshTunUdpPort=" + nshTunUdpPort + ", nshNsp=" + nshNsp
+                + ", nshNsi=" + nshNsi + ", nshMetaC1=" + nshMetaC1 + ", nshMetaC2=" + nshMetaC2 + "]";
+    }
+
+    /**
+     * This method loads the action into NX register.
+     *{@link NshUtils} Loading Register
+     * @param dstChoice destination
+     * @param value value
+     * @param endOffset Offset
+     * @param groupBucket Identifies the group
+     */
+    public static Action nxLoadRegAction(DstChoice dstChoice, BigInteger value, int endOffset, boolean groupBucket) {
+        NxRegLoad reg = new NxRegLoadBuilder().setDst(
+                new DstBuilder().setDstChoice(dstChoice)
+                    .setStart(Integer.valueOf(0))
+                    .setEnd(Integer.valueOf(endOffset))
+                    .build())
+            .setValue(value)
+            .build();
+        if (groupBucket) {
+            return new NxActionRegLoadNodesNodeGroupBucketsBucketActionsCaseBuilder().setNxRegLoad(reg).build();
+        } else {
+            return new NxActionRegLoadNodesNodeTableFlowApplyActionsCaseBuilder().setNxRegLoad(reg).build();
+        }
+    }
+
+    public static Action nxLoadRegAction(DstChoice dstChoice, BigInteger value) {
+        return nxLoadRegAction(dstChoice, value, 31, false);
+    }
+
+    public static Action nxLoadRegAction(Class<? extends NxmNxReg> reg, BigInteger value) {
+        return nxLoadRegAction(new DstNxRegCaseBuilder().setNxReg(reg).build(), value);
+    }
+
+    public static Action nxSetNsiAction(Short nsi) {
+        NxSetNsi newNsi = new NxSetNsiBuilder().setNsi(nsi).build();
+        return new NxActionSetNsiNodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNsi(newNsi).build();
+    }
+
+    public static Action nxSetNspAction(Long nsp) {
+        NxSetNsp newNsp = new NxSetNspBuilder().setNsp(nsp).build();
+        return new NxActionSetNspNodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNsp(newNsp).build();
+    }
+
+    public static Action nxLoadNshc1RegAction(Long value) {
+        NxSetNshc1 newNshc1 = new NxSetNshc1Builder().setNshc(value).build();
+        return new NxActionSetNshc1NodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNshc1(newNshc1).build();
+    }
+
+    public static Action nxLoadNshc2RegAction(Long value) {
+        NxSetNshc2 newNshc2 = new NxSetNshc2Builder().setNshc(value).build();
+        return new NxActionSetNshc2NodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNshc2(newNshc2).build();
+    }
+
+    public static Action nxLoadNshc3RegAction(Long value) {
+        NxSetNshc3 newNshc3 = new NxSetNshc3Builder().setNshc(value).build();
+        return new NxActionSetNshc3NodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNshc3(newNshc3).build();
+    }
+
+    public static Action nxLoadNshc4RegAction(Long value) {
+        NxSetNshc4 newNshc4 = new NxSetNshc4Builder().setNshc(value).build();
+        return new NxActionSetNshc4NodesNodeTableFlowApplyActionsCaseBuilder().setNxSetNshc4(newNshc4).build();
+    }
+
+    /**
+     * This method loads Destination IPv4 address of Tunnel.
+     */
+    public static Action nxLoadTunIPv4Action(String ipAddress, boolean groupBucket) {
+        int ip = InetAddresses.coerceToInteger(InetAddresses.forString(ipAddress));
+        long ipl = ip & 0xffffffffL;
+        return nxLoadRegAction(new DstNxTunIpv4DstCaseBuilder().setNxTunIpv4Dst(Boolean.TRUE).build(),
+                BigInteger.valueOf(ipl), 31, groupBucket);
+    }
+
+    public static Action nxLoadTunIdAction(BigInteger tunnelId, boolean groupBucket) {
+        return nxLoadRegAction(new DstNxTunIdCaseBuilder().setNxTunId(Boolean.TRUE).build(), tunnelId, 31, groupBucket);
+    }
+
+    /**
+     * This method loads output port.
+     */
+    public static Action nxOutputRegAction(SrcChoice srcChoice) {
+        NxOutputReg reg = new NxOutputRegBuilder().setSrc(
+                new org.opendaylight.yang.gen.v1.urn.opendaylight.openflowplugin.extension
+                    .nicira.action.rev140714.nx.action.output.reg.grouping.nx.output.reg
+                    .SrcBuilder().setSrcChoice(srcChoice)
+                    .setOfsNbits(Integer.valueOf(31))
+                    .build())
+            .setMaxLen(Integer.valueOf(0xffff))
+            .build();
+        return new NxActionOutputRegNodesNodeTableFlowApplyActionsCaseBuilder().setNxOutputReg(reg).build();
+    }
+
+    public static Action nxOutputRegAction(Class<? extends NxmNxReg> reg) {
+        return nxOutputRegAction(new SrcNxRegCaseBuilder().setNxReg(reg).build());
+    }
+}
index a9dc254eb8da3537d5e12d93021c33bd993593fc..441bcf8beea3f61f4eb9f909dd52e731e7c9e172 100644 (file)
@@ -88,11 +88,11 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       </plugin>
       <plugin>
         <artifactId>maven-failsafe-plugin</artifactId>
-        <!--<configuration>
+        <configuration>
           <excludes>
             <exclude>**/NetvirtSfcIT.java</exclude>
           </excludes>
-        </configuration>-->
+        </configuration>
       </plugin>
     </plugins>
   </build>
index 443656e68e4c4597f8de2452a3b35aff4e52c868..c39bf63bfdd6ed55f9456abdd69002fb42c6aca6 100644 (file)
@@ -35,6 +35,7 @@ import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.AclUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.ClassifierUtils;
 import org.opendaylight.ovsdb.openstack.netvirt.sfc.utils.SfcUtils;
+import org.opendaylight.ovsdb.southbound.SouthboundConstants;
 import org.opendaylight.ovsdb.southbound.SouthboundUtil;
 import org.opendaylight.ovsdb.utils.mdsal.utils.MdsalUtils;
 import org.opendaylight.ovsdb.utils.southbound.utils.SouthboundUtils;
@@ -159,7 +160,7 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
                         LogLevel.INFO.name()),
                 editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
                         "log4j.logger.org.opendaylight.ovsdb.openstack.netvirt.sfc",
-                        LogLevel.INFO.name()),
+                        LogLevel.TRACE.name()),
                 /*editConfigurationFilePut(ORG_OPS4J_PAX_LOGGING_CFG,
                         "log4j.logger.org.opendaylight.ovsdb",
                         LogLevelOption.LogLevel.TRACE.name()),*/
@@ -364,4 +365,58 @@ public class NetvirtSfcIT extends AbstractMdsalTestBase {
         Thread.sleep(1000);
         assertTrue(southboundUtils.disconnectOvsdbNode(connectionInfo));
     }
+
+    @Test
+    public void testDemo() throws InterruptedException {
+        for (DemoVm vm : demoVms){
+            ConnectionInfo connectionInfo = southboundUtils.getConnectionInfo(vm.ipAddr, vm.ipPort);
+            assertNotNull("connection failed", southboundUtils.connectOvsdbNode(connectionInfo));
+            Node ovsdbNode = southboundUtils.getOvsdbNode(connectionInfo);
+            assertNotNull("node is not connected", ovsdbNode);
+            String controllerTarget = SouthboundUtil.getControllerTarget(ovsdbNode);
+            assertNotNull("Failed to get controller target", controllerTarget);
+            List<ControllerEntry> setControllerEntry = southboundUtils.createControllerEntry(controllerTarget);
+            Uri setUri = new Uri(controllerTarget);
+            assertTrue(southboundUtils.addBridge(connectionInfo, null, vm.name, null, true,
+                    SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null,
+                    setControllerEntry, null));
+
+            for (int i = 0; i < 10; i++) {
+                OvsdbBridgeAugmentation bridge = southboundUtils.getBridge(connectionInfo, vm.name);
+                assertNotNull("bridge was not found: " + vm.name, bridge);
+                assertNotNull("ControllerEntry was not found: "
+                                + southboundUtils.createControllerEntry(controllerTarget),
+                        bridge.getControllerEntry());
+                List<ControllerEntry> getControllerEntries = bridge.getControllerEntry();
+                for (ControllerEntry entry : getControllerEntries) {
+                    if (entry.isIsConnected()) {
+                        assertTrue(entry.isIsConnected());
+                        break;
+                    }
+                }
+                Thread.sleep(1000);
+            }
+
+            assertTrue(southboundUtils.deleteBridge(connectionInfo, vm.name));
+            Thread.sleep(1000);
+            assertTrue(southboundUtils.disconnectOvsdbNode(connectionInfo));
+        }
+    }
+
+    private class DemoVm {
+        String name;
+        String ipAddr;
+        String ipPort;
+
+        DemoVm(String name, String ipAddr, String ipPort) {
+            this.name = name;
+            this.ipAddr = ipAddr;
+            this.ipPort = ipPort;
+        }
+    }
+
+    private DemoVm[] demoVms = {
+            new DemoVm("sw1", "192.168.50.70", "6640"),
+            //new DemoVm("sw2", "192.168.50.71", "6640"),
+    };
 }
index 13d215cf9612a1d0130e11b275bc782561bbfe86..88f72412370d848dced908e007415556efb184d7 100644 (file)
@@ -108,6 +108,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
         <configuration>
           <instructions>
             <Export-Package>
+              org.opendaylight.ovsdb.southbound,
               org.opendaylight.ovsdb.southbound.*,
               org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.southbound.impl.rev141210.*</Export-Package>
           </instructions>
index 66b8c0745f1cc96e195a868efd093c82e4baf592..aec457f70413cfa44baddc6565af3786123061e3 100644 (file)
@@ -72,13 +72,15 @@ public class OvsdbNodeRemoveCommand extends AbstractTransactionCommand {
         ManagerEntry onlyConnectedManager = null;
         if (ovsdbNodeAugmentation != null) {
             int connectedManager = 0;
-            for (ManagerEntry manager : ovsdbNodeAugmentation.getManagerEntry()) {
-                if (manager.isConnected()) {
-                    connectedManager++;
-                    if (connectedManager > ONE_CONNECTED_MANAGER) {
-                        return false;
+            if (ovsdbNodeAugmentation.getManagerEntry() != null) {
+                for (ManagerEntry manager : ovsdbNodeAugmentation.getManagerEntry()) {
+                    if (manager.isConnected()) {
+                        connectedManager++;
+                        if (connectedManager > ONE_CONNECTED_MANAGER) {
+                            return false;
+                        }
+                        onlyConnectedManager = manager;
                     }
-                    onlyConnectedManager = manager;
                 }
             }
             if (connectedManager == 0) {
index 1b149470582f70eb0f520888722795377a4d373a..fdf21997c2ab5b0668b5cbf1966170994edc5cb6 100644 (file)
@@ -17,6 +17,8 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
@@ -46,6 +48,7 @@ public class TransactionInvokerImpl implements TransactionInvoker,TransactionCha
     private Map<ReadWriteTransaction,TransactionCommand> transactionToCommand
         = new HashMap<ReadWriteTransaction,TransactionCommand>();
     private List<ReadWriteTransaction> pendingTransactions = new ArrayList<ReadWriteTransaction>();
+    private final AtomicBoolean runTask = new AtomicBoolean( true );
 
     public TransactionInvokerImpl(DataBroker db) {
         this.db = db;
@@ -75,7 +78,7 @@ public class TransactionInvokerImpl implements TransactionInvoker,TransactionCha
 
     @Override
     public void run() {
-        while (true) {
+        while (runTask.get()) {
             forgetSuccessfulTransactions();
             try {
                 List<TransactionCommand> commands = extractCommands();
@@ -159,5 +162,9 @@ public class TransactionInvokerImpl implements TransactionInvoker,TransactionCha
     @Override
     public void close() throws Exception {
         this.executor.shutdown();
+        if (!this.executor.awaitTermination(1, TimeUnit.SECONDS)) {
+            runTask.set(false);
+            this.executor.shutdownNow();
+        }
     }
 }
index 0060341887294fada2126f65c0eb42632275d78f..5adf90027deec150d9e01548e0d79360d1b105ab 100644 (file)
@@ -24,6 +24,7 @@ import java.util.Map;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -54,6 +55,7 @@ public class TransactionInvokerImplTest {
     private BlockingQueue<AsyncTransaction<?, ?>> failedTransactionQueue
         = new LinkedBlockingQueue<AsyncTransaction<?, ?>>(QUEUE_SIZE);
     @Mock private ExecutorService executor;
+    @Mock private AtomicBoolean runTask;
     private Map<ReadWriteTransaction,TransactionCommand> transactionToCommand
         = new HashMap<ReadWriteTransaction,TransactionCommand>();
     private List<ReadWriteTransaction> pendingTransactions = new ArrayList<ReadWriteTransaction>();
@@ -204,6 +206,7 @@ public class TransactionInvokerImplTest {
     @Test
     public void testClose() throws Exception {
         MemberModifier.field(TransactionInvokerImpl.class, "executor").set(transactionInvokerImpl, executor);
+        MemberModifier.field(TransactionInvokerImpl.class, "runTask").set(transactionInvokerImpl, runTask);
         doNothing().when(executor).shutdown();
         transactionInvokerImpl.close();
         verify(executor).shutdown();
index 118d859d797e8176a53905ec41457616bdbecfed..038edc3e11fd95a0ee2fd1b3f84da08c4e8b9353 100644 (file)
@@ -22,20 +22,13 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
 import java.io.File;
-import java.lang.reflect.Array;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.ArrayList;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
 
 import javax.inject.Inject;
 
@@ -129,7 +122,6 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     private static final Logger LOG = LoggerFactory.getLogger(SouthboundIT.class);
     private static final int OVSDB_UPDATE_TIMEOUT = 1000;
     private static final String FORMAT_STR = "%s_%s_%d";
-    public static final int NUM_THREADS = 1;
     private static String addressStr;
     private static int portNumber;
     private static String connectionType;
@@ -250,7 +242,7 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     @Override
     public void setup() throws InterruptedException {
         if (setup) {
-            LOG.info("Skipping setUp, already initialized");
+            LOG.info("Skipping setup, already initialized");
             return;
         }
 
@@ -352,12 +344,8 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     }
 
     private boolean addOvsdbNode(final ConnectionInfo connectionInfo) throws InterruptedException {
-        InstanceIdentifier<Node> iid = createInstanceIdentifier(connectionInfo);
-        // Check that the node doesn't already exist (we don't support connecting twice)
-        Assert.assertNull("The OVSDB node has already been added",
-                mdsalUtils.read(LogicalDatastoreType.CONFIGURATION, iid));
         boolean result = mdsalUtils.put(LogicalDatastoreType.CONFIGURATION,
-                iid,
+                createInstanceIdentifier(connectionInfo),
                 createNode(connectionInfo));
         Thread.sleep(OVSDB_UPDATE_TIMEOUT);
         return result;
@@ -396,6 +384,7 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         Assert.assertTrue(deleteOvsdbNode(connectionInfo));
         Node node = getOvsdbNode(connectionInfo);
         Assert.assertNull(node);
+        //Assume.assumeNotNull(node); // Using assumeNotNull because there is no assumeNull
         LOG.info("Disconnected from {}", connectionInfoToString(connectionInfo));
         return true;
     }
@@ -500,7 +489,7 @@ public class SouthboundIT extends AbstractMdsalTestBase {
 
     @Test
     public void testOvsdbBridgeControllerInfo() throws InterruptedException {
-        ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
+        ConnectionInfo connectionInfo = getConnectionInfo(addressStr,portNumber);
         Node ovsdbNode = connectOvsdbNode(connectionInfo);
         String controllerTarget = SouthboundUtil.getControllerTarget(ovsdbNode);
         assertNotNull("Failed to get controller target", controllerTarget);
@@ -601,7 +590,7 @@ public class SouthboundIT extends AbstractMdsalTestBase {
      * @param setManagedBy toggles whether to setManagedBy for the bridge
      * @param dpType if passed null, this parameter is ignored
      * @param externalIds if passed null, this parameter is ignored
-     * @param otherConfig if passed null, this parameter is ignored
+     * @param otherConfigs if passed null, this parameter is ignored
      * @return success of bridge addition
      * @throws InterruptedException
      */
@@ -768,7 +757,7 @@ public class SouthboundIT extends AbstractMdsalTestBase {
      * @return the augmentation (or {@code null} if none)
      */
     private OvsdbTerminationPointAugmentation getOvsdbTerminationPointAugmentation(
-            ConnectionInfo connectionInfo, String bridgeName, LogicalDatastoreType store, int index ) {
+            ConnectionInfo connectionInfo, String bridgeName, LogicalDatastoreType store, int index) {
 
         List<TerminationPoint> tpList = getBridgeNode(connectionInfo, bridgeName, store).getTerminationPoint();
         if (tpList == null) {
@@ -876,523 +865,145 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
-    private interface KeyValueBuilder<T> {
-        T build(String testName, String key, String value);
-        T[] build(String testName, int count, String key, String value);
-        void reset();
-    }
-
-    /**
-     * Find the real type arguments for the given class (in its hierarchy), with at least {@code nb} arguments.
-     *
-     * @param clazz The class to start with.
-     * @param nb The minimum number of type arguments.
-     * @param <T> The type of the starting class.
-     * @return The matching type arguments (a {@link RuntimeException} is thrown if none match).
+    /*
+     * @see <code>SouthboundIT.testCRUDPortExternalIds()</code>
+     * This is helper test method to compare a test "set" of BridgeExternalIds against an expected "set"
      */
-    private static <T> Type[] findTypeArguments(final Class<T> clazz, final int nb) {
-        if (clazz == null || clazz.getSuperclass() == null) {
-            throw new RuntimeException("Missing type parameters");
-        }
-        Type superClassType = clazz.getGenericSuperclass();
-        if (superClassType instanceof ParameterizedType && ((ParameterizedType) superClassType)
-                .getActualTypeArguments().length >= nb) {
-            return ((ParameterizedType) superClassType).getActualTypeArguments();
-        }
-        return findTypeArguments(clazz.getSuperclass(), nb);
-    }
-
-    private abstract static class BaseKeyValueBuilder<T> implements KeyValueBuilder<T> {
-        private static final int COUNTER_START = 0;
-        private int counter = COUNTER_START;
-        @SuppressWarnings("unchecked")
-        private final Class<T> builtClass = (Class<T>) findTypeArguments(getClass(), 1)[0];
-
-        protected abstract Builder<T> builder();
-
-        protected abstract void setKey(Builder<T> builder, String key);
-
-        protected abstract void setValue(Builder<T> builder, String value);
-
-        @Override
-        public final T build(final String testName, final String key, final String value) {
-            final Builder<T> builder = builder();
-            this.counter++;
-            if (key != null) {
-                setKey(builder, String.format(FORMAT_STR, testName, key, this.counter));
-            }
-            if (value != null) {
-                setValue(builder, String.format(FORMAT_STR, testName, value, this.counter));
-            }
-            return builder.build();
-        }
+    private void assertExpectedPortExternalIdsExist( List<PortExternalIds> expected,
+                                                     List<PortExternalIds> test ) {
 
-        @SuppressWarnings("unchecked")
-        @Override
-        public final T[] build(final String testName, final int count, final String key, final String value) {
-            final T[] instances = (T[]) Array.newInstance(builtClass, count);
-            for (int idx = 0; idx < count; idx++) {
-                try {
-                    instances[idx] = build(testName, key, value);
-                } catch (ArrayStoreException e) {
-                    LOG.error("Error storing a value; we think we're managing {}", builtClass, e);
-                    throw e;
-                }
+        if (expected != null) {
+            for (PortExternalIds expectedExternalId : expected) {
+                Assert.assertTrue("The retrieved ids don't contain " + expectedExternalId,
+                        test.contains(expectedExternalId));
             }
-            return instances;
-        }
-
-        @Override
-        public final void reset() {
-            this.counter = COUNTER_START;
-        }
-    }
-
-    private static final class SouthboundPortExternalIdsBuilder extends BaseKeyValueBuilder<PortExternalIds> {
-        @Override
-        protected Builder<PortExternalIds> builder() {
-            return new PortExternalIdsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<PortExternalIds> builder, String key) {
-            ((PortExternalIdsBuilder) builder).setExternalIdKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<PortExternalIds> builder, String value) {
-            ((PortExternalIdsBuilder) builder).setExternalIdValue(value);
-        }
-    }
-
-    private static final class SouthboundInterfaceExternalIdsBuilder extends BaseKeyValueBuilder<InterfaceExternalIds> {
-        @Override
-        protected Builder<InterfaceExternalIds> builder() {
-            return new InterfaceExternalIdsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<InterfaceExternalIds> builder, String key) {
-            ((InterfaceExternalIdsBuilder) builder).setExternalIdKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<InterfaceExternalIds> builder, String value) {
-            ((InterfaceExternalIdsBuilder) builder).setExternalIdValue(value);
-        }
-    }
-
-    private static final class SouthboundOptionsBuilder extends BaseKeyValueBuilder<Options> {
-        @Override
-        protected Builder<Options> builder() {
-            return new OptionsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<Options> builder, String key) {
-            ((OptionsBuilder) builder).setOption(key);
-        }
-
-        @Override
-        protected void setValue(Builder<Options> builder, String value) {
-            ((OptionsBuilder) builder).setValue(value);
-        }
-    }
-
-    private static final class SouthboundInterfaceOtherConfigsBuilder extends BaseKeyValueBuilder<InterfaceOtherConfigs> {
-        @Override
-        protected Builder<InterfaceOtherConfigs> builder() {
-            return new InterfaceOtherConfigsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<InterfaceOtherConfigs> builder, String key) {
-            ((InterfaceOtherConfigsBuilder) builder).setOtherConfigKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<InterfaceOtherConfigs> builder, String value) {
-            ((InterfaceOtherConfigsBuilder) builder).setOtherConfigValue(value);
-        }
-    }
-
-    private static final class SouthboundPortOtherConfigsBuilder extends BaseKeyValueBuilder<PortOtherConfigs> {
-        @Override
-        protected Builder<PortOtherConfigs> builder() {
-            return new PortOtherConfigsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<PortOtherConfigs> builder, String key) {
-            ((PortOtherConfigsBuilder) builder).setOtherConfigKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<PortOtherConfigs> builder, String value) {
-            ((PortOtherConfigsBuilder) builder).setOtherConfigValue(value);
-        }
-    }
-
-    private static final class SouthboundBridgeOtherConfigsBuilder extends BaseKeyValueBuilder<BridgeOtherConfigs> {
-        @Override
-        protected Builder<BridgeOtherConfigs> builder() {
-            return new BridgeOtherConfigsBuilder();
         }
-
-        @Override
-        protected void setKey(Builder<BridgeOtherConfigs> builder, String key) {
-            ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<BridgeOtherConfigs> builder, String value) {
-            ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigValue(value);
-        }
-    }
-
-    private static final class SouthboundBridgeExternalIdsBuilder extends BaseKeyValueBuilder<BridgeExternalIds> {
-        @Override
-        protected Builder<BridgeExternalIds> builder() {
-            return new BridgeExternalIdsBuilder();
-        }
-
-        @Override
-        protected void setKey(Builder<BridgeExternalIds> builder, String key) {
-            ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdKey(key);
-        }
-
-        @Override
-        protected void setValue(Builder<BridgeExternalIds> builder, String value) {
-            ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdValue(value);
-        }
-    }
-
-    /*
-     * Generates the test cases involved in testing key-value-based data.  See inline comments for descriptions of
-     * the particular cases considered.
-     */
-    private static <T> List<SouthboundTestCase<T>> generateKeyValueTestCases(
-            KeyValueBuilder<T> builder, String idKey, String idValue) {
-        List<SouthboundTestCase<T>> testCases = new ArrayList<>();
-
-        final String GOOD_KEY = "GoodKey";
-        final String GOOD_VALUE = "GoodValue";
-        final String NO_VALUE_FOR_KEY = "NoValueForKey";
-        final String NO_KEY_FOR_VALUE = "NoKeyForValue";
-
-        // Test Case 1:  TestOne
-        // Test Type:    Positive
-        // Description:  Create a termination point with one value
-        // Expected:     A port is created with the single value specified below
-        final String testOneName = "TestOne";
-        testCases.add(new SouthboundTestCaseBuilder<T>()
-                .name(testOneName)
-                .input(builder.build(testOneName, idKey, idValue))
-                .expectInputAsOutput()
-                .build());
-        builder.reset();
-
-        // Test Case 2:  TestFive
-        // Test Type:    Positive
-        // Description:  Create a termination point with multiple (five) values
-        // Expected:     A port is created with the five values specified below
-        final String testFiveName = "TestFive";
-        testCases.add(new SouthboundTestCaseBuilder<T>()
-                .name(testFiveName)
-                .input(builder.build(testFiveName, 5, idKey, idValue))
-                .expectInputAsOutput()
-                .build());
-        builder.reset();
-
-        // Test Case 3:  TestOneGoodOneMalformedValue
-        // Test Type:    Negative
-        // Description:
-        //     One perfectly fine input
-        //        (TestOneGoodOneMalformedValue_GoodKey_1,
-        //        TestOneGoodOneMalformedValue_GoodValue_1)
-        //     and one malformed input which only has key specified
-        //        (TestOneGoodOneMalformedValue_NoValueForKey_2,
-        //        UNSPECIFIED)
-        // Expected:     A port is created without any values
-        final String testOneGoodOneMalformedValueName = "TestOneGoodOneMalformedValue";
-        testCases.add(new SouthboundTestCaseBuilder<T>()
-                .name(testOneGoodOneMalformedValueName)
-                .input(
-                        builder.build(testOneGoodOneMalformedValueName, GOOD_KEY, GOOD_VALUE),
-                        builder.build(testOneGoodOneMalformedValueName, NO_VALUE_FOR_KEY, null)
-                )
-                .expect()
-                .build());
-        builder.reset();
-
-        // Test Case 4:  TestOneGoodOneMalformedKey
-        // Test Type:    Negative
-        // Description:
-        //     One perfectly fine input
-        //        (TestOneGoodOneMalformedKey_GoodKey_1,
-        //        TestOneGoodOneMalformedKey_GoodValue_1)
-        //     and one malformed input which only has value specified
-        //        (UNSPECIFIED,
-        //        TestOneGoodOneMalformedKey_NoKeyForValue_2)
-        // Expected:     A port is created without any values
-        final String testOneGoodOneMalformedKeyName = "TestOneGoodOneMalformedKey";
-        testCases.add(new SouthboundTestCaseBuilder<T>()
-                .name(testOneGoodOneMalformedKeyName)
-                .input(
-                        builder.build(testOneGoodOneMalformedKeyName, GOOD_KEY, GOOD_VALUE),
-                        builder.build(testOneGoodOneMalformedKeyName, null, NO_KEY_FOR_VALUE)
-                )
-                .expect()
-                .build());
-        builder.reset();
-
-        return testCases;
-    }
-
-    /*
-     * Generates the test cases involved in testing PortExternalIds.  See inline comments for descriptions of
-     * the particular cases considered.
-     */
-    private List<SouthboundTestCase<PortExternalIds>> generatePortExternalIdsTestCases() {
-        return generateKeyValueTestCases(new SouthboundPortExternalIdsBuilder(), "PortExternalIdKey",
-                "PortExternalIdValue");
     }
 
     /*
-     * Tests the CRUD operations for <code>Port</code>
-     * <code>external_ids</code>.
+     * Tests the CRUD operations for <code>Port</code> <code>external_ids</code>.
      *
-     * @see <code>SouthboundIT.generatePortExternalIdsTestCases()</code> for
-     * specific test case information
+     * @see <code>SouthboundIT.generatePortExternalIdsTestCases()</code> for specific test case information
      */
     @Test
-    public void testCRUDTerminationPointPortExternalIds()
-            throws InterruptedException, ExecutionException {
-
+    public void testCRUDTerminationPointPortExternalIds() throws InterruptedException {
         final String TEST_PREFIX = "CRUDTPPortExternalIds";
+        final int TERMINATION_POINT_TEST_INDEX = 0;
 
         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
         connectOvsdbNode(connectionInfo);
 
-        // updateFromTestCases represent the original test case value.
-        // updateToTestCases represent the new value after the update has been
-        // performed.
-        List<SouthboundTestCase<PortExternalIds>> updateFromTestCases = generatePortExternalIdsTestCases();
-        List<SouthboundTestCase<PortExternalIds>> updateToTestCases = generatePortExternalIdsTestCases();
-        String testBridgeName;
-        String testPortName;
-
-        int counter = 1;
-        // multihreads the test using NUM_THREADS
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<PortExternalIds> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<PortExternalIds> toTestCase : updateToTestCases) {
-                testPortName = testBridgeName = String.format(FORMAT_STR,
-                        TEST_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                executor.submit(new TestCRUDTerminationPointRunnable<>(
-                        new SouthboundTestHelper<PortExternalIds>() {
-                            @Override
-                            public List<PortExternalIds> readValues(
-                                    OvsdbTerminationPointAugmentation augmentation) {
-                                return augmentation.getPortExternalIds();
-                            }
-
-                            @Override
-                            public void writeValues(
-                                    OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
-                                    List<PortExternalIds> updateFromInput) {
-                                augmentationBuilder.setPortExternalIds(updateFromInput);
-                            }
-                        },
-                        connectionInfo, testBridgeName, testPortName,
-                        fromTestCase.inputValues,
-                        fromTestCase.expectedValues,
-                        toTestCase.inputValues,
-                        toTestCase.expectedValues));
-            }
-        }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
-        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
-    }
-
-    /**
-     * Southbound test helper. Classes implementing this interface are used to provide concrete access to the input and
-     * output of the underlying augmentation for the type being managed.
-     *
-     * @param <T> The type of data used for the test case.
-     */
-    private interface SouthboundTestHelper<T> {
-        /**
-         * Read the values from the augmentation. These would usually be checked against the expected values provided
-         * for the test case.
-         *
-         * @param augmentation The augmentation to read from.
-         * @return The values read.
-         */
-        List<T> readValues(OvsdbTerminationPointAugmentation augmentation);
-
-        /**
-         * Write the values to the augmentation (via its builder). This would usually be used to apply the input values
-         * provided for the test case.
-         *
-         * @param augmentationBuilder The augmentation builder.
-         * @param values The values to write.
-         */
-        void writeValues(OvsdbTerminationPointAugmentationBuilder augmentationBuilder, List<T> values);
-    }
-
-    /**
-     * <p>
-     * Test runner used to apply a suite of create/read/update/delete tests. Each instance of a runner expects:
-     * </p>
-     * <ul>
-     * <li>a helper used to manipulate the appropriate data structures in the termination point augmentation (see
-     * {@link SouthboundTestHelper});</li>
-     * <li>connection information for the southbound;</li>
-     * <li>a name to use for the test bridge (this allows multiple tests to be conducted in parallel against different
-     * bridges);</li>
-     * <li>a name to use for the test port;</li>
-     * <li>the initial input values to use for the termination point augmentation;</li>
-     * <li>the initial expected values to check the augmentation against;</li>
-     * <li>the target input values to update the terminal point to;</li>
-     * <li>the target expected values to check the augmentation point against.</li>
-     * </ul>
-     * <p>The following tests are performed:</p>
-     * <ol>
-     * <li>the bridge is added;</li>
-     * <li>the termination point is added, with the provided initial input values;</li>
-     * <li>the termination point is read from the <em>configuration</em> data store, and checked against the provided
-     * initial expected values;</li>
-     * <li>the termination point is read from the <em>operational</em> data store, and checked against the provided
-     * initial expected values;</li>
-     * <li>the termination point is updated by merging the provided target input values;</li>
-     * <li>the termination point is read from the <em>configuration</em> data store, and checked against the provided
-     * initial <b>and</b> target expected values;</li>
-     * <li>the termination point is read from the <em>operational</em> data store, and checked against the provided
-     * initial <b>and</b> target expected values;</li>
-     * <li>the bridge is deleted.</li>
-     * </ol>
-     *
-     * @param <T> The type of data used for the test case.
-     */
-    private final class TestCRUDTerminationPointRunnable<T> implements Runnable {
-        private final SouthboundTestHelper<T> helper;
-        private final ConnectionInfo connectionInfo;
-        private final String testBridgeName;
-        private final String testPortName;
-        private final List<T> updateFromInput;
-        private final List<T> updateFromExpected;
-        private final List<T> updateToInput;
-        private final List<T> updateToExpected;
-
-        private TestCRUDTerminationPointRunnable(
-                SouthboundTestHelper<T> helper, ConnectionInfo connectionInfo, String testBridgeName,
-                String testPortName, List<T> updateFromInput, List<T> updateFromExpected, List<T> updateToInput,
-                List<T> updateToExpected) {
-            this.helper = helper;
-            this.connectionInfo = connectionInfo;
-            this.testBridgeName = testBridgeName;
-            this.testPortName = testPortName;
-            this.updateFromInput = updateFromInput;
-            this.updateFromExpected = updateFromExpected;
-            this.updateToInput = updateToInput;
-            this.updateToExpected = updateToExpected;
-        }
+        // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
+        // the update has been performed.
+        List<SouthboundTestCase<PortExternalIds>> updateFromTestCases =
+                generateKeyValueTestCases(new SouthboundPortExternalIdsBuilder(), "PortExternalIdsFrom");
+        List<SouthboundTestCase<PortExternalIds>> updateToTestCases =
+                generateKeyValueTestCases(new SouthboundPortExternalIdsBuilder(), "PortExternalIdsTo");
+
+        for (SouthboundTestCase<PortExternalIds> updateFromTestCase : updateFromTestCases) {
+            List<PortExternalIds> updateFromInputExternalIds = updateFromTestCase.inputValues;
+            List<PortExternalIds> updateFromExpectedExternalIds = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<PortExternalIds> updateToTestCase : updateToTestCases) {
+                String testBridgeAndPortName = String.format("%s_%s", TEST_PREFIX, updateToTestCase.name);
+                List<PortExternalIds> updateToInputExternalIds = updateToTestCase.inputValues;
+                List<PortExternalIds> updateToExpectedExternalIds = updateToTestCase.expectedValues;
 
-        @Override
-        public void run() {
-            try {
-                final int TERMINATION_POINT_TEST_INDEX = 0;
                 // CREATE: Create the test bridge
-                Assert.assertTrue(addBridge(connectionInfo, null,
-                        testBridgeName, null, true,
-                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
-                        true, null, null, null, null));
+                Assert.assertTrue(addBridge(connectionInfo, null, testBridgeAndPortName, null, true,
+                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null));
                 NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
-                        connectionInfo, new OvsdbBridgeName(testBridgeName)));
+                        connectionInfo, new OvsdbBridgeName(testBridgeAndPortName)));
                 OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
                         createGenericOvsdbTerminationPointAugmentationBuilder();
-                tpCreateAugmentationBuilder.setName(testPortName);
-                helper.writeValues(tpCreateAugmentationBuilder, updateFromInput);
-                Assert.assertTrue(addTerminationPoint(testBridgeNodeId, testPortName, tpCreateAugmentationBuilder));
+                tpCreateAugmentationBuilder.setName(testBridgeAndPortName);
+                tpCreateAugmentationBuilder.setPortExternalIds(updateFromInputExternalIds);
+                Assert.assertTrue(
+                        addTerminationPoint(testBridgeNodeId, testBridgeAndPortName, tpCreateAugmentationBuilder));
 
                 // READ: Read the test port and ensure changes are propagated to the CONFIGURATION data store,
                 // then repeat for OPERATIONAL data store
                 OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
-                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
                                 LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
-                List<T> updateFromConfiguration = null;
                 if (updateFromConfigurationTerminationPointAugmentation != null) {
-                    updateFromConfiguration =
-                        helper.readValues(updateFromConfigurationTerminationPointAugmentation);
-                }
-                if (updateFromConfiguration != null) {
-                    Assert.assertTrue(updateFromConfiguration.containsAll(updateFromExpected));
+                    List<PortExternalIds> updateFromConfigurationExternalIds =
+                            updateFromConfigurationTerminationPointAugmentation.getPortExternalIds();
+                    assertExpectedPortExternalIdsExist(updateFromExpectedExternalIds,
+                            updateFromConfigurationExternalIds);
                 }
                 OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
-                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
                                 LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
-                List<T> updateFromOperational = null;
                 if (updateFromOperationalTerminationPointAugmentation != null) {
-                    updateFromOperational = helper.readValues(updateFromOperationalTerminationPointAugmentation);
-                }
-                if (updateFromOperational != null) {
-                    Assert.assertTrue(updateFromOperational.containsAll(updateFromExpected));
+                    List<PortExternalIds> updateFromOperationalExternalIds =
+                            updateFromOperationalTerminationPointAugmentation.getPortExternalIds();
+                    assertExpectedPortExternalIdsExist(updateFromExpectedExternalIds, updateFromOperationalExternalIds);
                 }
 
                 // UPDATE:  update the external_ids
-                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeName).getNodeId();
+                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeAndPortName).getNodeId();
                 OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
                         new OvsdbTerminationPointAugmentationBuilder();
-                helper.writeValues(tpUpdateAugmentationBuilder, updateToInput);
+                tpUpdateAugmentationBuilder.setPortExternalIds(updateToInputExternalIds);
                 InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
                 NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
                 NodeId portUpdateNodeId = createManagedNodeId(portIid);
                 portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
                 TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
-                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testPortName)));
+                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testBridgeAndPortName)));
                 tpUpdateBuilder.addAugmentation(
                         OvsdbTerminationPointAugmentation.class,
                         tpUpdateAugmentationBuilder.build());
                 portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
-                boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
-                        portIid, portUpdateNodeBuilder.build());
+                Assert.assertTrue(mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
+                        portIid, portUpdateNodeBuilder.build()));
                 Thread.sleep(OVSDB_UPDATE_TIMEOUT);
-                Assert.assertTrue(result);
 
                 // READ: the test port and ensure changes are propagated to the CONFIGURATION data store,
                 // then repeat for OPERATIONAL data store
                 OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
-                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
                                 LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
-                List<T> updateToConfiguration = helper.readValues(updateToConfigurationTerminationPointAugmentation);
-                Assert.assertTrue(updateToConfiguration.containsAll(updateToExpected));
-                Assert.assertTrue(updateToConfiguration.containsAll(updateFromExpected));
+                if (updateToConfigurationTerminationPointAugmentation != null) {
+                    List<PortExternalIds> updateToConfigurationExternalIds =
+                            updateToConfigurationTerminationPointAugmentation.getPortExternalIds();
+                    assertExpectedPortExternalIdsExist(updateToExpectedExternalIds, updateToConfigurationExternalIds);
+                    assertExpectedPortExternalIdsExist(updateFromExpectedExternalIds, updateToConfigurationExternalIds);
+                }
                 OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
-                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeName,
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
                                 LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
-                List<T> updateToOperational = helper.readValues(updateToOperationalTerminationPointAugmentation);
-                Assert.assertTrue(updateToOperational.containsAll(updateToExpected));
-                Assert.assertTrue(updateToOperational.containsAll(updateFromExpected));
+                if (updateToOperationalTerminationPointAugmentation != null) {
+                    List<PortExternalIds> updateToOperationalExternalIds =
+                            updateToOperationalTerminationPointAugmentation.getPortExternalIds();
+                    if (updateFromExpectedExternalIds != null ) {
+                        assertExpectedPortExternalIdsExist(updateToExpectedExternalIds, updateToOperationalExternalIds);
+                        assertExpectedPortExternalIdsExist(updateFromExpectedExternalIds,
+                                updateToOperationalExternalIds);
+                    }
+                    // testCRUDTerminationPointInterfaceExternalIds()'s null assertion of updateToOperationalExternalIds
+                    // fails here
+                }
 
                 // DELETE
-                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
-            } catch (InterruptedException e) {
-                LOG.error("Test interrupted", e);
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeAndPortName));
             }
         }
+        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
-
     /*
-     * Generates the test cases involved in testing InterfaceExternalIds.  See inline comments for descriptions of
-     * the particular cases considered.
+     * @see <code>SouthboundIT.testCRUDInterfaceExternalIds()</code>
+     * This is helper test method to compare a test "set" of InterfaceExternalIds against an expected "set"
      */
-    private static List<SouthboundTestCase<InterfaceExternalIds>> generateInterfaceExternalIdsTestCases() {
-        return generateKeyValueTestCases(new SouthboundInterfaceExternalIdsBuilder(), "IntExternalIdKey",
-                "IntExternalIdValue");
+    private void assertExpectedInterfaceExternalIdsExist( List<InterfaceExternalIds> expected,
+                                                          List<InterfaceExternalIds> test ) {
+
+        if (expected != null) {
+            for (InterfaceExternalIds expectedExternalId : expected) {
+                Assert.assertTrue(test.contains(expectedExternalId));
+            }
+        }
     }
 
     /*
@@ -1401,60 +1012,128 @@ public class SouthboundIT extends AbstractMdsalTestBase {
      * @see <code>SouthboundIT.generateInterfaceExternalIdsTestCases()</code> for specific test case information
      */
     @Test
-    public void testCRUDTerminationPointInterfaceExternalIds() throws InterruptedException, ExecutionException {
+    public void testCRUDTerminationPointInterfaceExternalIds() throws InterruptedException {
         final String TEST_PREFIX = "CRUDTPInterfaceExternalIds";
+        final int TERMINATION_POINT_TEST_INDEX = 0;
 
         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
         connectOvsdbNode(connectionInfo);
 
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<InterfaceExternalIds>> updateFromTestCases = generateInterfaceExternalIdsTestCases();
-        List<SouthboundTestCase<InterfaceExternalIds>> updateToTestCases = generateInterfaceExternalIdsTestCases();
-        String testBridgeName;
-        String testPortName;
-
-        int counter = 1;
-        // multithreads the test using NUM_THREADS
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<InterfaceExternalIds> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<InterfaceExternalIds> toTestCase : updateToTestCases) {
-                testPortName = testBridgeName = String.format(FORMAT_STR,
-                        TEST_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                executor.submit(new TestCRUDTerminationPointRunnable<>(
-                        new SouthboundTestHelper<InterfaceExternalIds>() {
-                            @Override
-                            public List<InterfaceExternalIds> readValues(
-                                    OvsdbTerminationPointAugmentation augmentation) {
-                                return augmentation.getInterfaceExternalIds();
-                            }
-
-                            @Override
-                            public void writeValues(
-                                    OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
-                                    List<InterfaceExternalIds> values) {
-                                augmentationBuilder.setInterfaceExternalIds(values);
-                            }
-                        },
-                        connectionInfo, testBridgeName, testPortName,
-                        fromTestCase.inputValues,
-                        fromTestCase.expectedValues,
-                        toTestCase.inputValues,
-                        toTestCase.expectedValues));
+        List<SouthboundTestCase<InterfaceExternalIds>> updateFromTestCases = generateKeyValueTestCases(
+                new SouthboundInterfaceExternalIdsBuilder(), "InterfaceExternalIdsFrom");
+        List<SouthboundTestCase<InterfaceExternalIds>> updateToTestCases = generateKeyValueTestCases(
+                new SouthboundInterfaceExternalIdsBuilder(), "InterfaceExternalIdsTo");
+
+        for (SouthboundTestCase<InterfaceExternalIds> updateFromTestCase : updateFromTestCases) {
+            List<InterfaceExternalIds> updateFromInputExternalIds = updateFromTestCase.inputValues;
+            List<InterfaceExternalIds> updateFromExpectedExternalIds = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<InterfaceExternalIds> updateToTestCase : updateToTestCases) {
+                String testBridgeAndPortName = String.format("%s_%s", TEST_PREFIX, updateToTestCase.name);
+                List<InterfaceExternalIds> updateToInputExternalIds = updateToTestCase.inputValues;
+                List<InterfaceExternalIds> updateToExpectedExternalIds = updateToTestCase.expectedValues;
+
+                // CREATE: Create the test interface
+                Assert.assertTrue(addBridge(connectionInfo, null, testBridgeAndPortName, null, true,
+                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null));
+                NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
+                        connectionInfo, new OvsdbBridgeName(testBridgeAndPortName)));
+                OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
+                        createGenericOvsdbTerminationPointAugmentationBuilder();
+                tpCreateAugmentationBuilder.setName(testBridgeAndPortName);
+                tpCreateAugmentationBuilder.setInterfaceExternalIds(updateFromInputExternalIds);
+                Assert.assertTrue(
+                        addTerminationPoint(testBridgeNodeId, testBridgeAndPortName, tpCreateAugmentationBuilder));
+
+                // READ: Read the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromConfigurationTerminationPointAugmentation != null) {
+                    List<InterfaceExternalIds> updateFromConfigurationExternalIds =
+                            updateFromConfigurationTerminationPointAugmentation.getInterfaceExternalIds();
+                    assertExpectedInterfaceExternalIdsExist(updateFromExpectedExternalIds,
+                            updateFromConfigurationExternalIds);
+                }
+                OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromOperationalTerminationPointAugmentation != null) {
+                    List<InterfaceExternalIds> updateFromOperationalExternalIds =
+                            updateFromOperationalTerminationPointAugmentation.getInterfaceExternalIds();
+                    assertExpectedInterfaceExternalIdsExist(updateFromExpectedExternalIds,
+                            updateFromOperationalExternalIds);
+                }
+
+                // UPDATE:  update the external_ids
+                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeAndPortName).getNodeId();
+                OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
+                        new OvsdbTerminationPointAugmentationBuilder();
+                tpUpdateAugmentationBuilder.setInterfaceExternalIds(updateToInputExternalIds);
+                InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
+                NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
+                NodeId portUpdateNodeId = createManagedNodeId(portIid);
+                portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
+                TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
+                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testBridgeAndPortName)));
+                tpUpdateBuilder.addAugmentation(
+                        OvsdbTerminationPointAugmentation.class,
+                        tpUpdateAugmentationBuilder.build());
+                portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
+                Assert.assertTrue(mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
+                        portIid, portUpdateNodeBuilder.build()));
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+
+                // READ: the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateToConfigurationTerminationPointAugmentation != null) {
+                    List<InterfaceExternalIds> updateToConfigurationExternalIds =
+                            updateToConfigurationTerminationPointAugmentation.getInterfaceExternalIds();
+                    assertExpectedInterfaceExternalIdsExist(updateToExpectedExternalIds,
+                            updateToConfigurationExternalIds);
+                    assertExpectedInterfaceExternalIdsExist(updateFromExpectedExternalIds,
+                            updateToConfigurationExternalIds);
+                }
+                OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateToOperationalTerminationPointAugmentation != null) {
+                    List<InterfaceExternalIds> updateToOperationalExternalIds =
+                            updateToOperationalTerminationPointAugmentation.getInterfaceExternalIds();
+                    if (updateFromExpectedExternalIds != null) {
+                        assertExpectedInterfaceExternalIdsExist(updateToExpectedExternalIds,
+                                updateToOperationalExternalIds);
+                        assertExpectedInterfaceExternalIdsExist(updateFromExpectedExternalIds,
+                                updateToOperationalExternalIds);
+                    } else {
+                        Assert.assertNull(updateToOperationalExternalIds);
+                    }
+                }
+
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeAndPortName));
             }
         }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
     /*
-     * Generates the test cases involved in testing TP Options.  See inline comments for descriptions of
-     * the particular cases considered.
+     * @see <code>SouthboundIT.testCRUDTerminationPointOptions()</code>
+     * This is helper test method to compare a test "set" of Options against an expected "set"
      */
-    private List<SouthboundTestCase<Options>> generateTerminationPointOptionsTestCases() {
-        return generateKeyValueTestCases(new SouthboundOptionsBuilder(), "TOPOptionsKey", "TPOptionsValue");
+    private void assertExpectedOptionsExist( List<Options> expected,
+                                             List<Options> test ) {
+
+        if (expected != null) {
+            for (Options expectedOption : expected) {
+                Assert.assertTrue(test.contains(expectedOption));
+            }
+        }
     }
 
     /*
@@ -1465,58 +1144,118 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     @Test
     public void testCRUDTerminationPointOptions() throws InterruptedException {
         final String TEST_PREFIX = "CRUDTPOptions";
+        final int TERMINATION_POINT_TEST_INDEX = 0;
 
         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
         connectOvsdbNode(connectionInfo);
 
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<Options>> updateFromTestCases = generateTerminationPointOptionsTestCases();
-        List<SouthboundTestCase<Options>> updateToTestCases = generateTerminationPointOptionsTestCases();
-        String testBridgeName;
-        String testPortName;
-
-        int counter = 1;
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<Options> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<Options> toTestCase : updateToTestCases) {
-                testPortName = testBridgeName = String.format(FORMAT_STR,
-                        TEST_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                executor.submit(new TestCRUDTerminationPointRunnable<>(
-                        new SouthboundTestHelper<Options>() {
-                            @Override
-                            public List<Options> readValues(
-                                    OvsdbTerminationPointAugmentation augmentation) {
-                                return augmentation.getOptions();
-                            }
-
-                            @Override
-                            public void writeValues(
-                                    OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
-                                    List<Options> values) {
-                                augmentationBuilder.setOptions(values);
-                            }
-                        },
-                        connectionInfo, testBridgeName, testPortName,
-                        fromTestCase.inputValues,
-                        fromTestCase.expectedValues,
-                        toTestCase.inputValues,
-                        toTestCase.expectedValues));
+        List<SouthboundTestCase<Options>> updateFromTestCases =
+                generateKeyValueTestCases(new SouthboundOptionsBuilder(), "OptionsFrom");
+        List<SouthboundTestCase<Options>> updateToTestCases = generateKeyValueTestCases(new SouthboundOptionsBuilder(),
+                "OptionsTo");
+
+        for (SouthboundTestCase<Options> updateFromTestCase : updateFromTestCases) {
+            List<Options> updateFromInputOptions = updateFromTestCase.inputValues;
+            List<Options> updateFromExpectedOptions = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<Options> updateToTestCase : updateToTestCases) {
+                String testBridgeAndPortName = String.format("%s_%s", TEST_PREFIX, updateToTestCase.name);
+                List<Options> updateToInputOptions = updateToTestCase.inputValues;
+                List<Options> updateToExpectedOptions = updateToTestCase.expectedValues;
+
+                // CREATE: Create the test interface
+                Assert.assertTrue(addBridge(connectionInfo, null, testBridgeAndPortName, null, true,
+                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null));
+                NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
+                        connectionInfo, new OvsdbBridgeName(testBridgeAndPortName)));
+                OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
+                        createGenericOvsdbTerminationPointAugmentationBuilder();
+                tpCreateAugmentationBuilder.setName(testBridgeAndPortName);
+                tpCreateAugmentationBuilder.setOptions(updateFromInputOptions);
+                Assert.assertTrue(
+                        addTerminationPoint(testBridgeNodeId, testBridgeAndPortName, tpCreateAugmentationBuilder));
+
+                // READ: Read the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromConfigurationTerminationPointAugmentation != null) {
+                    List<Options> updateFromConfigurationOptions =
+                            updateFromConfigurationTerminationPointAugmentation.getOptions();
+                    assertExpectedOptionsExist(updateFromExpectedOptions, updateFromConfigurationOptions);
+                }
+                OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromOperationalTerminationPointAugmentation != null) {
+                    List<Options> updateFromOperationalOptions =
+                            updateFromOperationalTerminationPointAugmentation.getOptions();
+                    assertExpectedOptionsExist(updateFromExpectedOptions, updateFromOperationalOptions);
+                }
+
+                // UPDATE:  update the external_ids
+                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeAndPortName).getNodeId();
+                OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
+                        new OvsdbTerminationPointAugmentationBuilder();
+                tpUpdateAugmentationBuilder.setOptions(updateToInputOptions);
+                InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
+                NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
+                NodeId portUpdateNodeId = createManagedNodeId(portIid);
+                portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
+                TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
+                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testBridgeAndPortName)));
+                tpUpdateBuilder.addAugmentation(
+                        OvsdbTerminationPointAugmentation.class,
+                        tpUpdateAugmentationBuilder.build());
+                portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
+                Assert.assertTrue(mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
+                        portIid, portUpdateNodeBuilder.build()));
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+
+                // READ: the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateToConfigurationTerminationPointAugmentation != null) {
+                    List<Options> updateToConfigurationOptions =
+                            updateToConfigurationTerminationPointAugmentation.getOptions();
+                    assertExpectedOptionsExist(updateToExpectedOptions, updateToConfigurationOptions);
+                    assertExpectedOptionsExist(updateFromExpectedOptions, updateToConfigurationOptions);
+                }
+                OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateToOperationalTerminationPointAugmentation != null) {
+                    List<Options> updateToOperationalOptions =
+                            updateToOperationalTerminationPointAugmentation.getOptions();
+                    if (updateFromExpectedOptions != null) {
+                        assertExpectedOptionsExist(updateToExpectedOptions, updateToOperationalOptions);
+                        assertExpectedOptionsExist(updateFromExpectedOptions, updateToOperationalOptions);
+                    }
+                }
+
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeAndPortName));
             }
         }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
     /*
-     * Generates the test cases involved in testing Interface other_configs.  See inline comments for descriptions of
-     * the particular cases considered.
+     * @see <code>SouthboundIT.testCRUDInterfaceOtherConfigs()</code>
+     * This is helper test method to compare a test "set" of Options against an expected "set"
      */
-    private List<SouthboundTestCase<InterfaceOtherConfigs>> generateInterfaceOtherConfigsTestCases() {
-        return generateKeyValueTestCases(new SouthboundInterfaceOtherConfigsBuilder(), "IntOtherConfigsKey",
-                "IntOtherConfigsValue");
+    private void assertExpectedInterfaceOtherConfigsExist( List<InterfaceOtherConfigs> expected,
+                                                           List<InterfaceOtherConfigs> test ) {
+
+        if (expected != null && test != null) {
+            for (InterfaceOtherConfigs expectedOtherConfigs : expected) {
+                Assert.assertTrue(test.contains(expectedOtherConfigs));
+            }
+        }
     }
 
     /*
@@ -1527,58 +1266,124 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     @Test
     public void testCRUDTerminationPointInterfaceOtherConfigs() throws InterruptedException {
         final String TEST_PREFIX = "CRUDTPInterfaceOtherConfigs";
+        final int TERMINATION_POINT_TEST_INDEX = 0;
 
         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
         connectOvsdbNode(connectionInfo);
 
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<InterfaceOtherConfigs>> updateFromTestCases = generateInterfaceOtherConfigsTestCases();
-        List<SouthboundTestCase<InterfaceOtherConfigs>> updateToTestCases = generateInterfaceOtherConfigsTestCases();
-        String testBridgeName;
-        String testPortName;
-
-        int counter = 1;
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<InterfaceOtherConfigs> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<InterfaceOtherConfigs> toTestCase : updateToTestCases) {
-                testPortName = testBridgeName = String.format(FORMAT_STR,
-                        TEST_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                executor.submit(new TestCRUDTerminationPointRunnable<>(
-                        new SouthboundTestHelper<InterfaceOtherConfigs>() {
-                            @Override
-                            public List<InterfaceOtherConfigs> readValues(
-                                    OvsdbTerminationPointAugmentation augmentation) {
-                                return augmentation.getInterfaceOtherConfigs();
-                            }
-
-                            @Override
-                            public void writeValues(
-                                    OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
-                                    List<InterfaceOtherConfigs> values) {
-                                augmentationBuilder.setInterfaceOtherConfigs(values);
-                            }
-                        },
-                        connectionInfo, testBridgeName, testPortName,
-                        fromTestCase.inputValues,
-                        fromTestCase.expectedValues,
-                        toTestCase.inputValues,
-                        toTestCase.expectedValues));
+        List<SouthboundTestCase<InterfaceOtherConfigs>> updateFromTestCases =
+                generateKeyValueTestCases(new SouthboundInterfaceOtherConfigsBuilder(), "InterfaceOtherConfigsFrom");
+        List<SouthboundTestCase<InterfaceOtherConfigs>> updateToTestCases =
+                generateKeyValueTestCases(new SouthboundInterfaceOtherConfigsBuilder(), "InterfaceOtherConfigsTo");
+
+        for (SouthboundTestCase<InterfaceOtherConfigs> updateFromTestCase : updateFromTestCases) {
+            List<InterfaceOtherConfigs> updateFromInputOtherConfigs = updateFromTestCase.inputValues;
+            List<InterfaceOtherConfigs> updateFromExpectedOtherConfigs = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<InterfaceOtherConfigs> updateToTestCase : updateToTestCases) {
+                String testBridgeAndPortName = String.format("%s_%s", TEST_PREFIX, updateToTestCase.name);
+                List<InterfaceOtherConfigs> updateToInputOtherConfigs = updateToTestCase.inputValues;
+                List<InterfaceOtherConfigs> updateToExpectedOtherConfigs = updateToTestCase.expectedValues;
+
+                // CREATE: Create the test interface
+                Assert.assertTrue(addBridge(connectionInfo, null, testBridgeAndPortName, null, true,
+                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null));
+                NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
+                        connectionInfo, new OvsdbBridgeName(testBridgeAndPortName)));
+                OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
+                        createGenericOvsdbTerminationPointAugmentationBuilder();
+                tpCreateAugmentationBuilder.setName(testBridgeAndPortName);
+                tpCreateAugmentationBuilder.setInterfaceOtherConfigs(updateFromInputOtherConfigs);
+                Assert.assertTrue(
+                        addTerminationPoint(testBridgeNodeId, testBridgeAndPortName, tpCreateAugmentationBuilder));
+
+                // READ: Read the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromConfigurationTerminationPointAugmentation != null) {
+                    List<InterfaceOtherConfigs> updateFromConfigurationOtherConfigs =
+                            updateFromConfigurationTerminationPointAugmentation.getInterfaceOtherConfigs();
+                    assertExpectedInterfaceOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateFromConfigurationOtherConfigs);
+                }
+                OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromOperationalTerminationPointAugmentation != null) {
+                    List<InterfaceOtherConfigs> updateFromOperationalOtherConfigs =
+                            updateFromOperationalTerminationPointAugmentation.getInterfaceOtherConfigs();
+                    assertExpectedInterfaceOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateFromOperationalOtherConfigs);
+                }
+
+                // UPDATE:  update the other_configs
+                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeAndPortName).getNodeId();
+                OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
+                        new OvsdbTerminationPointAugmentationBuilder();
+                tpUpdateAugmentationBuilder.setInterfaceOtherConfigs(updateToInputOtherConfigs);
+                InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
+                NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
+                NodeId portUpdateNodeId = createManagedNodeId(portIid);
+                portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
+                TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
+                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testBridgeAndPortName)));
+                tpUpdateBuilder.addAugmentation(
+                        OvsdbTerminationPointAugmentation.class,
+                        tpUpdateAugmentationBuilder.build());
+                portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
+                Assert.assertTrue(mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
+                        portIid, portUpdateNodeBuilder.build()));
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+
+                // READ: the test interface and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateToConfigurationTerminationPointAugmentation != null) {
+                    List<InterfaceOtherConfigs> updateToConfigurationOtherConfigs =
+                            updateToConfigurationTerminationPointAugmentation.getInterfaceOtherConfigs();
+                    assertExpectedInterfaceOtherConfigsExist(updateToExpectedOtherConfigs,
+                            updateToConfigurationOtherConfigs);
+                    assertExpectedInterfaceOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateToConfigurationOtherConfigs);
+                }
+                OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateToOperationalTerminationPointAugmentation != null) {
+                    List<InterfaceOtherConfigs> updateToOperationalOtherConfigs =
+                            updateToOperationalTerminationPointAugmentation.getInterfaceOtherConfigs();
+                    if (updateFromExpectedOtherConfigs != null) {
+                        assertExpectedInterfaceOtherConfigsExist(updateToExpectedOtherConfigs,
+                                updateToOperationalOtherConfigs);
+                        assertExpectedInterfaceOtherConfigsExist(updateFromExpectedOtherConfigs,
+                                updateToOperationalOtherConfigs);
+                    }
+                }
+
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeAndPortName));
             }
         }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
     /*
-     * Generates the test cases involved in testing Port other_configs.  See inline comments for descriptions of
-     * the particular cases considered.
+     * @see <code>SouthboundIT.testCRUDPortOtherConfigs()</code>
+     * This is helper test method to compare a test "set" of Options against an expected "set"
      */
-    private List<SouthboundTestCase<PortOtherConfigs>> generatePortOtherConfigsTestCases() {
-        return generateKeyValueTestCases(new SouthboundPortOtherConfigsBuilder(), "PortOtherConfigsKey",
-                "PortOtherConfigsValue");
+    private void assertExpectedPortOtherConfigsExist( List<PortOtherConfigs> expected,
+                                                      List<PortOtherConfigs> test ) {
+
+        if (expected != null && test != null) {
+            for (PortOtherConfigs expectedOtherConfigs : expected) {
+                Assert.assertTrue(test.contains(expectedOtherConfigs));
+            }
+        }
     }
 
     /*
@@ -1589,48 +1394,109 @@ public class SouthboundIT extends AbstractMdsalTestBase {
     @Test
     public void testCRUDTerminationPointPortOtherConfigs() throws InterruptedException {
         final String TEST_PREFIX = "CRUDTPPortOtherConfigs";
+        final int TERMINATION_POINT_TEST_INDEX = 0;
 
         ConnectionInfo connectionInfo = getConnectionInfo(addressStr, portNumber);
         connectOvsdbNode(connectionInfo);
 
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<PortOtherConfigs>> updateFromTestCases = generatePortOtherConfigsTestCases();
-        List<SouthboundTestCase<PortOtherConfigs>> updateToTestCases = generatePortOtherConfigsTestCases();
-        String testBridgeName;
-        String testPortName;
-
-        int counter = 1;
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<PortOtherConfigs> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<PortOtherConfigs> toTestCase : updateToTestCases) {
-                testPortName = testBridgeName = String.format(FORMAT_STR,
-                        TEST_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                executor.submit(new TestCRUDTerminationPointRunnable<>(
-                        new SouthboundTestHelper<PortOtherConfigs>() {
-                            @Override
-                            public List<PortOtherConfigs> readValues(
-                                    OvsdbTerminationPointAugmentation augmentation) {
-                                return augmentation.getPortOtherConfigs();
-                            }
-
-                            @Override
-                            public void writeValues(
-                                    OvsdbTerminationPointAugmentationBuilder augmentationBuilder,
-                                    List<PortOtherConfigs> values) {
-                                augmentationBuilder.setPortOtherConfigs(values);
-                            }
-                        },
-                        connectionInfo, testBridgeName, testPortName,
-                        fromTestCase.inputValues,
-                        fromTestCase.expectedValues,
-                        toTestCase.inputValues,
-                        toTestCase.expectedValues));
+        List<SouthboundTestCase<PortOtherConfigs>> updateFromTestCases =
+                generateKeyValueTestCases(new SouthboundPortOtherConfigsBuilder(), "PortOtherConfigsFrom");
+        List<SouthboundTestCase<PortOtherConfigs>> updateToTestCases =
+                generateKeyValueTestCases(new SouthboundPortOtherConfigsBuilder(), "PortOtherConfigsTo");
+
+        for (SouthboundTestCase<PortOtherConfigs> updateFromTestCase : updateFromTestCases) {
+            List<PortOtherConfigs> updateFromInputOtherConfigs = updateFromTestCase.inputValues;
+            List<PortOtherConfigs> updateFromExpectedOtherConfigs = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<PortOtherConfigs> updateToTestCase : updateToTestCases) {
+                String testBridgeAndPortName = String.format("%s_%s", TEST_PREFIX, updateToTestCase.name);
+                List<PortOtherConfigs> updateToInputOtherConfigs = updateToTestCase.inputValues;
+                List<PortOtherConfigs> updateToExpectedOtherConfigs = updateToTestCase.expectedValues;
+
+                // CREATE: Create the test port
+                Assert.assertTrue(addBridge(connectionInfo, null, testBridgeAndPortName, null, true,
+                        SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null));
+                NodeId testBridgeNodeId = createManagedNodeId(createInstanceIdentifier(
+                        connectionInfo, new OvsdbBridgeName(testBridgeAndPortName)));
+                OvsdbTerminationPointAugmentationBuilder tpCreateAugmentationBuilder =
+                        createGenericOvsdbTerminationPointAugmentationBuilder();
+                tpCreateAugmentationBuilder.setName(testBridgeAndPortName);
+                tpCreateAugmentationBuilder.setPortOtherConfigs(updateFromInputOtherConfigs);
+                Assert.assertTrue(
+                        addTerminationPoint(testBridgeNodeId, testBridgeAndPortName, tpCreateAugmentationBuilder));
+
+                // READ: Read the test port and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateFromConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromConfigurationTerminationPointAugmentation != null) {
+                    List<PortOtherConfigs> updateFromConfigurationOtherConfigs =
+                            updateFromConfigurationTerminationPointAugmentation.getPortOtherConfigs();
+                    assertExpectedPortOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateFromConfigurationOtherConfigs);
+                }
+                OvsdbTerminationPointAugmentation updateFromOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateFromOperationalTerminationPointAugmentation != null) {
+                    List<PortOtherConfigs> updateFromOperationalOtherConfigs =
+                            updateFromOperationalTerminationPointAugmentation.getPortOtherConfigs();
+                    assertExpectedPortOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateFromOperationalOtherConfigs);
+                }
+
+                // UPDATE:  update the other_configs
+                testBridgeNodeId = getBridgeNode(connectionInfo, testBridgeAndPortName).getNodeId();
+                OvsdbTerminationPointAugmentationBuilder tpUpdateAugmentationBuilder =
+                        new OvsdbTerminationPointAugmentationBuilder();
+                tpUpdateAugmentationBuilder.setPortOtherConfigs(updateToInputOtherConfigs);
+                InstanceIdentifier<Node> portIid = SouthboundMapper.createInstanceIdentifier(testBridgeNodeId);
+                NodeBuilder portUpdateNodeBuilder = new NodeBuilder();
+                NodeId portUpdateNodeId = createManagedNodeId(portIid);
+                portUpdateNodeBuilder.setNodeId(portUpdateNodeId);
+                TerminationPointBuilder tpUpdateBuilder = new TerminationPointBuilder();
+                tpUpdateBuilder.setKey(new TerminationPointKey(new TpId(testBridgeAndPortName)));
+                tpUpdateBuilder.addAugmentation(
+                        OvsdbTerminationPointAugmentation.class,
+                        tpUpdateAugmentationBuilder.build());
+                portUpdateNodeBuilder.setTerminationPoint(Lists.newArrayList(tpUpdateBuilder.build()));
+                Assert.assertTrue(mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION,
+                        portIid, portUpdateNodeBuilder.build()));
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+
+                // READ: the test port and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                OvsdbTerminationPointAugmentation updateToConfigurationTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.CONFIGURATION, TERMINATION_POINT_TEST_INDEX);
+                if (updateToConfigurationTerminationPointAugmentation != null) {
+                    List<PortOtherConfigs> updateToConfigurationOtherConfigs =
+                            updateToConfigurationTerminationPointAugmentation.getPortOtherConfigs();
+                    assertExpectedPortOtherConfigsExist(updateToExpectedOtherConfigs,
+                            updateToConfigurationOtherConfigs);
+                    assertExpectedPortOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateToConfigurationOtherConfigs);
+                }
+                OvsdbTerminationPointAugmentation updateToOperationalTerminationPointAugmentation =
+                        getOvsdbTerminationPointAugmentation(connectionInfo, testBridgeAndPortName,
+                                LogicalDatastoreType.OPERATIONAL, TERMINATION_POINT_TEST_INDEX);
+                if (updateToOperationalTerminationPointAugmentation != null) {
+                    List<PortOtherConfigs> updateToOperationalOtherConfigs =
+                            updateToOperationalTerminationPointAugmentation.getPortOtherConfigs();
+                    if (updateFromExpectedOtherConfigs != null) {
+                        assertExpectedPortOtherConfigsExist(updateToExpectedOtherConfigs,
+                                updateToOperationalOtherConfigs);
+                        assertExpectedPortOtherConfigsExist(updateFromExpectedOtherConfigs,
+                                updateToOperationalOtherConfigs);
+                    }
+                }
+
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeAndPortName));
             }
         }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
@@ -1783,31 +1649,14 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
-    private ArrayList<Set<Integer>> generateVlanSets() {
-        ArrayList<Set<Integer>> vlanSets = new ArrayList<>();
-
-        Set<Integer> emptySet = new HashSet<>();
-        vlanSets.add(emptySet);
-
-        Set<Integer> singleSet = new HashSet<>();
-        Integer single = 2222;
-        singleSet.add(single);
-        vlanSets.add(singleSet);
-
-        Set<Integer> minMaxMiddleSet = new HashSet<>();
-        Integer min = 0;
-        minMaxMiddleSet.add(min);
-        Integer max = 4095;
-        minMaxMiddleSet.add(max);
-        Integer minPlusOne = min + 1;
-        minMaxMiddleSet.add(minPlusOne);
-        Integer maxMinusOne = max - 1;
-        minMaxMiddleSet.add(maxMinusOne);
-        Integer middle = (max - min) / 2;
-        minMaxMiddleSet.add(middle);
-        vlanSets.add(minMaxMiddleSet);
-
-        return vlanSets;
+    @SuppressWarnings("unchecked")
+    private List<Set<Integer>> generateVlanSets() {
+        int min = 0;
+        int max = 4095;
+        return Lists.newArrayList(
+                Collections.<Integer>emptySet(),
+                Sets.newHashSet(2222),
+                Sets.newHashSet(min, max, min + 1, max - 1, (max - min) / 2));
     }
 
     private List<Trunks> buildTrunkList(Set<Integer> trunkSet) {
@@ -1923,15 +1772,6 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
-    /*
-     * Generates the test cases involved in testing BridgeOtherConfigs.  See inline comments for descriptions of
-     * the particular cases considered.
-     */
-    private List<SouthboundTestCase<BridgeOtherConfigs>> generateBridgeOtherConfigsTestCases() {
-        return generateKeyValueTestCases(new SouthboundBridgeOtherConfigsBuilder(), "BridgeOtherConfigsKey",
-                "BridgeOtherConfigsValue");
-    }
-
     /*
      * @see <code>SouthboundIT.testCRUDBridgeOtherConfigs()</code>
      * This is helper test method to compare a test "set" of BridgeExternalIds against an expected "set"
@@ -1956,127 +1796,71 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         connectOvsdbNode(connectionInfo);
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<BridgeOtherConfigs>> updateFromTestCases = generateBridgeOtherConfigsTestCases();
-        List<SouthboundTestCase<BridgeOtherConfigs>> updateToTestCases = generateBridgeOtherConfigsTestCases();
-        String testBridgeName;
-
-        int counter = 1;
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<BridgeOtherConfigs> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<BridgeOtherConfigs> toTestCase : updateToTestCases) {
-                testBridgeName = String.format(FORMAT_STR, TEST_BRIDGE_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                TestCRUDBridgeOtherConfigsRunnable testRunnable =
-                        new TestCRUDBridgeOtherConfigsRunnable(
-                                connectionInfo, testBridgeName,
-                                fromTestCase.inputValues,
-                                fromTestCase.expectedValues,
-                                toTestCase.inputValues,
-                                toTestCase.expectedValues);
-                executor.submit(testRunnable);
-            }
-        }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
-
-        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
-    }
-
-    class TestCRUDBridgeOtherConfigsRunnable implements Runnable {
-
-        ConnectionInfo connectionInfo;
-        String testBridgeName;
-        List<BridgeOtherConfigs> updateFromInputOtherConfigs;
-        List<BridgeOtherConfigs> updateFromExpectedOtherConfigs;
-        List<BridgeOtherConfigs> updateToInputOtherConfigs;
-        List<BridgeOtherConfigs> updateToExpectedOtherConfigs;
+        List<SouthboundTestCase<BridgeOtherConfigs>> updateFromTestCases =
+                generateKeyValueTestCases(new SouthboundBridgeOtherConfigsBuilder(), "BridgeOtherConfigsFrom");
+        List<SouthboundTestCase<BridgeOtherConfigs>> updateToTestCases = generateKeyValueTestCases(
+                new SouthboundBridgeOtherConfigsBuilder(), "BridgeOtherConfigsTo");
+        for (SouthboundTestCase<BridgeOtherConfigs> updateFromTestCase : updateFromTestCases) {
+            List<BridgeOtherConfigs> updateFromInputOtherConfigs = updateFromTestCase.inputValues;
+            List<BridgeOtherConfigs> updateFromExpectedOtherConfigs = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<BridgeOtherConfigs> updateToTestCase : updateToTestCases) {
+                String testBridgeName = String.format("%s_%s", TEST_BRIDGE_PREFIX, updateToTestCase.name);
+                List<BridgeOtherConfigs> updateToInputOtherConfigs = updateToTestCase.inputValues;
+                List<BridgeOtherConfigs> updateToExpectedOtherConfigs = updateToTestCase.expectedValues;
 
-        TestCRUDBridgeOtherConfigsRunnable(
-                ConnectionInfo connectionInfo, String testBridgeName,
-                List<BridgeOtherConfigs> updateFromInputOtherConfigs,
-                List<BridgeOtherConfigs> updateFromExpectedOtherConfigs,
-                List<BridgeOtherConfigs> updateToInputOtherConfigs,
-                List<BridgeOtherConfigs> updateToExpectedOtherConfigs) {
-
-            this.connectionInfo = connectionInfo;
-            this.testBridgeName = testBridgeName;
-            this.updateFromInputOtherConfigs = updateFromInputOtherConfigs;
-            this.updateFromExpectedOtherConfigs = updateFromExpectedOtherConfigs;
-            this.updateToInputOtherConfigs = updateToInputOtherConfigs;
-            this.updateToExpectedOtherConfigs = updateToExpectedOtherConfigs;
-        }
+                // CREATE: Create the test bridge
+                boolean bridgeAdded = addBridge(connectionInfo, null,
+                        testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
+                        true, null, null, null, updateFromInputOtherConfigs);
+                Assert.assertTrue(bridgeAdded);
 
-        @Override
-        public void run() {
-            try {
-                test();
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
+                // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                List<BridgeOtherConfigs> updateFromConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
+                        LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
+                assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
+                        updateFromConfigurationOtherConfigs);
+                List<BridgeOtherConfigs> updateFromOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName).getBridgeOtherConfigs();
+                assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
+                        updateFromOperationalOtherConfigs);
 
-        public void test() throws InterruptedException {
-            // CREATE: Create the test bridge
-            boolean bridgeAdded = addBridge(connectionInfo, null,
-                    testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
-                    true, null, null, null, updateFromInputOtherConfigs);
-            Assert.assertTrue(bridgeAdded);
-
-            // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
-            // then repeat for OPERATIONAL data store
-            List<BridgeOtherConfigs> updateFromConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
-                    LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
-            assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
-                    updateFromConfigurationOtherConfigs);
-            List<BridgeOtherConfigs> updateFromOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName)
-                    .getBridgeOtherConfigs();
-            assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
-                    updateFromOperationalOtherConfigs);
-
-            // UPDATE:  update the external_ids
-            OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
-            bridgeAugmentationBuilder.setBridgeOtherConfigs(updateToInputOtherConfigs);
-            InstanceIdentifier<Node> bridgeIid =
-                    createInstanceIdentifier(connectionInfo,
-                            new OvsdbBridgeName(testBridgeName));
-            NodeBuilder bridgeNodeBuilder = new NodeBuilder();
-            Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
-            bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
-            bridgeNodeBuilder.setKey(bridgeNode.getKey());
-            bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
-            boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
-                    bridgeNodeBuilder.build());
-            Thread.sleep(OVSDB_UPDATE_TIMEOUT);
-            Assert.assertTrue(result);
+                // UPDATE:  update the external_ids
+                OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
+                bridgeAugmentationBuilder.setBridgeOtherConfigs(updateToInputOtherConfigs);
+                InstanceIdentifier<Node> bridgeIid =
+                        createInstanceIdentifier(connectionInfo,
+                                new OvsdbBridgeName(testBridgeName));
+                NodeBuilder bridgeNodeBuilder = new NodeBuilder();
+                Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
+                bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
+                bridgeNodeBuilder.setKey(bridgeNode.getKey());
+                bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
+                boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
+                        bridgeNodeBuilder.build());
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+                Assert.assertTrue(result);
 
-            // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
-            // then repeat for OPERATIONAL data store
-            List<BridgeOtherConfigs> updateToConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
-                    LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
-            assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs, updateToConfigurationOtherConfigs);
-            assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
-                    updateToConfigurationOtherConfigs);
-            List<BridgeOtherConfigs> updateToOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName)
-                    .getBridgeOtherConfigs();
-            if (updateFromExpectedOtherConfigs != null) {
-                assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs,
-                        updateToOperationalOtherConfigs);
+                // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                List<BridgeOtherConfigs> updateToConfigurationOtherConfigs = getBridge(connectionInfo, testBridgeName,
+                        LogicalDatastoreType.CONFIGURATION).getBridgeOtherConfigs();
+                assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs, updateToConfigurationOtherConfigs);
                 assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
-                        updateToOperationalOtherConfigs);
-            }
+                        updateToConfigurationOtherConfigs);
+                List<BridgeOtherConfigs> updateToOperationalOtherConfigs = getBridge(connectionInfo, testBridgeName)
+                        .getBridgeOtherConfigs();
+                if (updateFromExpectedOtherConfigs != null) {
+                    assertExpectedBridgeOtherConfigsExist(updateToExpectedOtherConfigs,
+                            updateToOperationalOtherConfigs);
+                    assertExpectedBridgeOtherConfigsExist(updateFromExpectedOtherConfigs,
+                            updateToOperationalOtherConfigs);
+                }
 
-            // DELETE
-            Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
+            }
         }
-    }
-
-    /*
-     * Generates the test cases involved in testing BridgeExternalIds.  See inline comments for descriptions of
-     * the particular cases considered.
-     */
-    private List<SouthboundTestCase<BridgeExternalIds>> generateBridgeExternalIdsTestCases() {
-        return generateKeyValueTestCases(new SouthboundBridgeExternalIdsBuilder(), "BridgeExternalIdsKey",
-                "BridgeExternalIdsValue");
+        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
     /*
@@ -2103,112 +1887,66 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         connectOvsdbNode(connectionInfo);
         // updateFromTestCases represent the original test case value.  updateToTestCases represent the new value after
         // the update has been performed.
-        List<SouthboundTestCase<BridgeExternalIds>> updateFromTestCases = generateBridgeExternalIdsTestCases();
-        List<SouthboundTestCase<BridgeExternalIds>> updateToTestCases = generateBridgeExternalIdsTestCases();
-        String testBridgeName;
-
-        int counter = 1;
-        ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
-        for (SouthboundTestCase<BridgeExternalIds> fromTestCase : updateFromTestCases) {
-            for (SouthboundTestCase<BridgeExternalIds> toTestCase : updateToTestCases) {
-                testBridgeName = String.format(FORMAT_STR, TEST_BRIDGE_PREFIX, toTestCase.name, counter);
-                counter += 1;
-                TestCRUDBridgeExternalIdsRunnable testRunnable =
-                        new TestCRUDBridgeExternalIdsRunnable(
-                                connectionInfo, testBridgeName,
-                                fromTestCase.inputValues,
-                                fromTestCase.expectedValues,
-                                toTestCase.inputValues,
-                                toTestCase.expectedValues);
-                executor.submit(testRunnable);
-            }
-        }
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.MINUTES);
-
-        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
-    }
+        List<SouthboundTestCase<BridgeExternalIds>> updateFromTestCases = generateKeyValueTestCases(
+                new SouthboundBridgeExternalIdsBuilder(), "BridgeExternalIdsFrom");
+        List<SouthboundTestCase<BridgeExternalIds>> updateToTestCases = generateKeyValueTestCases(
+                new SouthboundBridgeExternalIdsBuilder(), "BridgeExternalIdsTo");
+        for (SouthboundTestCase<BridgeExternalIds> updateFromTestCase : updateFromTestCases) {
+            List<BridgeExternalIds> updateFromInputExternalIds = updateFromTestCase.inputValues;
+            List<BridgeExternalIds> updateFromExpectedExternalIds = updateFromTestCase.expectedValues;
+            for (SouthboundTestCase<BridgeExternalIds> updateToTestCase : updateToTestCases) {
+                String testBridgeName = String.format("%s_%s", TEST_BRIDGE_PREFIX, updateToTestCase.name);
+                List<BridgeExternalIds> updateToInputExternalIds = updateToTestCase.inputValues;
+                List<BridgeExternalIds> updateToExpectedExternalIds = updateToTestCase.expectedValues;
 
-    class TestCRUDBridgeExternalIdsRunnable implements Runnable {
-        ConnectionInfo connectionInfo;
-        String testBridgeName;
-        List<BridgeExternalIds> updateFromInputExternalIds;
-        List<BridgeExternalIds> updateFromExpectedExternalIds;
-        List<BridgeExternalIds> updateToInputExternalIds;
-        List<BridgeExternalIds> updateToExpectedExternalIds;
-
-        TestCRUDBridgeExternalIdsRunnable(
-                ConnectionInfo connectionInfo, String testBridgeName,
-                List<BridgeExternalIds> updateFromInputExternalIds,
-                List<BridgeExternalIds> updateFromExpectedExternalIds,
-                List<BridgeExternalIds> updateToInputExternalIds,
-                List<BridgeExternalIds> updateToExpectedExternalIds) {
+                // CREATE: Create the test bridge
+                boolean bridgeAdded = addBridge(connectionInfo, null,
+                        testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
+                        true, null, updateFromInputExternalIds, null, null);
+                Assert.assertTrue(bridgeAdded);
 
-            this.connectionInfo = connectionInfo;
-            this.testBridgeName = testBridgeName;
-            this.updateFromInputExternalIds = updateFromInputExternalIds;
-            this.updateFromExpectedExternalIds = updateFromExpectedExternalIds;
-            this.updateToInputExternalIds = updateToInputExternalIds;
-            this.updateToExpectedExternalIds = updateToExpectedExternalIds;
-        }
+                // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                List<BridgeExternalIds> updateFromConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
+                        LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
+                assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromConfigurationExternalIds);
+                List<BridgeExternalIds> updateFromOperationalExternalIds = getBridge(connectionInfo, testBridgeName).getBridgeExternalIds();
+                assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromOperationalExternalIds);
 
-        @Override
-        public void run() {
-            try {
-                test();
-            } catch (InterruptedException e) {
-                e.printStackTrace();
-            }
-        }
+                // UPDATE:  update the external_ids
+                OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
+                bridgeAugmentationBuilder.setBridgeExternalIds(updateToInputExternalIds);
+                InstanceIdentifier<Node> bridgeIid =
+                        createInstanceIdentifier(connectionInfo,
+                                new OvsdbBridgeName(testBridgeName));
+                NodeBuilder bridgeNodeBuilder = new NodeBuilder();
+                Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
+                bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
+                bridgeNodeBuilder.setKey(bridgeNode.getKey());
+                bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
+                boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
+                        bridgeNodeBuilder.build());
+                Thread.sleep(OVSDB_UPDATE_TIMEOUT);
+                Assert.assertTrue(result);
 
-        public void test() throws InterruptedException {
-            // CREATE: Create the test bridge
-            boolean bridgeAdded = addBridge(connectionInfo, null,
-                    testBridgeName, null, true, SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"),
-                    true, null, updateFromInputExternalIds, null, null);
-            Assert.assertTrue(bridgeAdded);
-
-            // READ: Read the test bridge and ensure changes are propagated to the CONFIGURATION data store,
-            // then repeat for OPERATIONAL data store
-            List<BridgeExternalIds> updateFromConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
-                    LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
-            assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromConfigurationExternalIds);
-            List<BridgeExternalIds> updateFromOperationalExternalIds = getBridge(connectionInfo, testBridgeName)
-                    .getBridgeExternalIds();
-            assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateFromOperationalExternalIds);
-
-            // UPDATE:  update the external_ids
-            OvsdbBridgeAugmentationBuilder bridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
-            bridgeAugmentationBuilder.setBridgeExternalIds(updateToInputExternalIds);
-            InstanceIdentifier<Node> bridgeIid =
-                    createInstanceIdentifier(connectionInfo,
-                            new OvsdbBridgeName(testBridgeName));
-            NodeBuilder bridgeNodeBuilder = new NodeBuilder();
-            Node bridgeNode = getBridgeNode(connectionInfo, testBridgeName);
-            bridgeNodeBuilder.setNodeId(bridgeNode.getNodeId());
-            bridgeNodeBuilder.setKey(bridgeNode.getKey());
-            bridgeNodeBuilder.addAugmentation(OvsdbBridgeAugmentation.class, bridgeAugmentationBuilder.build());
-            boolean result = mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, bridgeIid,
-                    bridgeNodeBuilder.build());
-            Thread.sleep(OVSDB_UPDATE_TIMEOUT);
-            Assert.assertTrue(result);
+                // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
+                // then repeat for OPERATIONAL data store
+                List<BridgeExternalIds> updateToConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
+                        LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
+                assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToConfigurationExternalIds);
+                assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToConfigurationExternalIds);
+                List<BridgeExternalIds> updateToOperationalExternalIds = getBridge(connectionInfo, testBridgeName)
+                        .getBridgeExternalIds();
+                if (updateFromExpectedExternalIds != null) {
+                    assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToOperationalExternalIds);
+                    assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToOperationalExternalIds);
+                }
 
-            // READ: the test bridge and ensure changes are propagated to the CONFIGURATION data store,
-            // then repeat for OPERATIONAL data store
-            List<BridgeExternalIds> updateToConfigurationExternalIds = getBridge(connectionInfo, testBridgeName,
-                    LogicalDatastoreType.CONFIGURATION).getBridgeExternalIds();
-            assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToConfigurationExternalIds);
-            assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToConfigurationExternalIds);
-            List<BridgeExternalIds> updateToOperationalExternalIds = getBridge(connectionInfo, testBridgeName)
-                    .getBridgeExternalIds();
-            if (updateFromExpectedExternalIds != null) {
-                assertExpectedBridgeExternalIdsExist(updateToExpectedExternalIds, updateToOperationalExternalIds);
-                assertExpectedBridgeExternalIdsExist(updateFromExpectedExternalIds, updateToOperationalExternalIds);
+                // DELETE
+                Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
             }
-
-            // DELETE
-            Assert.assertTrue(deleteBridge(connectionInfo, testBridgeName));
         }
+        Assert.assertTrue(disconnectOvsdbNode(connectionInfo));
     }
 
     public static InstanceIdentifier<Node> createInstanceIdentifier(ConnectionInfo key,OvsdbBridgeName bridgeName) {
@@ -2327,24 +2065,22 @@ public class SouthboundIT extends AbstractMdsalTestBase {
         }
 
         /**
-         * Sets the expected output values.
+         * Indicates that the provided input values should be expected as output values.
          *
-         * @param expectedValues The expected output values.
          * @return The builder.
          */
-        @SafeVarargs
-        public final SouthboundTestCaseBuilder<T> expect(final T... expectedValues) {
-            this.expectedValues = Lists.newArrayList(expectedValues);
+        public SouthboundTestCaseBuilder<T> expectInputAsOutput() {
+            this.expectedValues = this.inputValues;
             return this;
         }
 
         /**
-         * Indicates that the provided input values should be expected as output values.
+         * Indicates that no output should be expected.
          *
          * @return The builder.
          */
-        public SouthboundTestCaseBuilder<T> expectInputAsOutput() {
-            this.expectedValues = this.inputValues;
+        public SouthboundTestCaseBuilder<T> expectNoOutput() {
+            this.expectedValues = null;
             return this;
         }
 
@@ -2358,4 +2094,217 @@ public class SouthboundIT extends AbstractMdsalTestBase {
             return new SouthboundTestCase<>(name, inputValues, expectedValues);
         }
     }
+
+    private abstract static class KeyValueBuilder<T> {
+        private static final int COUNTER_START = 0;
+        private int counter = COUNTER_START;
+
+        protected abstract Builder<T> builder();
+
+        protected abstract void setKey(Builder<T> builder, String key);
+
+        protected abstract void setValue(Builder<T> builder, String value);
+
+        public final T build(final String testName, final String key, final String value) {
+            final Builder<T> builder = builder();
+            this.counter++;
+            if (key != null) {
+                setKey(builder, String.format(FORMAT_STR, testName, key, this.counter));
+            }
+            if (value != null) {
+                setValue(builder, String.format(FORMAT_STR, testName, value, this.counter));
+            }
+            return builder.build();
+        }
+
+        public final void reset() {
+            this.counter = COUNTER_START;
+        }
+    }
+
+    private static final class SouthboundPortExternalIdsBuilder extends KeyValueBuilder<PortExternalIds> {
+        @Override
+        protected Builder<PortExternalIds> builder() {
+            return new PortExternalIdsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<PortExternalIds> builder, String key) {
+            ((PortExternalIdsBuilder) builder).setExternalIdKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<PortExternalIds> builder, String value) {
+            ((PortExternalIdsBuilder) builder).setExternalIdValue(value);
+        }
+    }
+
+    private static final class SouthboundInterfaceExternalIdsBuilder extends KeyValueBuilder<InterfaceExternalIds> {
+        @Override
+        protected Builder<InterfaceExternalIds> builder() {
+            return new InterfaceExternalIdsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<InterfaceExternalIds> builder, String key) {
+            ((InterfaceExternalIdsBuilder) builder).setExternalIdKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<InterfaceExternalIds> builder, String value) {
+            ((InterfaceExternalIdsBuilder) builder).setExternalIdValue(value);
+        }
+    }
+
+    private static final class SouthboundOptionsBuilder extends KeyValueBuilder<Options> {
+        @Override
+        protected Builder<Options> builder() {
+            return new OptionsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<Options> builder, String key) {
+            ((OptionsBuilder) builder).setOption(key);
+        }
+
+        @Override
+        protected void setValue(Builder<Options> builder, String value) {
+            ((OptionsBuilder) builder).setValue(value);
+        }
+    }
+
+    private static final class SouthboundInterfaceOtherConfigsBuilder extends KeyValueBuilder<InterfaceOtherConfigs> {
+        @Override
+        protected Builder<InterfaceOtherConfigs> builder() {
+            return new InterfaceOtherConfigsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<InterfaceOtherConfigs> builder, String key) {
+            ((InterfaceOtherConfigsBuilder) builder).setOtherConfigKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<InterfaceOtherConfigs> builder, String value) {
+            ((InterfaceOtherConfigsBuilder) builder).setOtherConfigValue(value);
+        }
+    }
+
+    private static final class SouthboundPortOtherConfigsBuilder extends KeyValueBuilder<PortOtherConfigs> {
+        @Override
+        protected Builder<PortOtherConfigs> builder() {
+            return new PortOtherConfigsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<PortOtherConfigs> builder, String key) {
+            ((PortOtherConfigsBuilder) builder).setOtherConfigKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<PortOtherConfigs> builder, String value) {
+            ((PortOtherConfigsBuilder) builder).setOtherConfigValue(value);
+        }
+    }
+
+    private static final class SouthboundBridgeOtherConfigsBuilder extends KeyValueBuilder<BridgeOtherConfigs> {
+        @Override
+        protected Builder<BridgeOtherConfigs> builder() {
+            return new BridgeOtherConfigsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<BridgeOtherConfigs> builder, String key) {
+            ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<BridgeOtherConfigs> builder, String value) {
+            ((BridgeOtherConfigsBuilder) builder).setBridgeOtherConfigValue(value);
+        }
+    }
+
+    private static final class SouthboundBridgeExternalIdsBuilder extends KeyValueBuilder<BridgeExternalIds> {
+        @Override
+        protected Builder<BridgeExternalIds> builder() {
+            return new BridgeExternalIdsBuilder();
+        }
+
+        @Override
+        protected void setKey(Builder<BridgeExternalIds> builder, String key) {
+            ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdKey(key);
+        }
+
+        @Override
+        protected void setValue(Builder<BridgeExternalIds> builder, String value) {
+            ((BridgeExternalIdsBuilder) builder).setBridgeExternalIdValue(value);
+        }
+    }
+
+    /*
+     * Generates the test cases involved in testing key-value-based data.  See inline comments for descriptions of
+     * the particular cases considered.
+     */
+    private static <T> List<SouthboundTestCase<T>> generateKeyValueTestCases(
+            KeyValueBuilder<T> builder, String testName) {
+        List<SouthboundTestCase<T>> testCases = new ArrayList<>();
+
+        final String GOOD_KEY = "GoodKey";
+        final String GOOD_VALUE = "GoodValue";
+        final String NO_VALUE_FOR_KEY = "NoValueForKey";
+
+        final String idKey = testName + "Key";
+        final String idValue = testName + "Value";
+
+        // Test Case 1:  TestOne
+        // Test Type:    Positive
+        // Description:  Create a termination point with one value
+        // Expected:     A port is created with the single value specified below
+        final String testOneName = "TestOne" + testName;
+        testCases.add(new SouthboundTestCaseBuilder<T>()
+                .name(testOneName)
+                .input(builder.build(testOneName, idKey, idValue))
+                .expectInputAsOutput()
+                .build());
+
+        // Test Case 2:  TestFive
+        // Test Type:    Positive
+        // Description:  Create a termination point with multiple (five) values
+        // Expected:     A port is created with the five values specified below
+        final String testFiveName = "TestFive" + testName;
+        builder.reset();
+        testCases.add(new SouthboundTestCaseBuilder<T>()
+                .name(testFiveName)
+                .input(
+                        builder.build(testFiveName, idKey, idValue),
+                        builder.build(testFiveName, idKey, idValue),
+                        builder.build(testFiveName, idKey, idValue),
+                        builder.build(testFiveName, idKey, idValue),
+                        builder.build(testFiveName, idKey, idValue))
+                .expectInputAsOutput()
+                .build());
+
+        // Test Case 3:  TestOneGoodOneMalformedValue
+        // Test Type:    Negative
+        // Description:
+        //     One perfectly fine input
+        //        (TestOneGoodOneMalformedValue_GoodKey_1,
+        //        TestOneGoodOneMalformedValue_GoodValue_1)
+        //     and one malformed input which only has key specified
+        //        (TestOneGoodOneMalformedValue_NoValueForKey_2,
+        //        UNSPECIFIED)
+        // Expected:     A port is created without any values
+        final String testOneGoodOneMalformedValueName = "TestOneGoodOneMalformedValue" + testName;
+        builder.reset();
+        testCases.add(new SouthboundTestCaseBuilder<T>()
+                .name(testOneGoodOneMalformedValueName)
+                .input(
+                        builder.build(testOneGoodOneMalformedValueName, GOOD_KEY, GOOD_VALUE),
+                        builder.build(testOneGoodOneMalformedValueName, NO_VALUE_FOR_KEY, null))
+                .expectNoOutput()
+                .build());
+        builder.reset();
+
+        return testCases;
+    }
 }
index 7ac9a396bb8c244232eb5788dd3aab3b48ffda90..76c90549d84ec86a5e7b86d0dfb9ec43b197e03a 100644 (file)
@@ -317,6 +317,13 @@ public class SouthboundUtils {
         return result;
     }
 
+    public boolean addBridge(final ConnectionInfo connectionInfo, final String bridgeName)
+            throws InterruptedException {
+
+        return addBridge(connectionInfo, null, bridgeName, null, true,
+                SouthboundConstants.OVSDB_FAIL_MODE_MAP.inverse().get("secure"), true, null, null, null, null);
+    }
+
     private void setManagedBy(final OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder,
                               final ConnectionInfo connectionInfo) {
         InstanceIdentifier<Node> connectionNodePath = createInstanceIdentifier(connectionInfo);