Merge topic/master/clustering to master branch 37/27837/1
authorAnil Vishnoi <vishnoianil@gmail.com>
Fri, 2 Oct 2015 17:08:10 +0000 (22:38 +0530)
committerAnil Vishnoi <vishnoianil@gmail.com>
Fri, 2 Oct 2015 17:08:19 +0000 (22:38 +0530)
Squashed commit of the following:

commit e9e3a762ee75844b930d8251f44d2718a3358445
Merge: 2adbc25 83cfd5b
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Fri Oct 2 19:54:23 2015 +0530

    Merge branch 'master' into topic/master/clustering

    Change-Id: I3c26c3f4095b78d869cae271b01fa1ab2609cf89
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 2adbc25b474d46f394e8f956c818383425570600
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Fri Oct 2 07:22:44 2015 +0530

    Addressed following issuses:
    1) Fixed entity unregisteration
    2) Implemented logic for southbound provider to participate
    in entityOwnership service, Southbound plugin instance as an entity
    This will avoid all the instances to do initiatize the operational
    and config topology, only master instance (who got ownership) will do that.
    3) Properly closing listener registration for OvsdbConnectionManager listerner.

    Change-Id: I73693b4faa8a6409c81c33647836516002b4b2fd
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 193f145796924c051b36c61974df4253e68efb7e
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Thu Oct 1 16:54:41 2015 +0530

    Addressed following issues:
    1) Handled ownership change notifications for the device that is not registered by the instance.
    2) If there is no owner of the device hasOwner=false, then clean up the operational data store,
    3) For switch initiated connections, generate iid by fetching the openvswtich table row from the device.

    Change-Id: I03158a616e7c020ce8f851b8251e4162f38ba622
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit c26d79561555c23f4aec894418537cf059a1f85a
Merge: 740d21a 7848a77
Author: Flavio Fernandes <ffernand@redhat.com>
Date:   Thu Oct 1 14:42:00 2015 -0400

    Merge branch 'master' into topic/master/clustering

    Change-Id: I191e71030d6523a3d7a11765010cc1b81b9405f3
Signed-off-by: Flavio Fernandes <ffernand@redhat.com>
commit 740d21afacb5a652a3a1d78ceff2c0d998492add
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Wed Sep 30 02:15:26 2015 +0530

    Fixed iid issue for switch initiated connections
    and refactored the code a bit

    Patch 2:Fixed unit tests.
    Conflicts:
     southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionInstance.java
     southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java

    Change-Id: I9f8adfba4a4f866d423503b24f400c40bd43131b
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 9079eb260e123c00625dc13e63a6f55d9bc42aea
Merge: b203967 0179232
Author: Flavio Fernandes <ffernand@redhat.com>
Date:   Tue Sep 29 12:58:54 2015 -0400

    Merge branch 'master' into topic/master/clustering

    Change-Id: I02c771238cd7447dbaa326516467259516868aa6
Signed-off-by: Flavio Fernandes <ffernand@redhat.com>
commit b203967504c1de8814c32e7c7c8015ebb749e154
Author: Sharad Mishra <sharad.d.mishra@intel.com>
Date:   Mon Sep 28 06:19:38 2015 -0700

    OVSDB cluster device ownership

    Only allow device owner to update device.
    Patch 2: Fixed unit test

    Change-Id: Id942a2ba16a83b19807e135a9b67f8a7eb7da003
Signed-off-by: Sharad Mishra <sharad.d.mishra@intel.com>
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 218785ea03b74287de86395d1e57471ce02fe5e4
Author: Flavio Fernandes <ffernand@redhat.com>
Date:   Wed Sep 23 09:55:06 2015 -0400

    Use EntityOwnershipService to get ownership on the device.
    If ownership is granted then *this* instance of southbound
    plugin can perform actions to configure the device, rest
    two southbound plugin instanceses in the cluster will just
    hole the connection with them and won't do any operation
    of configuring the device or reading from the device.
    For controller initiated connections, all the southbound
    plugin instance will attempt to connect to the device and
    contest for ownership of the device and selected owner of
    the device will do CRUD operations. For switch initiated
    connection, only instance of southbound plugin that received
    connection from switch will contest for ownership.

    Change-Id: I349699ae0630e12bdbe18b6bd2588a651edd1f81
Signed-off-by: Flavio Fernandes <ffernand@redhat.com>
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit da209a4aad67400a3c938329fb393ec6f7e2439e
Merge: ec7827c 11c0865
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Tue Sep 29 00:44:50 2015 +0530

    Merge branch 'master' into topic/master/clustering

    Change-Id: I44ac4d66f731c0c0f56d746c563c6210eeceb978
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit ec7827c688340fa85c42942ba6559201da07286a
Author: Flavio Fernandes <ffernand@redhat.com>
Date:   Mon Sep 21 17:57:02 2015 -0400

    Add hasDeviceOwnership flag to OvsdbConnectionInstance

    Patch set 4: fix ut

    Change-Id: Id7da9071374938c16b086b0d67059aec0a7cc3a0
Signed-off-by: Flavio Fernandes <ffernand@redhat.com>
commit 3cc32fd3d007b4c91dfbadee3920b9ae61a9283c
Author: Flavio Fernandes <ffernand@redhat.com>
Date:   Fri Sep 25 17:02:13 2015 -0400

    Set bridge controller(s) based on ovsdb node manager(s)

    Change-Id: I10bcc04aed78f874cbb8a7d9a82e59cf1b80ec82
Signed-off-by: Flavio Fernandes <ffernand@redhat.com>
commit 628749e43002c4aa9fc8bfdf7c827cba0532eb6b
Merge: 3d7ce06 360d519
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Sat Sep 26 01:27:28 2015 +0530

    Merge branch 'master' into topic/master/clustering

    Change-Id: I20e84a6971466357c6819c7dba5d51809f9b2878
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 3d7ce0660d25080dd1a5e3927eda6d071e085d12
Merge: 8a90856 708c8c3
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Fri Sep 25 06:07:23 2015 +0530

    Merge branch 'master' into topic/master/clustering

    Change-Id: I35038c6d7b2150d2efc9692a02d5e94bd97a8dba
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 8a908569f4e8e19e7f03f08f97d0704f9ca46cc9
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Tue Sep 22 01:52:42 2015 +0530

    Enable remote notification for southbound plugin

    (cherry picked from commit 01f0737be6abacaf56721731f6a19dcb13e291f0)

    Change-Id: Ida036244f3861e4d07437b49179bbf2851a212a5
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
commit 7747c272e6ad5f52ebcae97fd14e05762bc8f790
Author: Anil Vishnoi <vishnoianil@gmail.com>
Date:   Tue Sep 22 00:05:49 2015 +0530

    Wire entity ownership service to OVSDB southbound plugin

    Change-Id: I8baa3b31f9762c4b5656b399a1a7f705c1aed9c1
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
Change-Id: Iec7a1c3055dac748c81b470a3d2286b42e32397e
Signed-off-by: Anil Vishnoi <vishnoianil@gmail.com>
17 files changed:
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/api/Southbound.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/BridgeConfigurationManagerImpl.java
openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/SouthboundImpl.java
openstack/net-virt/src/test/java/org/opendaylight/ovsdb/openstack/netvirt/impl/BridgeConfigurationManagerImplTest.java
southbound/southbound-impl/src/main/config/default-config.xml
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/InstanceIdentifierCodec.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionInstance.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbDataChangeListener.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/SouthboundMapper.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/SouthboundProvider.java
southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/SouthboundUtil.java
southbound/southbound-impl/src/main/java/org/opendaylight/yang/gen/v1/urn/opendaylight/params/xml/ns/yang/southbound/impl/rev141210/SouthboundImplModule.java
southbound/southbound-impl/src/main/yang/southbound-impl.yang
southbound/southbound-impl/src/test/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionInstanceTest.java
southbound/southbound-impl/src/test/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManagerTest.java
southbound/southbound-impl/src/test/java/org/opendaylight/ovsdb/southbound/OvsdbDataChangeListenerTest.java

