2 * Copyright © 2014, 2017 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.ovsdb.southbound;
10 import static java.util.Objects.requireNonNull;
11 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.util.concurrent.FluentFuture;
15 import com.google.common.util.concurrent.FutureCallback;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import java.net.ConnectException;
18 import java.net.InetAddress;
19 import java.net.UnknownHostException;
20 import java.util.ArrayList;
21 import java.util.List;
23 import java.util.Optional;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.concurrent.ExecutionException;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.TimeoutException;
29 import org.eclipse.jdt.annotation.NonNull;
30 import org.opendaylight.mdsal.binding.api.DataBroker;
31 import org.opendaylight.mdsal.binding.api.ReadTransaction;
32 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
33 import org.opendaylight.mdsal.eos.binding.api.Entity;
34 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipListener;
35 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
36 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
37 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
38 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipStateChange;
39 import org.opendaylight.ovsdb.lib.OvsdbClient;
40 import org.opendaylight.ovsdb.lib.OvsdbConnection;
41 import org.opendaylight.ovsdb.lib.OvsdbConnectionListener;
42 import org.opendaylight.ovsdb.lib.operations.Operation;
43 import org.opendaylight.ovsdb.lib.operations.OperationResult;
44 import org.opendaylight.ovsdb.lib.operations.Select;
45 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
46 import org.opendaylight.ovsdb.lib.schema.typed.TypedDatabaseSchema;
47 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
48 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationManager;
49 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationTask;
50 import org.opendaylight.ovsdb.southbound.reconciliation.configuration.BridgeConfigReconciliationTask;
51 import org.opendaylight.ovsdb.southbound.reconciliation.connection.ConnectionReconciliationTask;
52 import org.opendaylight.ovsdb.southbound.transactions.md.OvsdbNodeRemoveCommand;
53 import org.opendaylight.ovsdb.southbound.transactions.md.TransactionInvoker;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntryKey;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.opendaylight.yangtools.concepts.Registration;
62 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
66 public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoCloseable {
68 private final ConcurrentMap<ConnectionInfo, OvsdbConnectionInstance> clients = new ConcurrentHashMap<>();
69 private static final Logger LOG = LoggerFactory.getLogger(OvsdbConnectionManager.class);
70 private static final String ENTITY_TYPE = "ovsdb";
71 private static final int DB_FETCH_TIMEOUT = 1000;
73 private final DataBroker db;
74 private final TransactionInvoker txInvoker;
75 private final Map<OvsdbClient, OvsdbClient> alreadyProcessedClients = new ConcurrentHashMap<>();
76 private final Map<ConnectionInfo,InstanceIdentifier<Node>> instanceIdentifiers =
77 new ConcurrentHashMap<>();
78 private final Map<InstanceIdentifier<Node>, OvsdbConnectionInstance> nodeIdVsConnectionInstance =
79 new ConcurrentHashMap<>();
80 private final Map<Entity, OvsdbConnectionInstance> entityConnectionMap =
81 new ConcurrentHashMap<>();
82 private final EntityOwnershipService entityOwnershipService;
83 private final OvsdbDeviceEntityOwnershipListener ovsdbDeviceEntityOwnershipListener;
84 private final OvsdbConnection ovsdbConnection;
85 private final ReconciliationManager reconciliationManager;
86 private final InstanceIdentifierCodec instanceIdentifierCodec;
88 public OvsdbConnectionManager(final DataBroker db,final TransactionInvoker txInvoker,
89 final EntityOwnershipService entityOwnershipService,
90 final OvsdbConnection ovsdbConnection,
91 final InstanceIdentifierCodec instanceIdentifierCodec) {
93 this.txInvoker = txInvoker;
94 this.entityOwnershipService = entityOwnershipService;
95 ovsdbDeviceEntityOwnershipListener = new OvsdbDeviceEntityOwnershipListener(this, entityOwnershipService);
96 this.ovsdbConnection = ovsdbConnection;
97 reconciliationManager = new ReconciliationManager(db, instanceIdentifierCodec);
98 this.instanceIdentifierCodec = instanceIdentifierCodec;
102 public void connected(final OvsdbClient externalClient) {
103 if (alreadyProcessedClients.containsKey(externalClient)) {
104 LOG.info("OvsdbConnectionManager Library already connected {} from {}:{} to {}:{} "
105 + "to this, hence skipping the processing",
106 externalClient.getConnectionInfo().getType(),
107 externalClient.getConnectionInfo().getRemoteAddress(),
108 externalClient.getConnectionInfo().getRemotePort(),
109 externalClient.getConnectionInfo().getLocalAddress(),
110 externalClient.getConnectionInfo().getLocalPort());
113 alreadyProcessedClients.put(externalClient, externalClient);
115 LOG.info("OvsdbConnectionManager connected {} from {}:{} to {}:{}",
116 externalClient.getConnectionInfo().getType(),
117 externalClient.getConnectionInfo().getRemoteAddress(),
118 externalClient.getConnectionInfo().getRemotePort(),
119 externalClient.getConnectionInfo().getLocalAddress(),
120 externalClient.getConnectionInfo().getLocalPort());
122 List<String> databases = externalClient.getDatabases().get(DB_FETCH_TIMEOUT, TimeUnit.MILLISECONDS);
123 if (databases.contains(SouthboundConstants.OPEN_V_SWITCH)) {
124 OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient);
125 // Register Cluster Ownership for ConnectionInfo
126 registerEntityForOwnership(client);
127 OvsdbOperGlobalListener.runAfterTimeoutIfNodeNotCreated(client.getInstanceIdentifier(), () -> {
128 externalClient.disconnect();
129 disconnected(externalClient);
132 } catch (InterruptedException | ExecutionException | TimeoutException e) {
133 LOG.warn("OvsdbConnectionManager Unable to fetch Database list from device {}."
134 + "Disconnecting from the device.", externalClient.getConnectionInfo().getRemoteAddress(), e);
135 externalClient.disconnect();
140 public OvsdbConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) {
141 LOG.info("OVSDB Connection from {}:{}",externalClient.getConnectionInfo().getRemoteAddress(),
142 externalClient.getConnectionInfo().getRemotePort());
143 ConnectionInfo key = SouthboundMapper.createConnectionInfo(externalClient);
144 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
146 // Check if existing ovsdbConnectionInstance for the OvsdbClient present.
147 // In such cases, we will see if the ovsdbConnectionInstance has same externalClient.
148 if (ovsdbConnectionInstance != null) {
149 if (ovsdbConnectionInstance.hasOvsdbClient(externalClient)) {
150 LOG.warn("OVSDB Connection Instance {} already exists for client {}", key, externalClient);
151 return ovsdbConnectionInstance;
153 LOG.warn("OVSDB Connection Instance {} being replaced with client {}", key, externalClient);
155 // Unregister Cluster Ownership for ConnectionInfo
156 // Because the ovsdbConnectionInstance is about to be completely replaced!
157 unregisterEntityForOwnership(ovsdbConnectionInstance);
159 ovsdbConnectionInstance.disconnect();
161 removeConnectionInstance(key);
163 stopBridgeConfigReconciliationIfActive(ovsdbConnectionInstance.getInstanceIdentifier());
166 ovsdbConnectionInstance = new OvsdbConnectionInstance(key, externalClient, txInvoker,
167 getInstanceIdentifier(key));
168 ovsdbConnectionInstance.createTransactInvokers();
169 return ovsdbConnectionInstance;
173 public void disconnected(final OvsdbClient client) {
174 alreadyProcessedClients.remove(client);
175 LOG.info("Ovsdb Library disconnected {} from {}:{} to {}:{}. Cleaning up the operational data store",
176 client.getConnectionInfo().getType(),
177 client.getConnectionInfo().getRemoteAddress(),
178 client.getConnectionInfo().getRemotePort(),
179 client.getConnectionInfo().getLocalAddress(),
180 client.getConnectionInfo().getLocalPort());
181 ConnectionInfo key = SouthboundMapper.createConnectionInfo(client);
182 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
183 if (ovsdbConnectionInstance != null) {
184 // Unregister Entity ownership as soon as possible ,so this instance should
185 // not be used as a candidate in Entity election (given that this instance is
186 // about to disconnect as well), if current owner get disconnected from
188 if (ovsdbConnectionInstance.getHasDeviceOwnership()) {
189 LOG.info("Ovsdb Library disconnected {} this controller instance has ownership", key);
190 deleteOperNodeAndReleaseOwnership(ovsdbConnectionInstance);
192 LOG.info("Ovsdb Library disconnected {} this controller does not have ownership", key);
193 unregisterEntityForOwnership(ovsdbConnectionInstance);
195 removeConnectionInstance(key);
197 //Controller initiated connection can be terminated from switch side.
198 //So cleanup the instance identifier cache.
199 removeInstanceIdentifier(key);
200 nodeIdVsConnectionInstance.remove(ovsdbConnectionInstance.getInstanceIdentifier(),
201 ovsdbConnectionInstance);
202 stopBridgeConfigReconciliationIfActive(ovsdbConnectionInstance.getInstanceIdentifier());
203 retryConnection(ovsdbConnectionInstance.getInstanceIdentifier(),
204 ovsdbConnectionInstance.getOvsdbNodeAugmentation(),
205 ConnectionReconciliationTriggers.ON_DISCONNECT);
207 LOG.warn("Ovsdb disconnected : Connection instance not found for OVSDB Node {} ", key);
209 LOG.trace("OvsdbConnectionManager: exit disconnected client: {}", client);
212 private void deleteOperNodeAndReleaseOwnership(final OvsdbConnectionInstance ovsdbConnectionInstance) {
213 ovsdbConnectionInstance.setHasDeviceOwnership(false);
214 final InstanceIdentifier<?> nodeIid = ovsdbConnectionInstance.getInstanceIdentifier();
215 //remove the node from oper only if it has ownership
216 txInvoker.invoke(new OvsdbNodeRemoveCommand(ovsdbConnectionInstance, null, null) {
219 public void onSuccess() {
221 LOG.debug("Successfully removed node {} from oper", nodeIid);
222 //Giveup the ownership only after cleanup is done
223 unregisterEntityForOwnership(ovsdbConnectionInstance);
227 public void onFailure(final Throwable throwable) {
228 LOG.debug("Failed to remove node {} from oper", nodeIid);
229 super.onFailure(throwable);
230 unregisterEntityForOwnership(ovsdbConnectionInstance);
235 public OvsdbClient connect(final InstanceIdentifier<Node> iid,
236 final OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException, ConnectException {
237 LOG.info("Connecting to {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
239 // TODO handle case where we already have a connection
240 // TODO use transaction chains to handle ordering issues between disconnected
241 // TODO and connected when writing to the operational store
242 InetAddress ip = SouthboundMapper.createInetAddress(ovsdbNode.getConnectionInfo().getRemoteIp());
243 OvsdbClient client = ovsdbConnection.connect(ip,
244 ovsdbNode.getConnectionInfo().getRemotePort().getValue().toJava());
245 // For connections from the controller to the ovs instance, the library doesn't call
246 // this method for us
247 if (client != null) {
248 putInstanceIdentifier(ovsdbNode.getConnectionInfo(), iid.firstIdentifierOf(Node.class));
249 OvsdbConnectionInstance ovsdbConnectionInstance = connectedButCallBacksNotRegistered(client);
250 ovsdbConnectionInstance.setOvsdbNodeAugmentation(ovsdbNode);
252 // Register Cluster Ownership for ConnectionInfo
253 registerEntityForOwnership(ovsdbConnectionInstance);
255 LOG.warn("Failed to connect to OVSDB Node {}", ovsdbNode.getConnectionInfo());
260 public void disconnect(final OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
261 LOG.info("Disconnecting from {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
262 OvsdbConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo());
263 if (client != null) {
264 // Unregister Cluster Onwership for ConnectionInfo
265 deleteOperNodeAndReleaseOwnership(client);
269 removeInstanceIdentifier(ovsdbNode.getConnectionInfo());
271 stopBridgeConfigReconciliationIfActive(client.getInstanceIdentifier());
273 LOG.debug("disconnect : connection instance not found for {}",ovsdbNode.getConnectionInfo());
277 /* public void init(ConnectionInfo key) {
278 OvsdbConnectionInstance client = getConnectionInstance(key);
280 // TODO (FF): make sure that this cluster instance is the 'entity owner' fo the given OvsdbConnectionInstance ?
282 if (client != null) {
284 * Note: registerCallbacks() is idemPotent... so if you call it repeatedly all is safe,
285 * it only registersCallbacks on the *first* call.
287 client.registerCallbacks();
292 public void close() {
293 if (ovsdbDeviceEntityOwnershipListener != null) {
294 ovsdbDeviceEntityOwnershipListener.close();
297 for (OvsdbConnectionInstance client: clients.values()) {
303 void putConnectionInstance(final ConnectionInfo key,final OvsdbConnectionInstance instance) {
304 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
305 clients.put(connectionInfo, instance);
308 private void removeConnectionInstance(final ConnectionInfo key) {
309 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
310 clients.remove(connectionInfo);
314 void putInstanceIdentifier(final ConnectionInfo key, final InstanceIdentifier<Node> iid) {
315 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
316 instanceIdentifiers.put(connectionInfo, iid);
319 private void removeInstanceIdentifier(final ConnectionInfo key) {
320 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
321 instanceIdentifiers.remove(connectionInfo);
324 public InstanceIdentifier<Node> getInstanceIdentifier(final ConnectionInfo key) {
325 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
326 return instanceIdentifiers.get(connectionInfo);
329 public OvsdbConnectionInstance getConnectionInstance(final ConnectionInfo key) {
330 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
331 return clients.get(connectionInfo);
334 public OvsdbConnectionInstance getConnectionInstance(final OvsdbBridgeAttributes mn) {
335 Optional<OvsdbNodeAugmentation> optional = SouthboundUtil.getManagingNode(db, mn);
336 if (optional.isPresent()) {
337 return getConnectionInstance(optional.orElseThrow().getConnectionInfo());
343 public OvsdbConnectionInstance getConnectionInstance(final Node node) {
344 requireNonNull(node);
345 OvsdbNodeAugmentation ovsdbNode = node.augmentation(OvsdbNodeAugmentation.class);
346 OvsdbBridgeAugmentation ovsdbManagedNode = node.augmentation(OvsdbBridgeAugmentation.class);
347 if (ovsdbNode != null) {
348 return getConnectionInstance(ovsdbNode.getConnectionInfo());
349 } else if (ovsdbManagedNode != null) {
350 return getConnectionInstance(ovsdbManagedNode);
352 LOG.warn("This is not a node that gives any hint how to find its OVSDB Manager: {}",node);
357 public OvsdbConnectionInstance getConnectionInstance(final InstanceIdentifier<Node> nodePath) {
358 if (nodeIdVsConnectionInstance.get(nodePath) != null) {
359 return nodeIdVsConnectionInstance.get(nodePath);
362 ReadTransaction transaction = db.newReadOnlyTransaction();
363 FluentFuture<Optional<Node>> nodeFuture = transaction.read(
364 LogicalDatastoreType.OPERATIONAL, nodePath);
366 Optional<Node> optional = nodeFuture.get();
367 if (optional.isPresent()) {
368 return this.getConnectionInstance(optional.orElseThrow());
370 LOG.debug("Node was not found on the path in the operational DS: {}", nodePath);
373 } catch (InterruptedException | ExecutionException e) {
374 LOG.warn("Failed to get Ovsdb Node {}",nodePath, e);
379 public OvsdbClient getClient(final ConnectionInfo connectionInfo) {
380 OvsdbConnectionInstance connectionInstance = getConnectionInstance(connectionInfo);
381 if (connectionInstance != null) {
382 return connectionInstance.getOvsdbClient();
387 public OvsdbClient getClient(final OvsdbBridgeAttributes mn) {
388 return getConnectionInstance(mn).getOvsdbClient();
391 public OvsdbClient getClient(final Node node) {
392 return getConnectionInstance(node).getOvsdbClient();
395 public Boolean getHasDeviceOwnership(final ConnectionInfo connectionInfo) {
396 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
397 if (ovsdbConnectionInstance == null) {
398 return Boolean.FALSE;
400 return ovsdbConnectionInstance.getHasDeviceOwnership();
403 public void reconcileConnection(final InstanceIdentifier<Node> iid, final OvsdbNodeAugmentation ovsdbNode) {
404 retryConnection(iid, ovsdbNode,
405 ConnectionReconciliationTriggers.ON_CONTROLLER_INITIATED_CONNECTION_FAILURE);
409 public void stopConnectionReconciliationIfActive(final InstanceIdentifier<Node> iid,
410 final OvsdbNodeAugmentation ovsdbNode) {
411 final ReconciliationTask task = new ConnectionReconciliationTask(
412 reconciliationManager,
416 reconciliationManager.dequeue(task);
419 public void stopBridgeConfigReconciliationIfActive(final InstanceIdentifier<Node> iid) {
420 final ReconciliationTask task =
421 new BridgeConfigReconciliationTask(reconciliationManager, this, iid, null, instanceIdentifierCodec);
422 reconciliationManager.dequeue(task);
423 reconciliationManager.cancelTerminationPointReconciliation();
427 void handleOwnershipChanged(final Entity entity, final EntityOwnershipStateChange change) {
428 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(entity);
429 LOG.debug("Ovsdb handleOwnershipChanged: {} event received for device {}", change,
430 ovsdbConnectionInstance != null ? ovsdbConnectionInstance.getConnectionInfo()
431 : "that's currently NOT registered by *this* southbound plugin instance");
433 if (ovsdbConnectionInstance == null) {
434 if (change.isOwner()) {
435 LOG.warn("Ovsdb handleOwnershipChanged: *this* instance is elected as an owner of the device {} but it "
436 + "is NOT registered for ownership", entity);
438 // EntityOwnershipService sends notification to all the nodes, irrespective of whether
439 // that instance registered for the device ownership or not. It is to make sure that
440 // If all the controller instance that was connected to the device are down, so the
441 // running instance can clear up the operational data store even though it was not
442 // connected to the device.
443 LOG.debug("Ovsdb handleOwnershipChanged: No connection instance found for {}",
447 // If entity has no owner, clean up the operational data store (it's possible because owner controller
448 // might went down abruptly and didn't get a chance to clean up the operational data store.
449 if (!change.hasOwner()) {
450 LOG.info("Ovsdb {} has no owner, cleaning up the operational data store", entity);
451 cleanEntityOperationalData(entity);
455 //Connection detail need to be cached, irrespective of ownership result.
456 putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
458 if (change.isOwner() == ovsdbConnectionInstance.getHasDeviceOwnership()) {
459 LOG.info("Ovsdb handleOwnershipChanged: no change in ownership for {}. Ownership status is : {}",
460 ovsdbConnectionInstance.getConnectionInfo(), ovsdbConnectionInstance.getHasDeviceOwnership()
461 ? OwnershipStates.OWNER.getState()
462 : OwnershipStates.NONOWNER.getState());
466 ovsdbConnectionInstance.setHasDeviceOwnership(change.isOwner());
467 // You were not an owner, but now you are
468 if (change.isOwner()) {
469 LOG.info("Ovsdb handleOwnershipChanged: *this* southbound plugin instance is an OWNER of the device {}",
470 ovsdbConnectionInstance.getConnectionInfo());
472 //*this* instance of southbound plugin is owner of the device,
473 //so register for monitor callbacks
474 ovsdbConnectionInstance.registerCallbacks(instanceIdentifierCodec);
476 reconcileBridgeConfigurations(ovsdbConnectionInstance);
478 //You were owner of the device, but now you are not. With the current ownership
479 //grant mechanism, this scenario should not occur. Because this scenario will occur
480 //when this controller went down or switch flap the connection, but in both the case
481 //it will go through the re-registration process. We need to implement this condition
482 //when clustering service implement a ownership grant strategy which can revoke the
483 //device ownership for load balancing the devices across the instances.
484 //Once this condition occur, we should unregister the callback.
485 LOG.error("Ovsdb handleOwnershipChanged: *this* southbound plugin instance is no longer"
486 + " the owner of device {}.This should NOT happen.",
487 ovsdbConnectionInstance.getNodeId().getValue());
491 private void cleanEntityOperationalData(final Entity entity) {
493 //Do explicit cleanup rather than using OvsdbNodeRemoveCommand, because there
494 // are chances that other controller instance went down abruptly and it does
495 // not clear manager entry, which OvsdbNodeRemoveCommand look for before cleanup.
497 @SuppressWarnings("unchecked")
498 final InstanceIdentifier<Node> nodeIid = (InstanceIdentifier<Node>) entity.getIdentifier();
500 txInvoker.invoke(transaction -> {
501 Optional<Node> ovsdbNodeOpt = SouthboundUtil.readNode(transaction, nodeIid);
502 if (ovsdbNodeOpt.isPresent()) {
503 Node ovsdbNode = ovsdbNodeOpt.orElseThrow();
504 OvsdbNodeAugmentation nodeAugmentation = ovsdbNode.augmentation(OvsdbNodeAugmentation.class);
505 if (nodeAugmentation != null) {
506 Map<ManagedNodeEntryKey, ManagedNodeEntry> entries = nodeAugmentation.getManagedNodeEntry();
507 if (entries != null) {
508 for (ManagedNodeEntry managedNode : entries.values()) {
510 LogicalDatastoreType.OPERATIONAL, managedNode.getBridgeRef().getValue());
513 LOG.debug("{} had no managed nodes", ovsdbNode.getNodeId().getValue());
516 transaction.delete(LogicalDatastoreType.OPERATIONAL, nodeIid);
522 private static OpenVSwitch getOpenVswitchTableEntry(final OvsdbConnectionInstance connectionInstance) {
523 final TypedDatabaseSchema dbSchema;
525 dbSchema = connectionInstance.getSchema(OvsdbSchemaContants.DATABASE_NAME).get();
526 } catch (InterruptedException | ExecutionException e) {
527 LOG.warn("Ovsdb Not able to fetch schema for database {} from device {}",
528 OvsdbSchemaContants.DATABASE_NAME,connectionInstance.getConnectionInfo(),e);
532 final GenericTableSchema openVSwitchSchema = dbSchema.getTableSchema(OpenVSwitch.class);
533 final Select<GenericTableSchema> selectOperation = op.select(openVSwitchSchema);
534 selectOperation.setColumns(openVSwitchSchema.getColumnList());
536 List<Operation> operations = new ArrayList<>();
537 operations.add(selectOperation);
538 operations.add(op.comment("Fetching Open_VSwitch table rows"));
539 final List<OperationResult> results;
541 results = connectionInstance.transact(dbSchema, operations).get();
542 } catch (InterruptedException | ExecutionException e) {
543 LOG.warn("Ovsdb Not able to fetch OpenVswitch table row from device {}",
544 connectionInstance.getConnectionInfo(), e);
548 return results == null || results.isEmpty() ? null
549 : dbSchema.getTypedRowWrapper(OpenVSwitch.class, results.get(0).getRows().get(0));
552 private Entity getEntityFromConnectionInstance(@NonNull final OvsdbConnectionInstance ovsdbConnectionInstance) {
553 InstanceIdentifier<Node> iid = ovsdbConnectionInstance.getInstanceIdentifier();
555 /* Switch initiated connection won't have iid, till it gets OpenVSwitch
556 * table update but update callback is always registered after ownership
557 * is granted. So we are explicitly fetch the row here to get the iid.
559 OpenVSwitch openvswitchRow = getOpenVswitchTableEntry(ovsdbConnectionInstance);
560 iid = SouthboundMapper.getInstanceIdentifier(instanceIdentifierCodec, openvswitchRow);
561 LOG.info("Ovsdb InstanceIdentifier {} generated for device "
562 + "connection {}",iid,ovsdbConnectionInstance.getConnectionInfo());
563 ovsdbConnectionInstance.setInstanceIdentifier(iid);
565 Entity deviceEntity = new Entity(ENTITY_TYPE, iid);
566 LOG.debug("Ovsdb Entity {} created for device connection {}",
567 deviceEntity, ovsdbConnectionInstance.getConnectionInfo());
571 private OvsdbConnectionInstance getConnectionInstanceFromEntity(final Entity entity) {
572 return entityConnectionMap.get(entity);
575 private void registerEntityForOwnership(final OvsdbConnectionInstance ovsdbConnectionInstance) {
576 putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(), ovsdbConnectionInstance);
578 Entity candidateEntity = getEntityFromConnectionInstance(ovsdbConnectionInstance);
579 if (entityConnectionMap.containsKey(candidateEntity)) {
580 LOG.error("Ovsdb Old connection still hanging for {}", candidateEntity);
581 disconnected(ovsdbConnectionInstance.getOvsdbClient());
582 //TODO do cleanup for old connection or stale check
584 nodeIdVsConnectionInstance.put((InstanceIdentifier<Node>) candidateEntity.getIdentifier(),
585 ovsdbConnectionInstance);
586 entityConnectionMap.put(candidateEntity, ovsdbConnectionInstance);
587 ovsdbConnectionInstance.setConnectedEntity(candidateEntity);
589 Registration registration = entityOwnershipService.registerCandidate(candidateEntity);
590 ovsdbConnectionInstance.setDeviceOwnershipCandidateRegistration(registration);
591 LOG.info("OVSDB entity {} is registered for ownership.", candidateEntity);
593 } catch (CandidateAlreadyRegisteredException e) {
594 LOG.warn("OVSDB entity {} was already registered for ownership", candidateEntity, e);
596 //If entity already has owner, it won't get notification from EntityOwnershipService
597 Optional<EntityOwnershipState> ownershipStateOpt = entityOwnershipService.getOwnershipState(candidateEntity);
598 if (ownershipStateOpt.isPresent()) {
599 EntityOwnershipState ownershipState = ownershipStateOpt.orElseThrow();
600 if (ownershipState == EntityOwnershipState.OWNED_BY_OTHER) {
601 ovsdbConnectionInstance.setHasDeviceOwnership(false);
602 } else if (ownershipState == EntityOwnershipState.IS_OWNER) {
603 ovsdbConnectionInstance.setHasDeviceOwnership(true);
604 ovsdbConnectionInstance.registerCallbacks(instanceIdentifierCodec);
609 private void unregisterEntityForOwnership(final OvsdbConnectionInstance ovsdbConnectionInstance) {
610 ovsdbConnectionInstance.closeDeviceOwnershipCandidateRegistration();
611 entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity(), ovsdbConnectionInstance);
614 private void retryConnection(final InstanceIdentifier<Node> iid, final OvsdbNodeAugmentation ovsdbNode,
615 final ConnectionReconciliationTriggers trigger) {
616 final ReconciliationTask task = new ConnectionReconciliationTask(
617 reconciliationManager,
622 if (reconciliationManager.isEnqueued(task)) {
626 case ON_CONTROLLER_INITIATED_CONNECTION_FAILURE:
627 reconciliationManager.enqueueForRetry(task);
629 case ON_DISCONNECT: {
630 FluentFuture<Boolean> readNodeFuture;
631 try (ReadTransaction tx = db.newReadOnlyTransaction()) {
632 readNodeFuture = tx.exists(LogicalDatastoreType.CONFIGURATION, iid);
634 readNodeFuture.addCallback(new FutureCallback<Boolean>() {
636 public void onSuccess(final Boolean node) {
638 LOG.info("Disconnected/Failed connection {} was controller initiated, attempting "
639 + "reconnection", ovsdbNode.getConnectionInfo());
640 reconciliationManager.enqueue(task);
643 LOG.debug("Connection {} was switch initiated, no reconciliation is required",
644 iid.firstKeyOf(Node.class).getNodeId());
649 public void onFailure(final Throwable throwable) {
650 LOG.warn("Read Config/DS for Node failed! {}", iid, throwable);
652 }, MoreExecutors.directExecutor());
660 private void reconcileBridgeConfigurations(final OvsdbConnectionInstance client) {
661 final InstanceIdentifier<Node> nodeIid = client.getInstanceIdentifier();
662 final ReconciliationTask task = new BridgeConfigReconciliationTask(
663 reconciliationManager, OvsdbConnectionManager.this, nodeIid, client, instanceIdentifierCodec);
665 reconciliationManager.enqueue(task);
668 private static final class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener {
669 private final OvsdbConnectionManager cm;
670 private final Registration listenerRegistration;
672 OvsdbDeviceEntityOwnershipListener(final OvsdbConnectionManager cm,
673 final EntityOwnershipService entityOwnershipService) {
675 listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
678 public void close() {
679 listenerRegistration.close();
683 public void ownershipChanged(final Entity entity, final EntityOwnershipStateChange change,
684 final boolean inJeopardy) {
685 cm.handleOwnershipChanged(entity, change);
689 private enum ConnectionReconciliationTriggers {
691 Reconciliation trigger for scenario where controller's attempt
692 to connect to switch fails on config data store notification
694 ON_CONTROLLER_INITIATED_CONNECTION_FAILURE,
697 Reconciliation trigger for the scenario where controller
698 initiated connection disconnects.
703 private enum OwnershipStates {
705 NONOWNER("NON-OWNER");
707 private final String state;
709 OwnershipStates(final String state) {
714 public String toString() {