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.EntityOwnershipCandidateRegistration;
35 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipChange;
36 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipListener;
37 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipListenerRegistration;
38 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
39 import org.opendaylight.mdsal.eos.common.api.CandidateAlreadyRegisteredException;
40 import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
41 import org.opendaylight.ovsdb.lib.OvsdbClient;
42 import org.opendaylight.ovsdb.lib.OvsdbConnection;
43 import org.opendaylight.ovsdb.lib.OvsdbConnectionListener;
44 import org.opendaylight.ovsdb.lib.operations.Operation;
45 import org.opendaylight.ovsdb.lib.operations.OperationResult;
46 import org.opendaylight.ovsdb.lib.operations.Select;
47 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
48 import org.opendaylight.ovsdb.lib.schema.typed.TypedDatabaseSchema;
49 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
50 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationManager;
51 import org.opendaylight.ovsdb.southbound.reconciliation.ReconciliationTask;
52 import org.opendaylight.ovsdb.southbound.reconciliation.configuration.BridgeConfigReconciliationTask;
53 import org.opendaylight.ovsdb.southbound.reconciliation.connection.ConnectionReconciliationTask;
54 import org.opendaylight.ovsdb.southbound.transactions.md.OvsdbNodeRemoveCommand;
55 import org.opendaylight.ovsdb.southbound.transactions.md.TransactionInvoker;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntryKey;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
63 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
67 public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoCloseable {
69 private final ConcurrentMap<ConnectionInfo, OvsdbConnectionInstance> clients = new ConcurrentHashMap<>();
70 private static final Logger LOG = LoggerFactory.getLogger(OvsdbConnectionManager.class);
71 private static final String ENTITY_TYPE = "ovsdb";
72 private static final int DB_FETCH_TIMEOUT = 1000;
74 private final DataBroker db;
75 private final TransactionInvoker txInvoker;
76 private final Map<OvsdbClient, OvsdbClient> alreadyProcessedClients = new ConcurrentHashMap<>();
77 private final Map<ConnectionInfo,InstanceIdentifier<Node>> instanceIdentifiers =
78 new ConcurrentHashMap<>();
79 private final Map<InstanceIdentifier<Node>, OvsdbConnectionInstance> nodeIdVsConnectionInstance =
80 new ConcurrentHashMap<>();
81 private final Map<Entity, OvsdbConnectionInstance> entityConnectionMap =
82 new ConcurrentHashMap<>();
83 private final EntityOwnershipService entityOwnershipService;
84 private final OvsdbDeviceEntityOwnershipListener ovsdbDeviceEntityOwnershipListener;
85 private final OvsdbConnection ovsdbConnection;
86 private final ReconciliationManager reconciliationManager;
87 private final InstanceIdentifierCodec instanceIdentifierCodec;
89 public OvsdbConnectionManager(final DataBroker db,final TransactionInvoker txInvoker,
90 final EntityOwnershipService entityOwnershipService,
91 final OvsdbConnection ovsdbConnection,
92 final InstanceIdentifierCodec instanceIdentifierCodec) {
94 this.txInvoker = txInvoker;
95 this.entityOwnershipService = entityOwnershipService;
96 ovsdbDeviceEntityOwnershipListener = new OvsdbDeviceEntityOwnershipListener(this, entityOwnershipService);
97 this.ovsdbConnection = ovsdbConnection;
98 reconciliationManager = new ReconciliationManager(db, instanceIdentifierCodec);
99 this.instanceIdentifierCodec = instanceIdentifierCodec;
103 public void connected(final OvsdbClient externalClient) {
104 if (alreadyProcessedClients.containsKey(externalClient)) {
105 LOG.info("OvsdbConnectionManager Library already connected {} from {}:{} to {}:{} "
106 + "to this, hence skipping the processing",
107 externalClient.getConnectionInfo().getType(),
108 externalClient.getConnectionInfo().getRemoteAddress(),
109 externalClient.getConnectionInfo().getRemotePort(),
110 externalClient.getConnectionInfo().getLocalAddress(),
111 externalClient.getConnectionInfo().getLocalPort());
114 alreadyProcessedClients.put(externalClient, externalClient);
116 LOG.info("OvsdbConnectionManager connected {} from {}:{} to {}:{}",
117 externalClient.getConnectionInfo().getType(),
118 externalClient.getConnectionInfo().getRemoteAddress(),
119 externalClient.getConnectionInfo().getRemotePort(),
120 externalClient.getConnectionInfo().getLocalAddress(),
121 externalClient.getConnectionInfo().getLocalPort());
123 List<String> databases = externalClient.getDatabases().get(DB_FETCH_TIMEOUT, TimeUnit.MILLISECONDS);
124 if (databases.contains(SouthboundConstants.OPEN_V_SWITCH)) {
125 OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient);
126 // Register Cluster Ownership for ConnectionInfo
127 registerEntityForOwnership(client);
128 OvsdbOperGlobalListener.runAfterTimeoutIfNodeNotCreated(client.getInstanceIdentifier(), () -> {
129 externalClient.disconnect();
130 disconnected(externalClient);
133 } catch (InterruptedException | ExecutionException | TimeoutException e) {
134 LOG.warn("OvsdbConnectionManager Unable to fetch Database list from device {}."
135 + "Disconnecting from the device.", externalClient.getConnectionInfo().getRemoteAddress(), e);
136 externalClient.disconnect();
141 public OvsdbConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) {
142 LOG.info("OVSDB Connection from {}:{}",externalClient.getConnectionInfo().getRemoteAddress(),
143 externalClient.getConnectionInfo().getRemotePort());
144 ConnectionInfo key = SouthboundMapper.createConnectionInfo(externalClient);
145 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
147 // Check if existing ovsdbConnectionInstance for the OvsdbClient present.
148 // In such cases, we will see if the ovsdbConnectionInstance has same externalClient.
149 if (ovsdbConnectionInstance != null) {
150 if (ovsdbConnectionInstance.hasOvsdbClient(externalClient)) {
151 LOG.warn("OVSDB Connection Instance {} already exists for client {}", key, externalClient);
152 return ovsdbConnectionInstance;
154 LOG.warn("OVSDB Connection Instance {} being replaced with client {}", key, externalClient);
156 // Unregister Cluster Ownership for ConnectionInfo
157 // Because the ovsdbConnectionInstance is about to be completely replaced!
158 unregisterEntityForOwnership(ovsdbConnectionInstance);
160 ovsdbConnectionInstance.disconnect();
162 removeConnectionInstance(key);
164 stopBridgeConfigReconciliationIfActive(ovsdbConnectionInstance.getInstanceIdentifier());
167 ovsdbConnectionInstance = new OvsdbConnectionInstance(key, externalClient, txInvoker,
168 getInstanceIdentifier(key));
169 ovsdbConnectionInstance.createTransactInvokers();
170 return ovsdbConnectionInstance;
174 public void disconnected(final OvsdbClient client) {
175 alreadyProcessedClients.remove(client);
176 LOG.info("Ovsdb Library disconnected {} from {}:{} to {}:{}. Cleaning up the operational data store",
177 client.getConnectionInfo().getType(),
178 client.getConnectionInfo().getRemoteAddress(),
179 client.getConnectionInfo().getRemotePort(),
180 client.getConnectionInfo().getLocalAddress(),
181 client.getConnectionInfo().getLocalPort());
182 ConnectionInfo key = SouthboundMapper.createConnectionInfo(client);
183 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
184 if (ovsdbConnectionInstance != null) {
185 // Unregister Entity ownership as soon as possible ,so this instance should
186 // not be used as a candidate in Entity election (given that this instance is
187 // about to disconnect as well), if current owner get disconnected from
189 if (ovsdbConnectionInstance.getHasDeviceOwnership()) {
190 LOG.info("Ovsdb Library disconnected {} this controller instance has ownership", key);
191 deleteOperNodeAndReleaseOwnership(ovsdbConnectionInstance);
193 LOG.info("Ovsdb Library disconnected {} this controller does not have ownership", key);
194 unregisterEntityForOwnership(ovsdbConnectionInstance);
196 removeConnectionInstance(key);
198 //Controller initiated connection can be terminated from switch side.
199 //So cleanup the instance identifier cache.
200 removeInstanceIdentifier(key);
201 nodeIdVsConnectionInstance.remove(ovsdbConnectionInstance.getInstanceIdentifier(),
202 ovsdbConnectionInstance);
203 stopBridgeConfigReconciliationIfActive(ovsdbConnectionInstance.getInstanceIdentifier());
204 retryConnection(ovsdbConnectionInstance.getInstanceIdentifier(),
205 ovsdbConnectionInstance.getOvsdbNodeAugmentation(),
206 ConnectionReconciliationTriggers.ON_DISCONNECT);
208 LOG.warn("Ovsdb disconnected : Connection instance not found for OVSDB Node {} ", key);
210 LOG.trace("OvsdbConnectionManager: exit disconnected client: {}", client);
213 private void deleteOperNodeAndReleaseOwnership(final OvsdbConnectionInstance ovsdbConnectionInstance) {
214 ovsdbConnectionInstance.setHasDeviceOwnership(false);
215 final InstanceIdentifier<?> nodeIid = ovsdbConnectionInstance.getInstanceIdentifier();
216 //remove the node from oper only if it has ownership
217 txInvoker.invoke(new OvsdbNodeRemoveCommand(ovsdbConnectionInstance, null, null) {
220 public void onSuccess() {
222 LOG.debug("Successfully removed node {} from oper", nodeIid);
223 //Giveup the ownership only after cleanup is done
224 unregisterEntityForOwnership(ovsdbConnectionInstance);
228 public void onFailure(final Throwable throwable) {
229 LOG.debug("Failed to remove node {} from oper", nodeIid);
230 super.onFailure(throwable);
231 unregisterEntityForOwnership(ovsdbConnectionInstance);
236 public OvsdbClient connect(final InstanceIdentifier<Node> iid,
237 final OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException, ConnectException {
238 LOG.info("Connecting to {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
240 // TODO handle case where we already have a connection
241 // TODO use transaction chains to handle ordering issues between disconnected
242 // TODO and connected when writing to the operational store
243 InetAddress ip = SouthboundMapper.createInetAddress(ovsdbNode.getConnectionInfo().getRemoteIp());
244 OvsdbClient client = ovsdbConnection.connect(ip,
245 ovsdbNode.getConnectionInfo().getRemotePort().getValue().toJava());
246 // For connections from the controller to the ovs instance, the library doesn't call
247 // this method for us
248 if (client != null) {
249 putInstanceIdentifier(ovsdbNode.getConnectionInfo(), iid.firstIdentifierOf(Node.class));
250 OvsdbConnectionInstance ovsdbConnectionInstance = connectedButCallBacksNotRegistered(client);
251 ovsdbConnectionInstance.setOvsdbNodeAugmentation(ovsdbNode);
253 // Register Cluster Ownership for ConnectionInfo
254 registerEntityForOwnership(ovsdbConnectionInstance);
256 LOG.warn("Failed to connect to OVSDB Node {}", ovsdbNode.getConnectionInfo());
261 public void disconnect(final OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
262 LOG.info("Disconnecting from {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
263 OvsdbConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo());
264 if (client != null) {
265 // Unregister Cluster Onwership for ConnectionInfo
266 deleteOperNodeAndReleaseOwnership(client);
270 removeInstanceIdentifier(ovsdbNode.getConnectionInfo());
272 stopBridgeConfigReconciliationIfActive(client.getInstanceIdentifier());
274 LOG.debug("disconnect : connection instance not found for {}",ovsdbNode.getConnectionInfo());
278 /* public void init(ConnectionInfo key) {
279 OvsdbConnectionInstance client = getConnectionInstance(key);
281 // TODO (FF): make sure that this cluster instance is the 'entity owner' fo the given OvsdbConnectionInstance ?
283 if (client != null) {
285 * Note: registerCallbacks() is idemPotent... so if you call it repeatedly all is safe,
286 * it only registersCallbacks on the *first* call.
288 client.registerCallbacks();
293 public void close() {
294 if (ovsdbDeviceEntityOwnershipListener != null) {
295 ovsdbDeviceEntityOwnershipListener.close();
298 for (OvsdbConnectionInstance client: clients.values()) {
304 void putConnectionInstance(final ConnectionInfo key,final OvsdbConnectionInstance instance) {
305 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
306 clients.put(connectionInfo, instance);
309 private void removeConnectionInstance(final ConnectionInfo key) {
310 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
311 clients.remove(connectionInfo);
315 void putInstanceIdentifier(final ConnectionInfo key, final InstanceIdentifier<Node> iid) {
316 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
317 instanceIdentifiers.put(connectionInfo, iid);
320 private void removeInstanceIdentifier(final ConnectionInfo key) {
321 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
322 instanceIdentifiers.remove(connectionInfo);
325 public InstanceIdentifier<Node> getInstanceIdentifier(final ConnectionInfo key) {
326 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
327 return instanceIdentifiers.get(connectionInfo);
330 public OvsdbConnectionInstance getConnectionInstance(final ConnectionInfo key) {
331 ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
332 return clients.get(connectionInfo);
335 public OvsdbConnectionInstance getConnectionInstance(final OvsdbBridgeAttributes mn) {
336 Optional<OvsdbNodeAugmentation> optional = SouthboundUtil.getManagingNode(db, mn);
337 if (optional.isPresent()) {
338 return getConnectionInstance(optional.get().getConnectionInfo());
344 public OvsdbConnectionInstance getConnectionInstance(final Node node) {
345 requireNonNull(node);
346 OvsdbNodeAugmentation ovsdbNode = node.augmentation(OvsdbNodeAugmentation.class);
347 OvsdbBridgeAugmentation ovsdbManagedNode = node.augmentation(OvsdbBridgeAugmentation.class);
348 if (ovsdbNode != null) {
349 return getConnectionInstance(ovsdbNode.getConnectionInfo());
350 } else if (ovsdbManagedNode != null) {
351 return getConnectionInstance(ovsdbManagedNode);
353 LOG.warn("This is not a node that gives any hint how to find its OVSDB Manager: {}",node);
358 public OvsdbConnectionInstance getConnectionInstance(final InstanceIdentifier<Node> nodePath) {
359 if (nodeIdVsConnectionInstance.get(nodePath) != null) {
360 return nodeIdVsConnectionInstance.get(nodePath);
363 ReadTransaction transaction = db.newReadOnlyTransaction();
364 FluentFuture<Optional<Node>> nodeFuture = transaction.read(
365 LogicalDatastoreType.OPERATIONAL, nodePath);
367 Optional<Node> optional = nodeFuture.get();
368 if (optional.isPresent()) {
369 return this.getConnectionInstance(optional.get());
371 LOG.debug("Node was not found on the path in the operational DS: {}", nodePath);
374 } catch (InterruptedException | ExecutionException e) {
375 LOG.warn("Failed to get Ovsdb Node {}",nodePath, e);
380 public OvsdbClient getClient(final ConnectionInfo connectionInfo) {
381 OvsdbConnectionInstance connectionInstance = getConnectionInstance(connectionInfo);
382 if (connectionInstance != null) {
383 return connectionInstance.getOvsdbClient();
388 public OvsdbClient getClient(final OvsdbBridgeAttributes mn) {
389 return getConnectionInstance(mn).getOvsdbClient();
392 public OvsdbClient getClient(final Node node) {
393 return getConnectionInstance(node).getOvsdbClient();
396 public Boolean getHasDeviceOwnership(final ConnectionInfo connectionInfo) {
397 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
398 if (ovsdbConnectionInstance == null) {
399 return Boolean.FALSE;
401 return ovsdbConnectionInstance.getHasDeviceOwnership();
404 public void reconcileConnection(final InstanceIdentifier<Node> iid, final OvsdbNodeAugmentation ovsdbNode) {
405 retryConnection(iid, ovsdbNode,
406 ConnectionReconciliationTriggers.ON_CONTROLLER_INITIATED_CONNECTION_FAILURE);
410 public void stopConnectionReconciliationIfActive(final InstanceIdentifier<Node> iid,
411 final OvsdbNodeAugmentation ovsdbNode) {
412 final ReconciliationTask task = new ConnectionReconciliationTask(
413 reconciliationManager,
417 reconciliationManager.dequeue(task);
420 public void stopBridgeConfigReconciliationIfActive(final InstanceIdentifier<Node> iid) {
421 final ReconciliationTask task =
422 new BridgeConfigReconciliationTask(reconciliationManager, this, iid, null, instanceIdentifierCodec);
423 reconciliationManager.dequeue(task);
424 reconciliationManager.cancelTerminationPointReconciliation();
427 private void handleOwnershipChanged(final EntityOwnershipChange ownershipChange) {
428 OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(ownershipChange.getEntity());
429 LOG.debug("Ovsdb handleOwnershipChanged: {} event received for device {}",
430 ownershipChange, ovsdbConnectionInstance != null ? ovsdbConnectionInstance.getConnectionInfo()
431 : "that's currently NOT registered by *this* southbound plugin instance");
433 if (ovsdbConnectionInstance == null) {
434 if (ownershipChange.getState().isOwner()) {
435 LOG.warn("Ovsdb handleOwnershipChanged: *this* instance is elected as an owner of the device {} but it "
436 + "is NOT registered for ownership", ownershipChange.getEntity());
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 {}",
444 ownershipChange.getEntity());
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 (!ownershipChange.getState().hasOwner()) {
450 LOG.info("Ovsdb {} has no owner, cleaning up the operational data store", ownershipChange.getEntity());
451 cleanEntityOperationalData(ownershipChange.getEntity());
455 //Connection detail need to be cached, irrespective of ownership result.
456 putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
458 if (ownershipChange.getState().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(ownershipChange.getState().isOwner());
467 // You were not an owner, but now you are
468 if (ownershipChange.getState().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.get();
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 EntityOwnershipCandidateRegistration registration =
590 entityOwnershipService.registerCandidate(candidateEntity);
591 ovsdbConnectionInstance.setDeviceOwnershipCandidateRegistration(registration);
592 LOG.info("OVSDB entity {} is registered for ownership.", candidateEntity);
594 } catch (CandidateAlreadyRegisteredException e) {
595 LOG.warn("OVSDB entity {} was already registered for ownership", candidateEntity, e);
597 //If entity already has owner, it won't get notification from EntityOwnershipService
598 java.util.Optional<EntityOwnershipState> ownershipStateOpt =
599 entityOwnershipService.getOwnershipState(candidateEntity);
600 if (ownershipStateOpt.isPresent()) {
601 EntityOwnershipState ownershipState = ownershipStateOpt.get();
602 if (ownershipState == EntityOwnershipState.OWNED_BY_OTHER) {
603 ovsdbConnectionInstance.setHasDeviceOwnership(false);
604 } else if (ownershipState == EntityOwnershipState.IS_OWNER) {
605 ovsdbConnectionInstance.setHasDeviceOwnership(true);
606 ovsdbConnectionInstance.registerCallbacks(instanceIdentifierCodec);
611 private void unregisterEntityForOwnership(final OvsdbConnectionInstance ovsdbConnectionInstance) {
612 ovsdbConnectionInstance.closeDeviceOwnershipCandidateRegistration();
613 entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity(), ovsdbConnectionInstance);
616 private void retryConnection(final InstanceIdentifier<Node> iid, final OvsdbNodeAugmentation ovsdbNode,
617 final ConnectionReconciliationTriggers trigger) {
618 final ReconciliationTask task = new ConnectionReconciliationTask(
619 reconciliationManager,
624 if (reconciliationManager.isEnqueued(task)) {
628 case ON_CONTROLLER_INITIATED_CONNECTION_FAILURE:
629 reconciliationManager.enqueueForRetry(task);
631 case ON_DISCONNECT: {
632 FluentFuture<Boolean> readNodeFuture;
633 try (ReadTransaction tx = db.newReadOnlyTransaction()) {
634 readNodeFuture = tx.exists(LogicalDatastoreType.CONFIGURATION, iid);
636 readNodeFuture.addCallback(new FutureCallback<Boolean>() {
638 public void onSuccess(final Boolean node) {
640 LOG.info("Disconnected/Failed connection {} was controller initiated, attempting "
641 + "reconnection", ovsdbNode.getConnectionInfo());
642 reconciliationManager.enqueue(task);
645 LOG.debug("Connection {} was switch initiated, no reconciliation is required",
646 iid.firstKeyOf(Node.class).getNodeId());
651 public void onFailure(final Throwable throwable) {
652 LOG.warn("Read Config/DS for Node failed! {}", iid, throwable);
654 }, MoreExecutors.directExecutor());
662 private void reconcileBridgeConfigurations(final OvsdbConnectionInstance client) {
663 final InstanceIdentifier<Node> nodeIid = client.getInstanceIdentifier();
664 final ReconciliationTask task = new BridgeConfigReconciliationTask(
665 reconciliationManager, OvsdbConnectionManager.this, nodeIid, client, instanceIdentifierCodec);
667 reconciliationManager.enqueue(task);
670 private static final class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener {
671 private final OvsdbConnectionManager cm;
672 private final EntityOwnershipListenerRegistration listenerRegistration;
674 OvsdbDeviceEntityOwnershipListener(final OvsdbConnectionManager cm,
675 final EntityOwnershipService entityOwnershipService) {
677 listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
680 public void close() {
681 listenerRegistration.close();
685 public void ownershipChanged(final EntityOwnershipChange ownershipChange) {
686 cm.handleOwnershipChanged(ownershipChange);
690 private enum ConnectionReconciliationTriggers {
692 Reconciliation trigger for scenario where controller's attempt
693 to connect to switch fails on config data store notification
695 ON_CONTROLLER_INITIATED_CONNECTION_FAILURE,
698 Reconciliation trigger for the scenario where controller
699 initiated connection disconnects.
704 private enum OwnershipStates {
706 NONOWNER("NON-OWNER");
708 private final String state;
710 OwnershipStates(final String state) {
715 public String toString() {