index 7a8f6e6a593255e785777a743b0ea77d6993c657..5289742d8c250dd50237520703dc4a6058be665b 100644 (file)
@@ -35,7 +35,7 @@ public interface Southbound {
     boolean isBridgeOnOvsdbNode(Node node, String bridgeName);
     String getOvsdbNodeUUID(Node node);
     String getOsdbNodeExternalIdsValue(OvsdbNodeAugmentation ovsdbNodeAugmentation, String key);
-    boolean addBridge(Node ovsdbNode, String bridgeName, String target);
+    boolean addBridge(Node ovsdbNode, String bridgeName, List<String> controllersStr);
     boolean deleteBridge(Node ovsdbNode);
     OvsdbBridgeAugmentation readBridge(Node node, String name);
     Node readBridgeNode(Node node, String name);
index 2a9b1b39d761a99fc61b829da0a04f8569b41c15..af9e6137ae6099b81bc278009c2b9351848d21c5 100644 (file)
@@ -21,6 +21,7 @@ import org.opendaylight.ovsdb.utils.config.ConfigProperties;
 import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagerEntry;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 
 import com.google.common.base.Preconditions;
@@ -29,6 +30,7 @@ import com.google.common.collect.Lists;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.net.UnknownHostException;
+import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
 
@@ -446,7 +448,7 @@ public class BridgeConfigurationManagerImpl implements BridgeConfigurationManage
         boolean rv = true;
         if ((!southbound.isBridgeOnOvsdbNode(ovsdbNode, bridgeName)) ||
                 (southbound.getBridgeFromConfig(ovsdbNode, bridgeName) == null)) {
-            rv = southbound.addBridge(ovsdbNode, bridgeName, getControllerTarget(ovsdbNode));
+            rv = southbound.addBridge(ovsdbNode, bridgeName, getControllersFromOvsdbNode(ovsdbNode));
         }
         return rv;
     }
@@ -491,42 +493,61 @@ public class BridgeConfigurationManagerImpl implements BridgeConfigurationManage
         return openFlowPort;
     }
 
