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);
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;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
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;
}
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() {
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();
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(
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)
}
}
- 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;
}
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;
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);
<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>
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);
InstanceIdentifier<?> iid = bindingNormalizedNodeSerializer.fromYangInstanceIdentifier(yangIID);
return iid;
}
-
}
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;
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;
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;
}
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);
+ }
+ }
}
*/
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;
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;
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");
}
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());
// 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());
}
}
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();
}
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);
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);
+ }
+ }
}
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;
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;
// Finally disconnect if we need to
disconnect(changes);
- init(changes);
+ // init(changes);
LOG.trace("onDataChanged: exit");
}
}
}
}
-
+/*
private void init(
AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
for (Entry<InstanceIdentifier<?>, DataObject> created : changes.getCreatedData().entrySet()) {
}
}
-
+*/
public Map<InstanceIdentifier<Node>,OvsdbConnectionInstance> connectionInstancesFromChanges(
AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> changes) {
Map<InstanceIdentifier<Node>,OvsdbConnectionInstance> result =
}
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());
}
}
}
+ 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;
+ }
}
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;
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
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()) {
}
}
- 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);
+ }
+ }
+
}
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;
instanceIdentifierCodec = iidc;
}
+ public static InstanceIdentifierCodec getInstanceIdentifierCodec() {
+ return instanceIdentifierCodec;
+ }
+
public static String serializeInstanceIdentifier(InstanceIdentifier<?> iid) {
return instanceIdentifierCodec.serialize(iid);
}
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 {
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;
}
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";
}
}
}
+ container clustering-entity-ownership-service {
+ uses config:service-ref {
+ refine type {
+ mandatory true;
+ config:required-identity eos:entity-ownership-service;
+ }
+ }
+ }
}
}
}
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" })
//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());
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;
@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
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")
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).
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());
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);
when(ovsdbConnectionManager.getConnectionInstance(any(ConnectionInfo.class))).thenReturn(ovsdbConnectionInstance);
//client not null
- ovsdbConnectionManager.init(key);
+ // ovsdbConnectionManager.init(key);
verify(ovsdbConnectionInstance).registerCallbacks();
}
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));
}
}
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);
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")
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" })
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));