X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=southbound%2Fsouthbound-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fovsdb%2Fsouthbound%2FOvsdbConnectionManager.java;h=a3ee6cd37ab5ae91d695ad4d23f941670e1a9161;hb=82997ed5ede5fad4508fa2f2fcbaaf673b983699;hp=ecfedfe48f1b787f71331f454c1dc944ad72898e;hpb=bdfed1e23ae795a74910b94f913f44bb2672da45;p=ovsdb.git diff --git a/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java b/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java index ecfedfe48..a3ee6cd37 100644 --- a/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java +++ b/southbound/southbound-impl/src/main/java/org/opendaylight/ovsdb/southbound/OvsdbConnectionManager.java @@ -18,7 +18,10 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; 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; @@ -42,12 +45,17 @@ 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.reconciliation.ReconciliationManager; +import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationTask; +import org.opendaylight.ovsdb.southbound.reconciliation.connection.ConnectionReconciliationTask; import org.opendaylight.ovsdb.southbound.transactions.md.OvsdbNodeRemoveCommand; +import org.opendaylight.ovsdb.southbound.transactions.md.TransactionCommand; 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.OvsdbBridgeAugmentation; 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.ManagedNodeEntry; 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; @@ -73,6 +81,7 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos private EntityOwnershipService entityOwnershipService; private OvsdbDeviceEntityOwnershipListener ovsdbDeviceEntityOwnershipListener; private OvsdbConnection ovsdbConnection; + private final ReconciliationManager reconciliationManager; public OvsdbConnectionManager(DataBroker db,TransactionInvoker txInvoker, EntityOwnershipService entityOwnershipService, @@ -82,15 +91,29 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos this.entityOwnershipService = entityOwnershipService; this.ovsdbDeviceEntityOwnershipListener = new OvsdbDeviceEntityOwnershipListener(this, entityOwnershipService); this.ovsdbConnection = ovsdbConnection; + this.reconciliationManager = new ReconciliationManager(db); } @Override public void connected(@Nonnull final OvsdbClient externalClient) { + LOG.info("Library connected {} from {}:{} to {}:{}", + externalClient.getConnectionInfo().getType(), + externalClient.getConnectionInfo().getRemoteAddress(), + externalClient.getConnectionInfo().getRemotePort(), + externalClient.getConnectionInfo().getLocalAddress(), + externalClient.getConnectionInfo().getLocalPort()); + List databases = new ArrayList<>(); + try { + databases = externalClient.getDatabases().get(); + } catch (InterruptedException | ExecutionException e) { + LOG.warn("Unable to fetch database list"); + } - OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient); - - // Register Cluster Ownership for ConnectionInfo - registerEntityForOwnership(client); + if(databases.contains(SouthboundConstants.OPEN_V_SWITCH)) { + OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient); + // Register Cluster Ownership for ConnectionInfo + registerEntityForOwnership(client); + } } public OvsdbConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) { @@ -125,9 +148,12 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos @Override public void disconnected(OvsdbClient client) { - LOG.info("OVSDB Disconnected from {}:{}. Cleaning up the operational data store" - ,client.getConnectionInfo().getRemoteAddress(), - client.getConnectionInfo().getRemotePort()); + LOG.info("Library disconnected {} from {}:{} to {}:{}. Cleaning up the operational data store", + client.getConnectionInfo().getType(), + client.getConnectionInfo().getRemoteAddress(), + client.getConnectionInfo().getRemotePort(), + client.getConnectionInfo().getLocalAddress(), + client.getConnectionInfo().getLocalPort()); ConnectionInfo key = SouthboundMapper.createConnectionInfo(client); OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key); if (ovsdbConnectionInstance != null) { @@ -140,14 +166,24 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos txInvoker.invoke(new OvsdbNodeRemoveCommand(ovsdbConnectionInstance, null, null)); removeConnectionInstance(key); + + //Controller initiated connection can be terminated from switch side. + //So cleanup the instance identifier cache. + removeInstanceIdentifier(key); + retryConnection(ovsdbConnectionInstance.getInstanceIdentifier(), + ovsdbConnectionInstance.getOvsdbNodeAugmentation(), + ConnectionReconciliationTriggers.ON_DISCONNECT); } else { LOG.warn("disconnected : Connection instance not found for OVSDB Node {} ", key); } - LOG.trace("OvsdbConnectionManager: disconnected exit"); + LOG.trace("OvsdbConnectionManager: exit disconnected client: {}", client); + } public OvsdbClient connect(InstanceIdentifier iid, OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException { + LOG.info("Connecting to {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo())); + // TODO handle case where we already have a connection // TODO use transaction chains to handle ordering issues between disconnected // TODO and connected when writing to the operational store @@ -170,6 +206,7 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos } public void disconnect(OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException { + LOG.info("Disconnecting from {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo())); OvsdbConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo()); if (client != null) { // Unregister Cluster Onwership for ConnectionInfo @@ -198,7 +235,7 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos } */ @Override - public void close() throws Exception { + public void close() { if (ovsdbDeviceEntityOwnershipListener != null) { ovsdbDeviceEntityOwnershipListener.close(); } @@ -268,7 +305,7 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos LogicalDatastoreType.OPERATIONAL, nodePath); transaction.close(); Optional optional = nodeFuture.get(); - if (optional != null && optional.isPresent() && optional.get() instanceof Node) { + if (optional != null && optional.isPresent() && optional.get() != null) { return this.getConnectionInstance(optional.get()); } else { LOG.warn("Found non-topological node {} on path {}",optional); @@ -300,13 +337,20 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos return ovsdbConnectionInstance.getHasDeviceOwnership(); } - public void setHasDeviceOwnership(ConnectionInfo connectionInfo, Boolean hasDeviceOwnership) { - OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo); - if (ovsdbConnectionInstance != null) { - ovsdbConnectionInstance.setHasDeviceOwnership(hasDeviceOwnership); - } + public void reconcileConnection(InstanceIdentifier iid, OvsdbNodeAugmentation ovsdbNode){ + this.retryConnection(iid, ovsdbNode, + ConnectionReconciliationTriggers.ON_CONTROLLER_INITIATED_CONNECTION_FAILURE); + } + public void stopConnectionReconciliationIfActive(InstanceIdentifier iid, OvsdbNodeAugmentation ovsdbNode) { + final ReconciliationTask task = new ConnectionReconciliationTask( + reconciliationManager, + this, + iid, + ovsdbNode); + reconciliationManager.dequeue(task); + } private void handleOwnershipChanged(EntityOwnershipChange ownershipChange) { OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(ownershipChange.getEntity()); LOG.debug("handleOwnershipChanged: {} event received for device {}", @@ -371,14 +415,36 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos private void cleanEntityOperationalData(Entity entity) { - InstanceIdentifier nodeIid = (InstanceIdentifier) SouthboundUtil - .getInstanceIdentifierCodec().bindingDeserializer(entity.getId()); + //Do explicit cleanup rather than using OvsdbNodeRemoveCommand, because there + // are chances that other controller instance went down abruptly and it does + // not clear manager entry, which OvsdbNodeRemoveCommand look for before cleanup. + + @SuppressWarnings("unchecked") final InstanceIdentifier nodeIid = + (InstanceIdentifier) SouthboundUtil + .getInstanceIdentifierCodec().bindingDeserializer(entity.getId()); + + txInvoker.invoke(new TransactionCommand() { + @Override + public void execute(ReadWriteTransaction transaction) { + Optional ovsdbNodeOpt = SouthboundUtil.readNode(transaction, nodeIid); + if (ovsdbNodeOpt.isPresent()) { + Node ovsdbNode = ovsdbNodeOpt.get(); + OvsdbNodeAugmentation nodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class); + if (nodeAugmentation != null) { + if (nodeAugmentation.getManagedNodeEntry() != null) { + for (ManagedNodeEntry managedNode : nodeAugmentation.getManagedNodeEntry()) { + transaction.delete( + LogicalDatastoreType.OPERATIONAL, managedNode.getBridgeRef().getValue()); + } + } else { + LOG.debug("{} had no managed nodes", ovsdbNode.getNodeId().getValue()); + } + } + transaction.delete(LogicalDatastoreType.OPERATIONAL, nodeIid); + } + } + }); - final ReadWriteTransaction transaction = db.newReadWriteTransaction(); - Optional node = SouthboundUtil.readNode(transaction, nodeIid); - if (node.isPresent()) { - SouthboundUtil.deleteNode(transaction, nodeIid); - } } private OpenVSwitch getOpenVswitchTableEntry(OvsdbConnectionInstance connectionInstance) { @@ -475,6 +541,54 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity()); } + private void retryConnection(final InstanceIdentifier iid, final OvsdbNodeAugmentation ovsdbNode, + ConnectionReconciliationTriggers trigger) { + final ReconciliationTask task = new ConnectionReconciliationTask( + reconciliationManager, + this, + iid, + ovsdbNode); + + if(reconciliationManager.isEnqueued(task)){ + return; + } + switch(trigger){ + case ON_CONTROLLER_INITIATED_CONNECTION_FAILURE: + reconciliationManager.enqueueForRetry(task); + break; + case ON_DISCONNECT: + { + ReadOnlyTransaction tx = db.newReadOnlyTransaction(); + CheckedFuture, ReadFailedException> readNodeFuture = + tx.read(LogicalDatastoreType.CONFIGURATION, iid); + + final OvsdbConnectionManager connectionManager = this; + Futures.addCallback(readNodeFuture, new FutureCallback>() { + @Override + public void onSuccess(@Nullable Optional node) { + if (node.isPresent()) { + LOG.info("Disconnected/Failed connection {} was controller initiated, attempting " + + "reconnection", ovsdbNode.getConnectionInfo()); + reconciliationManager.enqueue(task); + + } else { + LOG.debug("Connection {} was switch initiated, no reconciliation is required" + , ovsdbNode.getConnectionInfo()); + } + } + + @Override + public void onFailure(Throwable t) { + LOG.warn("Read Config/DS for Node failed! {}", iid, t); + } + }); + break; + } + default: + break; + } + } + private class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener { private OvsdbConnectionManager cm; private EntityOwnershipListenerRegistration listenerRegistration; @@ -491,4 +605,18 @@ public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoClos cm.handleOwnershipChanged(ownershipChange); } } + + private enum ConnectionReconciliationTriggers { + /* + Reconciliation trigger for scenario where controller's attempt + to connect to switch fails on config data store notification + */ + ON_CONTROLLER_INITIATED_CONNECTION_FAILURE, + + /* + Reconciliation trigger for the scenario where controller + initiated connection disconnects. + */ + ON_DISCONNECT + } }