-    private String getControllerTarget(Node node) {
-        String setControllerStr = null;
-        short openflowPort = Constants.OPENFLOW_PORT;
-        //Look at user configuration.
-        //TODO: In case we move to config subsystem to expose these user facing parameter,
-        // we will have to modify this code.
+    private List<String> getControllersFromOvsdbNode(Node node) {
+        List<String> controllersStr = new ArrayList<>();
 
         String controllerIpStr = getControllerIPAddress();
-
-        if(controllerIpStr == null){
-            // Check if ovsdb node has connection info
+        if (controllerIpStr != null) {
+            // If codepath makes it here, the ip address to be used was explicitly provided.
+            // Being so, also fetch openflowPort provided via ConfigProperties.
+            controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
+                    + ":" + controllerIpStr + ":" + getControllerOFPort());
+        } else {
+            // Check if ovsdb node has manager entries
             OvsdbNodeAugmentation ovsdbNodeAugmentation = southbound.extractOvsdbNode(node);
             if (ovsdbNodeAugmentation != null) {
-                ConnectionInfo connectionInfo = ovsdbNodeAugmentation.getConnectionInfo();
-                if(connectionInfo != null && connectionInfo.getLocalIp() != null) {
-                    controllerIpStr = new String(connectionInfo.getLocalIp().getValue());
-                }else{
-                    LOG.warn("Ovsdb Node does not contains connection info : {}", node);
+                List<ManagerEntry> managerEntries = ovsdbNodeAugmentation.getManagerEntry();
+                if (managerEntries != null && !managerEntries.isEmpty()) {
+                    for (ManagerEntry managerEntry : managerEntries) {
+                        if (managerEntry == null || managerEntry.getTarget() == null) {
+                            continue;
+                        }
+                        String[] tokens = managerEntry.getTarget().getValue().split(":");
+                        if (tokens.length == 3 && tokens[0].equalsIgnoreCase("tcp")) {
+                            controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
+                                    + ":" + tokens[1] + ":" + getControllerOFPort());
+                        } else {
+                            LOG.trace("Skipping manager entry {} for node {}",
+                                    managerEntry.getTarget(), node.getNodeId().getValue());
+                        }
+                    }
+                } else {
+                    LOG.warn("Ovsdb Node does not contain manager entries : {}", node);
                 }
             }
-        }else {
-            openflowPort = getControllerOFPort();
         }
 
-        if(controllerIpStr == null) {
-            // Neither user provided ip nor ovsdb node has controller ip, Lets use local machine ip address
+        if (controllersStr.isEmpty()) {
+            // Neither user provided ip nor ovsdb node has manager entries. Lets use local machine ip address.
             LOG.debug("Use local machine ip address as a OpenFlow Controller ip address");
             controllerIpStr = getLocalControllerHostIpAddress();
+            if (controllerIpStr != null) {
+                controllersStr.add(Constants.OPENFLOW_CONNECTION_PROTOCOL
+                        + ":" + controllerIpStr + ":" + Constants.OPENFLOW_PORT);
+            }
         }
-        if(controllerIpStr != null){
-            LOG.debug("Targe OpenFlow Controller found : {}", controllerIpStr);
-            setControllerStr = Constants.OPENFLOW_CONNECTION_PROTOCOL + ":" + controllerIpStr + ":" + openflowPort;
-        }else {
+
+        if (controllersStr.isEmpty()) {
             LOG.warn("Failed to determine OpenFlow controller ip address");
+        } else if (LOG.isDebugEnabled()) {
+            controllerIpStr = "";
+            for (String currControllerIpStr : controllersStr) {
+                controllerIpStr += " " + currControllerIpStr;
+            }
+            LOG.debug("Found {} OpenFlow Controller(s) :{}", controllersStr.size(), controllerIpStr);
         }
-        return setControllerStr;
+
+        return controllersStr;
     }
 
     private String getLocalControllerHostIpAddress() {
index 22934ea40965811109523cd7fe0f8dcfc4b02e6e..4200448a6a05b8121ae8f0128d4a29c521ac948e 100644 (file)
@@ -165,10 +165,10 @@ public class SouthboundImpl implements Southbound {
         return value;
     }
 
-    public boolean addBridge(Node ovsdbNode, String bridgeName, String target) {
+    public boolean addBridge(Node ovsdbNode, String bridgeName, List<String> controllersStr) {
         boolean result = false;
 
-        LOG.info("addBridge: node: {}, bridgeName: {}, target: {}", ovsdbNode, bridgeName, target);
+        LOG.info("addBridge: node: {}, bridgeName: {}, controller(s): {}", ovsdbNode, bridgeName, controllersStr);
         ConnectionInfo connectionInfo = getConnectionInfo(ovsdbNode);
         if (connectionInfo != null) {
             NodeBuilder bridgeNodeBuilder = new NodeBuilder();
@@ -177,7 +177,7 @@ public class SouthboundImpl implements Southbound {
             NodeId bridgeNodeId = MdsalHelper.createManagedNodeId(bridgeIid);
             bridgeNodeBuilder.setNodeId(bridgeNodeId);
             OvsdbBridgeAugmentationBuilder ovsdbBridgeAugmentationBuilder = new OvsdbBridgeAugmentationBuilder();
-            ovsdbBridgeAugmentationBuilder.setControllerEntry(createControllerEntries(target));
+            ovsdbBridgeAugmentationBuilder.setControllerEntry(createControllerEntries(controllersStr));
             ovsdbBridgeAugmentationBuilder.setBridgeName(new OvsdbBridgeName(bridgeName));
             ovsdbBridgeAugmentationBuilder.setProtocolEntry(createMdsalProtocols());
             ovsdbBridgeAugmentationBuilder.setFailMode(
@@ -262,10 +262,10 @@ public class SouthboundImpl implements Southbound {
         ovsdbBridgeAugmentationBuilder.setManagedBy(new OvsdbNodeRef(connectionNodePath));
     }
 
-    private void setControllerForBridge(Node ovsdbNode, String bridgeName, String targetString) {
+    private void setControllersForBridge(Node ovsdbNode, String bridgeName, List<String> controllersString) {
         ConnectionInfo connectionInfo = getConnectionInfo(ovsdbNode);
         if (connectionInfo != null) {
-            for (ControllerEntry controllerEntry: createControllerEntries(targetString)) {
+            for (ControllerEntry controllerEntry : createControllerEntries(controllersString)) {
                 InstanceIdentifier<ControllerEntry> iid =
                         MdsalHelper.createInstanceIdentifier(ovsdbNode.getKey(), bridgeName)
                                 .augmentation(OvsdbBridgeAugmentation.class)
@@ -277,11 +277,15 @@ public class SouthboundImpl implements Southbound {
         }
     }
 
-    private List<ControllerEntry> createControllerEntries(String targetString) {
-        List<ControllerEntry> controllerEntries = new ArrayList<ControllerEntry>();
-        ControllerEntryBuilder controllerEntryBuilder = new ControllerEntryBuilder();
-        controllerEntryBuilder.setTarget(new Uri(targetString));
-        controllerEntries.add(controllerEntryBuilder.build());
+    private List<ControllerEntry> createControllerEntries(List<String> controllersStr) {
+        List<ControllerEntry> controllerEntries = new ArrayList<>();
+        if (controllersStr != null) {
+            for (String controllerStr : controllersStr) {
+                ControllerEntryBuilder controllerEntryBuilder = new ControllerEntryBuilder();
+                controllerEntryBuilder.setTarget(new Uri(controllerStr));
+                controllerEntries.add(controllerEntryBuilder.build());
+            }
+        }
         return controllerEntries;
     }
 
index c69d021c1416410f132b9c3fdf32b06781656435..8c48b2709a22ac070ccf6af9351131c287169a0a 100644 (file)
@@ -12,6 +12,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyList;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.RETURNS_MOCKS;
@@ -222,7 +223,7 @@ public class BridgeConfigurationManagerImplTest {
         PowerMockito.mockStatic(ConfigProperties.class);
         when(ConfigProperties.getProperty(any(Class.class), anyString())).thenReturn(ADDRESS);
 
-        when(southbound.addBridge(any(Node.class), anyString(), anyString())).thenReturn(true);
+        when(southbound.addBridge(any(Node.class), anyString(), anyList())).thenReturn(true);
         when(configurationService.isL3ForwardingEnabled()).thenReturn(true);
 
         bridgeConfigurationManagerImpl.prepareNode(node);
index b552df5cd4787bdc53cb632b7df54fdc658e66db..547f31ca685dacb094f8a938adea851ac62d6cef 100644 (file)
@@ -30,6 +30,10 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
             <type xmlns:binding="urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding">binding:binding-normalized-node-serializer</type>
             <name>runtime-mapping-singleton</name>
           </binding-normalized-node-serializer>
+          <clustering-entity-ownership-service>
+             <type xmlns:ns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:core:spi:entity-ownership-service">ns:entity-ownership-service</type>
+             <name>entity-ownership-service</name>
+          </clustering-entity-ownership-service>
         </module>
       </modules>
     </data>
index 3d9430f56bd7575ef1ba519dfbc5fdc517325453..6b4774d0b977e89997e178eb0edcc4ad7f06aea3 100644 (file)
@@ -61,6 +61,10 @@ public class InstanceIdentifierCodec extends AbstractModuleStringInstanceIdentif
         return serialize(normalizedIid);
     }
 
+    public YangInstanceIdentifier getYangInstanceIdentifier(InstanceIdentifier<?> iid) {
+        return bindingNormalizedNodeSerializer.toYangInstanceIdentifier(iid);
+    }
+
     public  InstanceIdentifier<?> bindingDeserializer(String iidString) throws DeserializationException {
         YangInstanceIdentifier normalizedYangIid = deserialize(iidString);
         InstanceIdentifier<?> iid = bindingNormalizedNodeSerializer.fromYangInstanceIdentifier(normalizedYangIid);
@@ -71,5 +75,4 @@ public class InstanceIdentifierCodec extends AbstractModuleStringInstanceIdentif
         InstanceIdentifier<?> iid = bindingNormalizedNodeSerializer.fromYangInstanceIdentifier(yangIID);
         return iid;
     }
-
 }
index 547ad2642c06230b60491193d71e22fcbcacef5a..3ea56b258399dc8c1847eb9e10e5a71e0e52b0fe 100644 (file)
@@ -13,6 +13,8 @@ import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
 
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
 import org.opendaylight.ovsdb.lib.EchoServiceCallbackFilters;
 import org.opendaylight.ovsdb.lib.LockAquisitionCallback;
 import org.opendaylight.ovsdb.lib.LockStolenCallback;
@@ -47,6 +49,8 @@ import org.slf4j.LoggerFactory;
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.ListenableFuture;
 
+import javax.annotation.Nonnull;
+
 public class OvsdbConnectionInstance implements OvsdbClient {
     private static final Logger LOG = LoggerFactory.getLogger(OvsdbConnectionInstance.class);
     private OvsdbClient client;
@@ -54,15 +58,18 @@ public class OvsdbConnectionInstance implements OvsdbClient {
     private TransactionInvoker txInvoker;
     private Map<DatabaseSchema,TransactInvoker> transactInvokers;
     private MonitorCallBack callback;
-    private ConnectionInfo key;
+    // private ConnectionInfo key;
     private InstanceIdentifier<Node> instanceIdentifier;
+    private volatile boolean hasDeviceOwnership = false;
+    private Entity connectedEntity;
+    private EntityOwnershipCandidateRegistration deviceOwnershipCandidateRegistration;
 
     OvsdbConnectionInstance(ConnectionInfo key,OvsdbClient client,TransactionInvoker txInvoker,
             InstanceIdentifier<Node> iid) {
         this.connectionInfo = key;
         this.client = client;
         this.txInvoker = txInvoker;
-        this.key = key;
+        // this.key = key;
         this.instanceIdentifier = iid;
     }
 
@@ -237,4 +244,37 @@ public class OvsdbConnectionInstance implements OvsdbClient {
             MonitorHandle monitorHandle, MonitorCallBack callback) {
         return null;
     }
+
+    public Entity getConnectedEntity() {
+        return this.connectedEntity;
+    }
+
+    public void setConnectedEntity(Entity entity ) {
+        this.connectedEntity = entity;
+    }
+
+    public Boolean hasOvsdbClient(OvsdbClient otherClient) {
+        return client.equals(otherClient);
+    }
+
+    public Boolean getHasDeviceOwnership() {
+        return Boolean.valueOf(hasDeviceOwnership);
+    }
+
+    public void setHasDeviceOwnership(Boolean hasDeviceOwnership) {
+        if (hasDeviceOwnership != null) {
+            this.hasDeviceOwnership = hasDeviceOwnership.booleanValue();
+        }
+    }
+
+    public void setDeviceOwnershipCandidateRegistration(@Nonnull EntityOwnershipCandidateRegistration registration) {
+        this.deviceOwnershipCandidateRegistration = registration;
+    }
+
+    public void closeDeviceOwnershipCandidateRegistration() {
+        if (deviceOwnershipCandidateRegistration != null) {
+            this.deviceOwnershipCandidateRegistration.close();
+            setHasDeviceOwnership(Boolean.FALSE);
+        }
+    }
 }
index 0db2495b5be877717e793c5271cafb9fd7f9e99a..54ff1e5a21b811fb66a797c1a00f21dc90100fd5 100644 (file)
@@ -7,18 +7,40 @@
  */
 package org.opendaylight.ovsdb.southbound;
 
+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;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Nonnull;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
+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.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 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;
+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.openvswitch.OpenVSwitch;
 import org.opendaylight.ovsdb.southbound.transactions.md.OvsdbNodeRemoveCommand;
 import org.opendaylight.ovsdb.southbound.transactions.md.TransactionInvoker;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
@@ -27,6 +49,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.re
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,44 +58,82 @@ import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
 
 public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoCloseable {
-    Map<ConnectionInfo,OvsdbConnectionInstance> clients
-        = new ConcurrentHashMap<ConnectionInfo,OvsdbConnectionInstance>();
+    private Map<ConnectionInfo, OvsdbConnectionInstance> clients =
+            new ConcurrentHashMap<ConnectionInfo,OvsdbConnectionInstance>();
     private static final Logger LOG = LoggerFactory.getLogger(OvsdbConnectionManager.class);
+    private static final String ENTITY_TYPE = "ovsdb";
 
     private DataBroker db;
     private TransactionInvoker txInvoker;
     private Map<ConnectionInfo,InstanceIdentifier<Node>> instanceIdentifiers =
             new ConcurrentHashMap<ConnectionInfo,InstanceIdentifier<Node>>();
+    private Map<Entity, OvsdbConnectionInstance> entityConnectionMap =
+            new ConcurrentHashMap<>();
+    private EntityOwnershipService entityOwnershipService;
+    private OvsdbDeviceEntityOwnershipListener ovsdbDeviceEntityOwnershipListener;
 
-    public OvsdbConnectionManager(DataBroker db,TransactionInvoker txInvoker) {
+    public OvsdbConnectionManager(DataBroker db,TransactionInvoker txInvoker,
+                                  EntityOwnershipService entityOwnershipService) {
         this.db = db;
         this.txInvoker = txInvoker;
+        this.entityOwnershipService = entityOwnershipService;
+        this.ovsdbDeviceEntityOwnershipListener = new OvsdbDeviceEntityOwnershipListener(this, entityOwnershipService);
     }
 
     @Override
-    public void connected(final OvsdbClient externalClient) {
+    public void connected(@Nonnull final OvsdbClient externalClient) {
+
         OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient);
-        client.registerCallbacks();
+
+        // Register Cluster Ownership for ConnectionInfo
+        registerEntityForOwnership(client);
     }
 
     public OvsdbConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) {
         LOG.info("OVSDB Connection from {}:{}",externalClient.getConnectionInfo().getRemoteAddress(),
                 externalClient.getConnectionInfo().getRemotePort());
         ConnectionInfo key = SouthboundMapper.createConnectionInfo(externalClient);
-        OvsdbConnectionInstance client = new OvsdbConnectionInstance(key,externalClient,txInvoker,
+        OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
+
+        // Check if existing ovsdbConnectionInstance for the OvsdbClient present.
+        // In such cases, we will see if the ovsdbConnectionInstance has same externalClient.
+        if (ovsdbConnectionInstance != null) {
+            if (ovsdbConnectionInstance.hasOvsdbClient(externalClient)) {
+                LOG.warn("OVSDB Connection Instance {} already exists for client {}", key, externalClient);
+                return ovsdbConnectionInstance;
+            }
+            LOG.warn("OVSDB Connection Instance {} being replaced with client {}", key, externalClient);
+            ovsdbConnectionInstance.disconnect();
+
+            // Unregister Cluster Ownership for ConnectionInfo
+            // Because the ovsdbConnectionInstance is about to be completely replaced!
+            unregisterEntityForOwnership(ovsdbConnectionInstance);
+
+            removeConnectionInstance(key);
+        }
+
+        ovsdbConnectionInstance = new OvsdbConnectionInstance(key, externalClient, txInvoker,
                 getInstanceIdentifier(key));
-        putConnectionInstance(key, client);
-        client.createTransactInvokers();
-        return client;
+        ovsdbConnectionInstance.createTransactInvokers();
+        return ovsdbConnectionInstance;
     }
 
     @Override
     public void disconnected(OvsdbClient client) {
-        LOG.info("OVSDB Disconnect from {}:{}",client.getConnectionInfo().getRemoteAddress(),
+        LOG.info("OVSDB Disconnected from {}:{}. Cleaning up the operational data store"
+                ,client.getConnectionInfo().getRemoteAddress(),
                 client.getConnectionInfo().getRemotePort());
         ConnectionInfo key = SouthboundMapper.createConnectionInfo(client);
-        txInvoker.invoke(new OvsdbNodeRemoveCommand(getConnectionInstance(key),null,null));
-        clients.remove(key);
+        OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
+        if (ovsdbConnectionInstance != null) {
+            txInvoker.invoke(new OvsdbNodeRemoveCommand(ovsdbConnectionInstance, null, null));
+            removeConnectionInstance(key);
+
+            // Unregister Cluster Onwership for ConnectionInfo
+            unregisterEntityForOwnership(ovsdbConnectionInstance);
+        } else {
+            LOG.warn("OVSDB disconnected event did not find connection instance for {}", key);
+        }
         LOG.trace("OvsdbConnectionManager: disconnected exit");
     }
 
@@ -80,7 +141,7 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos
             OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
         // TODO handle case where we already have a connection
         // TODO use transaction chains to handle ordering issues between disconnected
-        // and connected when writing to the operational store
+        // TODO and connected when writing to the operational store
         InetAddress ip = SouthboundMapper.createInetAddress(ovsdbNode.getConnectionInfo().getRemoteIp());
         OvsdbClient client = OvsdbConnectionService.getService().connect(ip,
                 ovsdbNode.getConnectionInfo().getRemotePort().getValue());
@@ -88,7 +149,10 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos
         // this method for us
         if (client != null) {
             putInstanceIdentifier(ovsdbNode.getConnectionInfo(), iid.firstIdentifierOf(Node.class));
-            connectedButCallBacksNotRegistered(client);
+            OvsdbConnectionInstance ovsdbConnectionInstance = connectedButCallBacksNotRegistered(client);
+
+            // Register Cluster Ownership for ConnectionInfo
+            registerEntityForOwnership(ovsdbConnectionInstance);
         } else {
             LOG.warn("Failed to connect to Ovsdb Node {}", ovsdbNode.getConnectionInfo());
         }
@@ -96,26 +160,37 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos
     }
 
     public void disconnect(OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
-        OvsdbClient client = getConnectionInstance(ovsdbNode.getConnectionInfo());
+        OvsdbConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo());
         if (client != null) {
             client.disconnect();
+
+            // Unregister Cluster Onwership for ConnectionInfo
+            unregisterEntityForOwnership(client);
+
             removeInstanceIdentifier(ovsdbNode.getConnectionInfo());
         }
     }
 
-    public void init(ConnectionInfo key) {
+/*    public void init(ConnectionInfo key) {
         OvsdbConnectionInstance client = getConnectionInstance(key);
+
+        // TODO (FF): make sure that this cluster instance is the 'entity owner' fo the given OvsdbConnectionInstance ?
+
         if (client != null) {
-            /*
+
              *  Note: registerCallbacks() is idemPotent... so if you call it repeatedly all is safe,
              *  it only registersCallbacks on the *first* call.
-             */
+
             client.registerCallbacks();
         }
     }
-
+*/
     @Override
     public void close() throws Exception {
+        if (ovsdbDeviceEntityOwnershipListener != null) {
+            ovsdbDeviceEntityOwnershipListener.close();
+        }
+
         for (OvsdbClient client: clients.values()) {
             client.disconnect();
         }
@@ -126,6 +201,11 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos
         clients.put(connectionInfo, instance);
     }
 
+    private void removeConnectionInstance(ConnectionInfo key) {
+        ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
+        clients.remove(connectionInfo);
+    }
+
     private void putInstanceIdentifier(ConnectionInfo key,InstanceIdentifier<Node> iid) {
         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
         instanceIdentifiers.put(connectionInfo, iid);
@@ -200,4 +280,195 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos
     public OvsdbClient getClient(Node node) {
         return getConnectionInstance(node);
     }
+
+    public Boolean getHasDeviceOwnership(ConnectionInfo connectionInfo) {
+        OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
+        if (ovsdbConnectionInstance == null) {
+            return Boolean.FALSE;
+        }
+        return ovsdbConnectionInstance.getHasDeviceOwnership();
+    }
+
+    public void setHasDeviceOwnership(ConnectionInfo connectionInfo, Boolean hasDeviceOwnership) {
+        OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
+        if (ovsdbConnectionInstance != null) {
+            ovsdbConnectionInstance.setHasDeviceOwnership(hasDeviceOwnership);
+        }
+    }
+
+    private void handleOwnershipChanged(EntityOwnershipChange ownershipChange) {
+        OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(ownershipChange.getEntity());
+        LOG.info("handleOwnershipChanged: {} event received for device {}",
+                ownershipChange, ovsdbConnectionInstance != null ? ovsdbConnectionInstance.getConnectionInfo()
+                        : "THAT'S NOT REGISTERED BY THIS SOUTHBOUND PLUGIN INSTANCE");
+
+        if (ovsdbConnectionInstance == null) {
+            if (ownershipChange.isOwner()) {
+                LOG.warn("handleOwnershipChanged: found no connection instance for {}", ownershipChange.getEntity());
+            } else {
+                // EntityOwnershipService sends notification to all the nodes, irrespective of whether
+                // that instance registered for the device ownership or not. It is to make sure that
+                // If all the controller instance that was connected to the device are down, so the
+                // running instance can clear up the operational data store even though it was not
+                // connected to the device.
+                LOG.debug("handleOwnershipChanged: found no connection instance for {}", ownershipChange.getEntity());
+            }
+
+            // 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());
+                // 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.
+                if (ownershipChange.wasOwner()) {
+                    cleanEntityOperationalData(ownershipChange.getEntity());
+                }
+                // If first cleanEntityOperationalData() was called, this call will be no-op.
+                cleanEntityOperationalData(ownershipChange.getEntity());
+            }
+            return;
+        }
+        //Connection detail need to be cached, irrespective of ownership result.
+        putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
+
+        if (ownershipChange.isOwner() == ovsdbConnectionInstance.getHasDeviceOwnership()) {
+            LOG.debug("handleOwnershipChanged: no change in ownership for {}. Ownership status is : {}",
+                    ovsdbConnectionInstance.getConnectionInfo(), ovsdbConnectionInstance.getHasDeviceOwnership());
+            return;
+        }
+
+        ovsdbConnectionInstance.setHasDeviceOwnership(ownershipChange.isOwner());
+        // You were not an owner, but now you are
+        if (ownershipChange.isOwner()) {
+            LOG.info("handleOwnershipChanged: *this* southbound plugin instance is owner of device {}",
+                    ovsdbConnectionInstance.getConnectionInfo());
+
+            //*this* instance of southbound plugin is owner of the device,
+            //so register for monitor callbacks
+            ovsdbConnectionInstance.registerCallbacks();
+
+        } else {
+            //You were owner of the device, but now you are not. With the current ownership
+            //grant mechanism, this scenario should not occur. Because this scenario will occur
+            //when this controller went down or switch flap the connection, but in both the case
+            //it will go through the re-registration process. We need to implement this condition
+            //when clustering service implement a ownership grant strategy which can revoke the
+            //device ownership for load balancing the devices across the instances.
+            //Once this condition occur, we should unregister the callback.
+            LOG.error("handleOwnershipChanged: *this* southbound plugin instance is no longer the owner of device {}",
+                    ovsdbConnectionInstance.getNodeId().getValue());
+        }
+    }
+
+    private void cleanEntityOperationalData(Entity entity) {
+
+        InstanceIdentifier<Node> nodeIid = (InstanceIdentifier<Node>) SouthboundUtil
+                .getInstanceIdentifierCodec().bindingDeserializer(entity.getId());
+
+        final ReadWriteTransaction transaction = db.newReadWriteTransaction();
+        Optional<Node> node = SouthboundUtil.readNode(transaction, nodeIid);
+        if (node.isPresent()) {
+            SouthboundUtil.deleteNode(transaction, nodeIid);
+        }
+    }
+
+    private OpenVSwitch getOpenVswitchTableEntry(OvsdbConnectionInstance connectionInstance) {
+        DatabaseSchema dbSchema = null;
+        OpenVSwitch openVSwitchRow = null;
+        try {
+            dbSchema = connectionInstance.getSchema(OvsdbSchemaContants.databaseName).get();
+        } catch (InterruptedException | ExecutionException e) {
+            LOG.warn("Not able to fetch schema for database {} from device {}",
+                    OvsdbSchemaContants.databaseName,connectionInstance.getConnectionInfo(),e);
+        }
+        if (dbSchema != null) {
+            GenericTableSchema openVSwitchSchema = TyperUtils.getTableSchema(dbSchema, OpenVSwitch.class);
+
+            List<String> openVSwitchTableColumn = new ArrayList<String>();
+            openVSwitchTableColumn.addAll(openVSwitchSchema.getColumns());
+            Select<GenericTableSchema> selectOperation = op.select(openVSwitchSchema);
+            selectOperation.setColumns(openVSwitchTableColumn);;
+
+            ArrayList<Operation> operations = new ArrayList<Operation>();
+            operations.add(selectOperation);
+            operations.add(op.comment("Fetching Open_VSwitch table rows"));
+            List<OperationResult> results = null;
+            try {
+                results = connectionInstance.transact(dbSchema, operations).get();
+                if (results != null ) {
+                    OperationResult selectResult = results.get(0);
+                    openVSwitchRow = TyperUtils.getTypedRowWrapper(
+                            dbSchema,OpenVSwitch.class,selectResult.getRows().get(0));
+
+                }
+            } catch (InterruptedException | ExecutionException e) {
+                LOG.warn("Not able to fetch OpenVswitch table row from device {}",
+                        connectionInstance.getConnectionInfo(),e);
+            }
+        }
+        return openVSwitchRow;
+    }
+    private Entity getEntityFromConnectionInstance(@Nonnull OvsdbConnectionInstance ovsdbConnectionInstance) {
+        YangInstanceIdentifier entityId = null;
+        InstanceIdentifier<Node> iid = ovsdbConnectionInstance.getInstanceIdentifier();;
+        if ( iid == null ) {
+            /* Switch initiated connection won't have iid, till it gets OpenVSwitch
+             * table update but update callback is always registered after ownership
+             * is granted. So we are explicitly fetch the row here to get the iid.
+             */
+            OpenVSwitch openvswitchRow = getOpenVswitchTableEntry(ovsdbConnectionInstance);
+            iid = SouthboundMapper.getInstanceIdentifier(openvswitchRow);
+            LOG.info("InstanceIdentifier {} generated for device "
+                    + "connection {}",iid,ovsdbConnectionInstance.getConnectionInfo());
+
+        }
+        entityId = SouthboundUtil.getInstanceIdentifierCodec().getYangInstanceIdentifier(iid);
+        Entity deviceEntity = new Entity(ENTITY_TYPE, entityId);
+        LOG.debug("Entity {} created for device connection {}",
+                deviceEntity, ovsdbConnectionInstance.getConnectionInfo());
+        return deviceEntity;
+    }
+
+    private OvsdbConnectionInstance getConnectionInstanceFromEntity(Entity entity) {
+        return entityConnectionMap.get(entity);
+    }
+
+    private void registerEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
+
+        Entity candidateEntity = getEntityFromConnectionInstance(ovsdbConnectionInstance);
+        entityConnectionMap.put(candidateEntity, ovsdbConnectionInstance);
+        ovsdbConnectionInstance.setConnectedEntity(candidateEntity);
+        try {
+            EntityOwnershipCandidateRegistration registration =
+                    entityOwnershipService.registerCandidate(candidateEntity);
+            ovsdbConnectionInstance.setDeviceOwnershipCandidateRegistration(registration);
+            LOG.info("OVSDB entity {} is registred for ownership.", candidateEntity);
+        } catch (CandidateAlreadyRegisteredException e) {
+            LOG.warn("OVSDB entity {} was already registered for {} ownership", candidateEntity, e);
+        }
+
+    }
+
+    private void unregisterEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
+        ovsdbConnectionInstance.closeDeviceOwnershipCandidateRegistration();
+        entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity());
+    }
+
+    private class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener {
+        private OvsdbConnectionManager cm;
+        private EntityOwnershipListenerRegistration listenerRegistration;
+
+        OvsdbDeviceEntityOwnershipListener(OvsdbConnectionManager cm, EntityOwnershipService entityOwnershipService) {
+            this.cm = cm;
+            listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
+        }
+        public void close() {
+            listenerRegistration.close();
+        }
+        @Override
+        public void ownershipChanged(EntityOwnershipChange ownershipChange) {
+            cm.handleOwnershipChanged(ownershipChange);
+        }
+    }
 }
index 34eb3d9671d45aa75e18205d490009db0099fff5..221f9f2eb69e0220eefacd81afccf9746c06286f 100644 (file)
@@ -15,6 +15,7 @@ import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 
+import org.opendaylight.controller.md.sal.binding.api.ClusteredDataChangeListener;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
@@ -42,7 +43,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
 import com.google.common.collect.Maps;
 
-public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseable {
+public class OvsdbDataChangeListener implements ClusteredDataChangeListener, AutoCloseable {
 
     private ListenerRegistration<DataChangeListener> registration;
     private OvsdbConnectionManager cm;
@@ -83,7 +84,7 @@ public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseabl
         // Finally disconnect if we need to
         disconnect(changes);
 
-        init(changes);
//       init(changes);
 
         LOG.trace("onDataChanged: exit");
     }
@@ -160,7 +161,7 @@ public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseabl
             }
         }
     }
-
+/*
     private void init(
             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
         for (Entry<InstanceIdentifier<?>, DataObject> created : changes.getCreatedData().entrySet()) {
@@ -171,7 +172,7 @@ public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseabl
         }
 
     }
-
+*/
     public Map<InstanceIdentifier<Node>,OvsdbConnectionInstance> connectionInstancesFromChanges(
             AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
         Map<InstanceIdentifier<Node>,OvsdbConnectionInstance> result =
@@ -212,7 +213,25 @@ public class OvsdbDataChangeListener implements DataChangeListener, AutoCloseabl
                 }
                 if (client != null) {
                     LOG.debug("Found client for {}", created.getValue());
-                    result.put((InstanceIdentifier<Node>) created.getKey(), client);
+                    /*
+                     * As of now data change sets are processed by single thread, so we can assume that device will
+                     * be connected and ownership will be decided before sending any instructions down to the device.
+                     * Note:Processing order in onDataChange() method should not change. If processing is changed to
+                     * use multiple thread, we might need to take care of corner cases, where ownership is not decided
+                     * but transaction are ready to go to switch. In that scenario, either we need to queue those task
+                     * till ownership is decided for that specific device.
+                     * Given that each DataChangeNotification is notified through separate thread, so we are already
+                     * multi threaded and i don't see any need to further parallelism per DataChange
+                     * notifications processing.
+                     */
+                    if ( cm.getHasDeviceOwnership(client.getMDConnectionInfo())) {
+                        LOG.debug("*this* instance of southbound plugin is an "
+                                + "owner of the device {}",created.getValue());
+                        result.put((InstanceIdentifier<Node>) created.getKey(), client);
+                    } else {
+                        LOG.debug("*this* instance of southbound plugin is not an "
+                                + "owner of the device {}",created.getValue());
+                    }
                 } else {
                     LOG.debug("Did not find client for {}",created.getValue());
                 }
index f0db949e8748b4fab43ea9c53b66531bfd3fb24d..86b76407931d4340f3d5be0b8dd53443028f4244 100644 (file)
@@ -456,4 +456,23 @@ public class SouthboundMapper {
         }
     }
 
+    public static InstanceIdentifier<Node> getInstanceIdentifier(OpenVSwitch ovs) {
+        InstanceIdentifier<Node> iid = null;
+        if (ovs.getExternalIdsColumn() != null
+                && ovs.getExternalIdsColumn().getData() != null
+                && ovs.getExternalIdsColumn().getData().containsKey(SouthboundConstants.IID_EXTERNAL_ID_KEY)) {
+            String iidString = ovs.getExternalIdsColumn().getData().get(SouthboundConstants.IID_EXTERNAL_ID_KEY);
+            iid = (InstanceIdentifier<Node>) SouthboundUtil.deserializeInstanceIdentifier(iidString);
+        } else {
+            String nodeString = SouthboundConstants.OVSDB_URI_PREFIX + "://" + SouthboundConstants.UUID + "/"
+                    + ovs.getUuid().toString();
+            NodeId nodeId = new NodeId(new Uri(nodeString));
+            NodeKey nodeKey = new NodeKey(nodeId);
+            iid = InstanceIdentifier.builder(NetworkTopology.class)
+                    .child(Topology.class,new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID))
+                    .child(Node.class,nodeKey)
+                    .build();
+        }
+        return iid;
+    }
 }
index 0ba25b4aec730e04a3616714d63b59ee8c05408a..a13e15c422fa0c2bb235b7ea5570166d7b5c8a6e 100644 (file)
@@ -9,6 +9,13 @@ package org.opendaylight.ovsdb.southbound;
 
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
+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.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
 import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
@@ -32,36 +39,49 @@ import com.google.common.util.concurrent.CheckedFuture;
 public class SouthboundProvider implements BindingAwareProvider, AutoCloseable {
 
     private static final Logger LOG = LoggerFactory.getLogger(SouthboundProvider.class);
+    private static final String ENTITY_TYPE = "ovsdb-southbound-provider";
 
     public static DataBroker getDb() {
         return db;
     }
 
-    //private DataBroker db;
     private static DataBroker db;
     private OvsdbConnectionManager cm;
-//    private OvsdbNodeDataChangeListener ovsdbNodeListener;
-//    private OvsdbManagedNodeDataChangeListener ovsdbManagedNodeListener;
-//    private OvsdbTerminationPointDataChangeListener ovsdbTerminationPointListener;
     private TransactionInvoker txInvoker;
     private OvsdbDataChangeListener ovsdbDataChangeListener;
+    private EntityOwnershipService entityOwnershipService;
+    private EntityOwnershipCandidateRegistration registration;
+    private SouthboundPluginInstanceEntityOwnershipListener providerOwnershipChangeListener;
+    private OvsdbConnection ovsdbConnection;
 
 
+    public SouthboundProvider(
+            EntityOwnershipService entityOwnershipServiceDependency) {
+        this.entityOwnershipService = entityOwnershipServiceDependency;
+        registration = null;
+    }
+
     @Override
     public void onSessionInitiated(ProviderContext session) {
         LOG.info("SouthboundProvider Session Initiated");
         db = session.getSALService(DataBroker.class);
         this.txInvoker = new TransactionInvokerImpl(db);
-        cm = new OvsdbConnectionManager(db,txInvoker);
+        cm = new OvsdbConnectionManager(db,txInvoker,entityOwnershipService);
         ovsdbDataChangeListener = new OvsdbDataChangeListener(db,cm);
-//        ovsdbNodeListener = new OvsdbNodeDataChangeListener(db, cm);
-//        ovsdbManagedNodeListener = new OvsdbManagedNodeDataChangeListener(db, cm);
-//        ovsdbTerminationPointListener = new OvsdbTerminationPointDataChangeListener(db, cm);
-        initializeOvsdbTopology(LogicalDatastoreType.OPERATIONAL);
-        initializeOvsdbTopology(LogicalDatastoreType.CONFIGURATION);
-        OvsdbConnection ovsdbConnection = new OvsdbConnectionService();
-        ovsdbConnection.registerConnectionListener(cm);
-        ovsdbConnection.startOvsdbManager(SouthboundConstants.DEFAULT_OVSDB_PORT);
+
+        //Register listener for entityOnwership changes
+        providerOwnershipChangeListener =
+                new SouthboundPluginInstanceEntityOwnershipListener(this,this.entityOwnershipService);
+        entityOwnershipService.registerListener(ENTITY_TYPE,providerOwnershipChangeListener);
+
+        //register instance entity to get the ownership of the provider
+        Entity instanceEntity = new Entity(ENTITY_TYPE, ENTITY_TYPE);
+        try {
+            registration = entityOwnershipService.registerCandidate(instanceEntity);
+        } catch (CandidateAlreadyRegisteredException e) {
+            LOG.warn("OVSDB Southbound Provider instance entity {} was already "
+                    + "registered for {} ownership", instanceEntity, e);
+        }
     }
 
     @Override
@@ -69,17 +89,16 @@ public class SouthboundProvider implements BindingAwareProvider, AutoCloseable {
         LOG.info("SouthboundProvider Closed");
         cm.close();
         ovsdbDataChangeListener.close();
-//        ovsdbNodeListener.close();
-//        ovsdbManagedNodeListener.close();
-//        ovsdbTerminationPointListener.close();
+        registration.close();
+        providerOwnershipChangeListener.close();
     }
 
     private void initializeOvsdbTopology(LogicalDatastoreType type) {
         InstanceIdentifier<Topology> path = InstanceIdentifier
                 .create(NetworkTopology.class)
                 .child(Topology.class, new TopologyKey(SouthboundConstants.OVSDB_TOPOLOGY_ID));
+        initializeTopology(type);
         ReadWriteTransaction transaction = db.newReadWriteTransaction();
-        initializeTopology(transaction,type);
         CheckedFuture<Optional<Topology>, ReadFailedException> ovsdbTp = transaction.read(type, path);
         try {
             if (!ovsdbTp.get().isPresent()) {
@@ -95,16 +114,57 @@ public class SouthboundProvider implements BindingAwareProvider, AutoCloseable {
         }
     }
 
-    private void initializeTopology(ReadWriteTransaction transaction, LogicalDatastoreType type) {
+    private void initializeTopology(LogicalDatastoreType type) {
+        ReadWriteTransaction transaction = db.newReadWriteTransaction();
         InstanceIdentifier<NetworkTopology> path = InstanceIdentifier.create(NetworkTopology.class);
         CheckedFuture<Optional<NetworkTopology>, ReadFailedException> topology = transaction.read(type,path);
         try {
             if (!topology.get().isPresent()) {
                 NetworkTopologyBuilder ntb = new NetworkTopologyBuilder();
                 transaction.put(type,path,ntb.build());
+                transaction.submit();
+            } else {
+                transaction.cancel();
             }
         } catch (Exception e) {
             LOG.error("Error initializing ovsdb topology {}",e);
         }
     }
+
+    public void handleOwnershipChange(EntityOwnershipChange ownershipChange) {
+        if (ownershipChange.isOwner()) {
+            LOG.info("*This* instance of OVSDB southbound provider is set as a MASTER instance");
+            LOG.info("Initialize OVSDB topology {} in operational and config data store if not already present"
+                    ,SouthboundConstants.OVSDB_TOPOLOGY_ID);
+            initializeOvsdbTopology(LogicalDatastoreType.OPERATIONAL);
+            initializeOvsdbTopology(LogicalDatastoreType.CONFIGURATION);
+        } else {
+            LOG.info("*This* instance of OVSDB southbound provider is set as a SLAVE instance");
+        }
+        if (ovsdbConnection == null) {
+            ovsdbConnection = new OvsdbConnectionService();
+            ovsdbConnection.registerConnectionListener(cm);
+            ovsdbConnection.startOvsdbManager(SouthboundConstants.DEFAULT_OVSDB_PORT);
+        }
+    }
+
+    private class SouthboundPluginInstanceEntityOwnershipListener implements EntityOwnershipListener {
+        private SouthboundProvider sp;
+        private EntityOwnershipListenerRegistration listenerRegistration;
+
+        SouthboundPluginInstanceEntityOwnershipListener(SouthboundProvider sp,
+                EntityOwnershipService entityOwnershipService) {
+            this.sp = sp;
+            listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
+        }
+
+        public void close() {
+            this.listenerRegistration.close();
+        }
+        @Override
+        public void ownershipChanged(EntityOwnershipChange ownershipChange) {
+            sp.handleOwnershipChange(ownershipChange);
+        }
+    }
+
 }
index 3e2ecb9743844d0978e2789dd7d6e969a2f3aa32..4786871edff3d167e83549d18e3a067eee0ef875 100644 (file)
@@ -10,14 +10,17 @@ package org.opendaylight.ovsdb.southbound;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.util.concurrent.CheckedFuture;
+
 import java.net.InetAddress;
 import java.net.NetworkInterface;
 import java.util.Enumeration;
+
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeRef;
@@ -42,6 +45,10 @@ public class SouthboundUtil {
         instanceIdentifierCodec = iidc;
     }
 
+    public static InstanceIdentifierCodec getInstanceIdentifierCodec() {
+        return instanceIdentifierCodec;
+    }
+
     public static String serializeInstanceIdentifier(InstanceIdentifier<?> iid) {
         return instanceIdentifierCodec.serialize(iid);
     }
@@ -110,6 +117,20 @@ public class SouthboundUtil {
         return node;
     }
 
+    public static <D extends org.opendaylight.yangtools.yang.binding.DataObject> boolean deleteNode(
+            ReadWriteTransaction transaction, final InstanceIdentifier<D> connectionIid) {
+        boolean result = false;
+        transaction.delete(LogicalDatastoreType.OPERATIONAL, connectionIid);
+        CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
+        try {
+            future.checkedGet();
+            result = true;
+        } catch (TransactionCommitFailedException e) {
+            LOG.warn("Failed to delete {} ", connectionIid, e);
+        }
+        return result;
+    }
+
     private static String getLocalControllerHostIpAddress() {
         String ipaddress = null;
         try {
index 1f6fae12ab6c339f6a252dbcfd9bca2f8f74b74a..5014d144549a43120d1f984a965540077020b698 100644 (file)
@@ -31,7 +31,7 @@ public class SouthboundImplModule extends org.opendaylight.yang.gen.v1.urn.opend
     public java.lang.AutoCloseable createInstance() {
         SouthboundUtil.setInstanceIdentifierCodec(new InstanceIdentifierCodec(getSchemaServiceDependency(),
                 getBindingNormalizedNodeSerializerDependency()));
-        SouthboundProvider provider = new SouthboundProvider();
+        SouthboundProvider provider = new SouthboundProvider(getClusteringEntityOwnershipServiceDependency());
         getBrokerDependency().registerProvider(provider);
         return provider;
     }
index d5e30f570cfb42731941df75247273d4b871286a..89a2eac74f8fe0e88981eb50bd0d79e46a257e35 100644 (file)
@@ -6,6 +6,7 @@ module southbound-impl {
     import config { prefix config; revision-date 2013-04-05; }
     import opendaylight-md-sal-binding { prefix md-sal-binding; revision-date 2013-10-28;}
     import opendaylight-md-sal-dom {prefix dom; revision-date 2013-10-28;}
+    import opendaylight-entity-ownership-service {prefix eos; revision-date 2015-08-10;}
 
     description
         "Service definition for southbound project";
@@ -47,6 +48,14 @@ module southbound-impl {
                     }
                 }
             }
+            container clustering-entity-ownership-service {
+                uses config:service-ref {
+                    refine type {
+                        mandatory true;
+                        config:required-identity eos:entity-ownership-service;
+                    }
+                }
+            }
         }
     }
 }
index 39087b80487424b1dde12d41851f3495ed4d70a2..9cbb99dc30b269365d87b0ac914cf276ea0c8da2 100644 (file)
@@ -73,9 +73,9 @@ public class OvsdbConnectionInstanceTest {
     public void setUp() throws Exception {
         ovsdbConnectionInstance = PowerMockito.mock(OvsdbConnectionInstance.class, Mockito.CALLS_REAL_METHODS);
         MemberModifier.field(OvsdbConnectionInstance.class, "txInvoker").set(ovsdbConnectionInstance, txInvoker);
-        MemberModifier.field(OvsdbConnectionInstance.class, "key").set(ovsdbConnectionInstance, key);
         MemberModifier.field(OvsdbConnectionInstance.class, "connectionInfo").set(ovsdbConnectionInstance, key);
         MemberModifier.field(OvsdbConnectionInstance.class, "instanceIdentifier").set(ovsdbConnectionInstance, instanceIdentifier);
+        MemberModifier.field(OvsdbConnectionInstance.class, "hasDeviceOwnership").set(ovsdbConnectionInstance, false);
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -288,7 +288,7 @@ public class OvsdbConnectionInstanceTest {
 
         //test setMDConnectionInfo()
         ovsdbConnectionInstance.setMDConnectionInfo(key);
-        assertEquals("Error, incorrect ConnectionInfo", key, Whitebox.getInternalState(ovsdbConnectionInstance, "key"));
+        assertEquals("Error, incorrect ConnectionInfo", key, Whitebox.getInternalState(ovsdbConnectionInstance, "connectionInfo"));
 
         //test getInstanceIdentifier()
         assertEquals("Error, incorrect instanceIdentifier", instanceIdentifier, ovsdbConnectionInstance.getInstanceIdentifier());
index 0e48534ee1c24aa36ffe2108d793c4b222a9d81c..f9a8239cd89cfe0b5aa8062419a1fa98d88c14a6 100644 (file)
@@ -13,11 +13,15 @@ import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
+import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
 import org.opendaylight.ovsdb.lib.OvsdbClient;
 import org.opendaylight.ovsdb.lib.OvsdbConnection;
 import org.opendaylight.ovsdb.lib.impl.OvsdbConnectionService;
@@ -46,8 +50,11 @@ public class OvsdbConnectionManagerTest {
     @Mock private OvsdbConnectionManager ovsdbConnectionManager;
     @Mock private DataBroker db;
     @Mock private TransactionInvoker txInvoker;
+    @Mock private EntityOwnershipService entityOwnershipService;
     private Map<ConnectionInfo,OvsdbConnectionInstance> clients;
     private Map<ConnectionInfo,InstanceIdentifier<Node>> instanceIdentifiers;
+    private Map<Entity, OvsdbConnectionInstance> entityConnectionMap;
+
     @Mock private InstanceIdentifier<Node> iid;
 
     @Before
@@ -55,16 +62,26 @@ public class OvsdbConnectionManagerTest {
         ovsdbConnectionManager = PowerMockito.mock(OvsdbConnectionManager.class, Mockito.CALLS_REAL_METHODS);
         MemberModifier.field(OvsdbConnectionManager.class, "db").set(ovsdbConnectionManager, db);
         MemberModifier.field(OvsdbConnectionManager.class, "txInvoker").set(ovsdbConnectionManager, txInvoker);
+        MemberModifier.field(OvsdbConnectionManager.class, "entityOwnershipService").set(ovsdbConnectionManager, entityOwnershipService);
+        entityConnectionMap = new ConcurrentHashMap<>();
     }
     @Test
-    public void testConnected() {
+    public void testConnected() throws Exception {
         OvsdbConnectionInstance client = mock(OvsdbConnectionInstance.class);
         OvsdbClient externalClient = mock(OvsdbClient.class);
         MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "connectedButCallBacksNotRegistered", OvsdbClient.class));
         when(ovsdbConnectionManager.connectedButCallBacksNotRegistered(any(OvsdbClient.class))).thenReturn(client);
         doNothing().when(client).registerCallbacks();
+
+        //TODO: Write unit tests for EntityOwnershipService
+        InstanceIdentifier<Node> iid = mock(InstanceIdentifier.class);
+        when(client.getInstanceIdentifier()).thenReturn(iid);
+        MemberModifier.field(OvsdbConnectionManager.class, "entityConnectionMap").set(ovsdbConnectionManager, entityConnectionMap);
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "getEntityFromConnectionInstance", OvsdbConnectionInstance.class));
+
+        //TODO: Write unit tests for entity ownership service related code.
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "registerEntityForOwnership", OvsdbConnectionInstance.class));
         ovsdbConnectionManager.connected(externalClient);
-        verify(client).registerCallbacks();
     }
 
     @SuppressWarnings("unchecked")
@@ -86,6 +103,9 @@ public class OvsdbConnectionManagerTest {
         InstanceIdentifier<Node> iid = mock(InstanceIdentifier.class);
         when(ovsdbConnectionManager.getInstanceIdentifier(key)).thenReturn(iid);
 
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "getConnectionInstance", ConnectionInfo.class));
+        when(ovsdbConnectionManager.getConnectionInstance(key)).thenReturn(null);
+
         MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "putConnectionInstance", ConnectionInfo.class, OvsdbConnectionInstance.class));
         doNothing().when(client).createTransactInvokers();
         PowerMockito.whenNew(OvsdbConnectionInstance.class).
@@ -107,13 +127,18 @@ public class OvsdbConnectionManagerTest {
         PowerMockito.mockStatic(SouthboundMapper.class);
         when(SouthboundMapper.createConnectionInfo(any(OvsdbClient.class))).thenReturn(key);
 
+        clients = new ConcurrentHashMap<ConnectionInfo,OvsdbConnectionInstance>();
+        clients.put(key, ovsdbConnectionInstance);
+        MemberModifier.field(OvsdbConnectionManager.class, "clients").set(ovsdbConnectionManager, clients);
+
         MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "getConnectionInstance", ConnectionInfo.class));
         when(ovsdbConnectionManager.getConnectionInstance(any(ConnectionInfo.class))).thenReturn(ovsdbConnectionInstance);
         doNothing().when(txInvoker).invoke(any(TransactionCommand.class));
 
-        clients = new ConcurrentHashMap<ConnectionInfo,OvsdbConnectionInstance>();
-        clients.put(key, ovsdbConnectionInstance);
-        MemberModifier.field(OvsdbConnectionManager.class, "clients").set(ovsdbConnectionManager, clients);
+        when(SouthboundMapper.suppressLocalIpPort(any(ConnectionInfo.class))).thenReturn(key);
+
+      //TODO: Write unit tests for EntityOwnershipService
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "unregisterEntityForOwnership", OvsdbConnectionInstance.class));
         ovsdbConnectionManager.disconnected(client);
         Map<ConnectionInfo,OvsdbConnectionInstance> testClients = Whitebox.getInternalState(ovsdbConnectionManager, "clients");
         assertEquals("Error, size of the hashmap is incorrect", 0, testClients.size());
@@ -129,10 +154,14 @@ public class OvsdbConnectionManagerTest {
         when(ovsdbConnectionManager.getConnectionInstance(any(ConnectionInfo.class))).thenReturn(ovsdbConnectionInstance);
 
         MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "removeInstanceIdentifier", ConnectionInfo.class));
+
+        //TODO: Write unit tests for entity ownership service related code.
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "unregisterEntityForOwnership", OvsdbConnectionInstance.class));
         ovsdbConnectionManager.disconnect(ovsdbNode);
         verify((OvsdbClient)ovsdbConnectionInstance).disconnect();
     }
 
+    @Ignore
     @Test
     public void testInit() {
         ConnectionInfo key = mock(ConnectionInfo.class);
@@ -141,7 +170,7 @@ public class OvsdbConnectionManagerTest {
         when(ovsdbConnectionManager.getConnectionInstance(any(ConnectionInfo.class))).thenReturn(ovsdbConnectionInstance);
 
         //client not null
-        ovsdbConnectionManager.init(key);
+        // ovsdbConnectionManager.init(key);
         verify(ovsdbConnectionInstance).registerCallbacks();
     }
 
@@ -260,6 +289,12 @@ public class OvsdbConnectionManagerTest {
         OvsdbConnectionInstance ovsdbConnectionInstance = mock(OvsdbConnectionInstance.class);
         when(ovsdbConnectionManager.connectedButCallBacksNotRegistered(any(OvsdbClient.class))).thenReturn(ovsdbConnectionInstance);
 
+        InstanceIdentifier<Node> iid = mock(InstanceIdentifier.class);
+        when(ovsdbConnectionInstance.getInstanceIdentifier()).thenReturn(iid);
+        MemberModifier.field(OvsdbConnectionManager.class, "entityConnectionMap").set(ovsdbConnectionManager, entityConnectionMap);
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "getEntityFromConnectionInstance", OvsdbConnectionInstance.class));
+        //TODO: Write unit tests for entity ownership service related code.
+        MemberModifier.suppress(MemberMatcher.method(OvsdbConnectionManager.class, "registerEntityForOwnership", OvsdbConnectionInstance.class));
         assertEquals("ERROR", client, ovsdbConnectionManager.connect(PowerMockito.mock(InstanceIdentifier.class), ovsdbNode));
     }
 }
index 9760b103abbf4ea26a8af437572083548c5af431..3025fddeada47cde9f49500933f4d48ca30d3e0b 100644 (file)
@@ -84,7 +84,6 @@ public class OvsdbDataChangeListenerTest {
         MemberModifier.suppress(MemberMatcher.method(OvsdbDataChangeListener.class, "updateConnections", AsyncDataChangeEvent.class));
         MemberModifier.suppress(MemberMatcher.method(OvsdbDataChangeListener.class, "updateData", AsyncDataChangeEvent.class));
         MemberModifier.suppress(MemberMatcher.method(OvsdbDataChangeListener.class, "disconnect", AsyncDataChangeEvent.class));
-        MemberModifier.suppress(MemberMatcher.method(OvsdbDataChangeListener.class, "init", AsyncDataChangeEvent.class));
 
         //iid null case
         when(cm.getInstanceIdentifier(any(ConnectionInfo.class))).thenReturn(null);
@@ -94,7 +93,6 @@ public class OvsdbDataChangeListenerTest {
         PowerMockito.verifyPrivate(ovsdbDataChangeListener, times(1)).invoke("updateConnections", any(AsyncDataChangeEvent.class));
         PowerMockito.verifyPrivate(ovsdbDataChangeListener, times(1)).invoke("updateData", any(AsyncDataChangeEvent.class));
         PowerMockito.verifyPrivate(ovsdbDataChangeListener, times(1)).invoke("disconnect", any(AsyncDataChangeEvent.class));
-        PowerMockito.verifyPrivate(ovsdbDataChangeListener, times(1)).invoke("init", any(AsyncDataChangeEvent.class));
     }
 
     @SuppressWarnings("unchecked")
@@ -146,11 +144,13 @@ public class OvsdbDataChangeListenerTest {
         Whitebox.invokeMethod(ovsdbDataChangeListener, "connect", changes);
         verify(cm).disconnect(any(OvsdbNodeAugmentation.class));
 
-        //test init
-        ConnectionInfo connectionInfo = mock(ConnectionInfo.class);
-        when(ovsdbNode.getConnectionInfo()).thenReturn(connectionInfo);
-        Whitebox.invokeMethod(ovsdbDataChangeListener, "init", changes);
-        verify(cm).init(any(ConnectionInfo.class));
+        // test init
+        /**
+         * ConnectionInfo connectionInfo = mock(ConnectionInfo.class);
+         * when(ovsdbNode.getConnectionInfo()).thenReturn(connectionInfo);
+         * Whitebox.invokeMethod(ovsdbDataChangeListener, "init", changes);
+         * verify(cm).init(any(ConnectionInfo.class));
+         */
     }
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
@@ -212,6 +212,7 @@ public class OvsdbDataChangeListenerTest {
         when(cm.getConnectionInstance(any(OvsdbBridgeAugmentation.class))).thenReturn(client);
         OvsdbBridgeAugmentation bridge = mock(OvsdbBridgeAugmentation.class);
         when(node.getAugmentation(OvsdbBridgeAugmentation.class)).thenReturn(bridge);
+        when(cm.getHasDeviceOwnership(any(ConnectionInfo.class))).thenReturn(true);
         assertEquals("Error returning correct Map", testResultMap, ovsdbDataChangeListener.connectionInstancesFromMap(map));
         verify(cm).getConnectionInstance(any(OvsdbBridgeAugmentation.class));