acb665a98c23a6fa0e7c6fbba9ccd674ede58d9c
[ovsdb.git] / southbound / southbound-impl / src / main / java / org / opendaylight / ovsdb / southbound / OvsdbConnectionManager.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.ovsdb.southbound;
9
10 import static org.opendaylight.ovsdb.lib.operations.Operations.op;
11
12 import java.net.InetAddress;
13 import java.net.UnknownHostException;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.ConcurrentHashMap;
18 import java.util.concurrent.ExecutionException;
19
20 import javax.annotation.Nonnull;
21
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
24 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.controller.md.sal.common.api.clustering.CandidateAlreadyRegisteredException;
26 import org.opendaylight.controller.md.sal.common.api.clustering.Entity;
27 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipCandidateRegistration;
28 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipChange;
29 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListener;
30 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipListenerRegistration;
31 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipService;
32 import org.opendaylight.controller.md.sal.common.api.clustering.EntityOwnershipState;
33 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
34 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
35 import org.opendaylight.ovsdb.lib.OvsdbClient;
36 import org.opendaylight.ovsdb.lib.OvsdbConnection;
37 import org.opendaylight.ovsdb.lib.OvsdbConnectionListener;
38 import org.opendaylight.ovsdb.lib.operations.Operation;
39 import org.opendaylight.ovsdb.lib.operations.OperationResult;
40 import org.opendaylight.ovsdb.lib.operations.Select;
41 import org.opendaylight.ovsdb.lib.schema.DatabaseSchema;
42 import org.opendaylight.ovsdb.lib.schema.GenericTableSchema;
43 import org.opendaylight.ovsdb.lib.schema.typed.TyperUtils;
44 import org.opendaylight.ovsdb.schema.openvswitch.OpenVSwitch;
45 import org.opendaylight.ovsdb.southbound.transactions.md.OvsdbNodeRemoveCommand;
46 import org.opendaylight.ovsdb.southbound.transactions.md.TransactionCommand;
47 import org.opendaylight.ovsdb.southbound.transactions.md.TransactionInvoker;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAttributes;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbNodeAugmentation;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ConnectionInfo;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.ovsdb.node.attributes.ManagedNodeEntry;
53 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
55 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 import com.google.common.base.Optional;
60 import com.google.common.base.Preconditions;
61 import com.google.common.util.concurrent.CheckedFuture;
62
63 public class OvsdbConnectionManager implements OvsdbConnectionListener, AutoCloseable {
64     private Map<ConnectionInfo, OvsdbConnectionInstance> clients =
65             new ConcurrentHashMap<>();
66     private static final Logger LOG = LoggerFactory.getLogger(OvsdbConnectionManager.class);
67     private static final String ENTITY_TYPE = "ovsdb";
68
69     private DataBroker db;
70     private TransactionInvoker txInvoker;
71     private Map<ConnectionInfo,InstanceIdentifier<Node>> instanceIdentifiers =
72             new ConcurrentHashMap<>();
73     private Map<Entity, OvsdbConnectionInstance> entityConnectionMap =
74             new ConcurrentHashMap<>();
75     private EntityOwnershipService entityOwnershipService;
76     private OvsdbDeviceEntityOwnershipListener ovsdbDeviceEntityOwnershipListener;
77     private OvsdbConnection ovsdbConnection;
78
79     public OvsdbConnectionManager(DataBroker db,TransactionInvoker txInvoker,
80                                   EntityOwnershipService entityOwnershipService,
81                                   OvsdbConnection ovsdbConnection) {
82         this.db = db;
83         this.txInvoker = txInvoker;
84         this.entityOwnershipService = entityOwnershipService;
85         this.ovsdbDeviceEntityOwnershipListener = new OvsdbDeviceEntityOwnershipListener(this, entityOwnershipService);
86         this.ovsdbConnection = ovsdbConnection;
87     }
88
89     @Override
90     public void connected(@Nonnull final OvsdbClient externalClient) {
91         LOG.info("Library connected {} from {}:{} to {}:{}",
92                 externalClient.getConnectionInfo().getType(),
93                 externalClient.getConnectionInfo().getRemoteAddress(),
94                 externalClient.getConnectionInfo().getRemotePort(),
95                 externalClient.getConnectionInfo().getLocalAddress(),
96                 externalClient.getConnectionInfo().getLocalPort());
97         List<String> databases = new ArrayList<>();
98         try {
99             databases = externalClient.getDatabases().get();
100         } catch (InterruptedException | ExecutionException e) {
101             LOG.warn("Unable to fetch database list");
102         }
103
104         if(databases.contains(SouthboundConstants.OPEN_V_SWITCH)) {
105             OvsdbConnectionInstance client = connectedButCallBacksNotRegistered(externalClient);
106             // Register Cluster Ownership for ConnectionInfo
107             registerEntityForOwnership(client);
108         }
109     }
110
111     public OvsdbConnectionInstance connectedButCallBacksNotRegistered(final OvsdbClient externalClient) {
112         LOG.info("OVSDB Connection from {}:{}",externalClient.getConnectionInfo().getRemoteAddress(),
113                 externalClient.getConnectionInfo().getRemotePort());
114         ConnectionInfo key = SouthboundMapper.createConnectionInfo(externalClient);
115         OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
116
117         // Check if existing ovsdbConnectionInstance for the OvsdbClient present.
118         // In such cases, we will see if the ovsdbConnectionInstance has same externalClient.
119         if (ovsdbConnectionInstance != null) {
120             if (ovsdbConnectionInstance.hasOvsdbClient(externalClient)) {
121                 LOG.warn("OVSDB Connection Instance {} already exists for client {}", key, externalClient);
122                 return ovsdbConnectionInstance;
123             }
124             LOG.warn("OVSDB Connection Instance {} being replaced with client {}", key, externalClient);
125
126             // Unregister Cluster Ownership for ConnectionInfo
127             // Because the ovsdbConnectionInstance is about to be completely replaced!
128             unregisterEntityForOwnership(ovsdbConnectionInstance);
129
130             ovsdbConnectionInstance.disconnect();
131
132             removeConnectionInstance(key);
133         }
134
135         ovsdbConnectionInstance = new OvsdbConnectionInstance(key, externalClient, txInvoker,
136                 getInstanceIdentifier(key));
137         ovsdbConnectionInstance.createTransactInvokers();
138         return ovsdbConnectionInstance;
139     }
140
141     @Override
142     public void disconnected(OvsdbClient client) {
143         LOG.info("Library disconnected {} from {}:{} to {}:{}. Cleaning up the operational data store",
144                 client.getConnectionInfo().getType(),
145                 client.getConnectionInfo().getRemoteAddress(),
146                 client.getConnectionInfo().getRemotePort(),
147                 client.getConnectionInfo().getLocalAddress(),
148                 client.getConnectionInfo().getLocalPort());
149         ConnectionInfo key = SouthboundMapper.createConnectionInfo(client);
150         OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(key);
151         if (ovsdbConnectionInstance != null) {
152             // Unregister Entity ownership as soon as possible ,so this instance should
153             // not be used as a candidate in Entity election (given that this instance is
154             // about to disconnect as well), if current owner get disconnected from
155             // OVSDB device.
156             unregisterEntityForOwnership(ovsdbConnectionInstance);
157
158             txInvoker.invoke(new OvsdbNodeRemoveCommand(ovsdbConnectionInstance, null, null));
159
160             removeConnectionInstance(key);
161         } else {
162             LOG.warn("disconnected : Connection instance not found for OVSDB Node {} ", key);
163         }
164         LOG.trace("OvsdbConnectionManager: exit disconnected client: {}", client);
165     }
166
167     public OvsdbClient connect(InstanceIdentifier<Node> iid,
168             OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
169         LOG.info("Connecting to {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
170
171         // TODO handle case where we already have a connection
172         // TODO use transaction chains to handle ordering issues between disconnected
173         // TODO and connected when writing to the operational store
174         InetAddress ip = SouthboundMapper.createInetAddress(ovsdbNode.getConnectionInfo().getRemoteIp());
175         OvsdbClient client = ovsdbConnection.connect(ip,
176                 ovsdbNode.getConnectionInfo().getRemotePort().getValue());
177         // For connections from the controller to the ovs instance, the library doesn't call
178         // this method for us
179         if (client != null) {
180             putInstanceIdentifier(ovsdbNode.getConnectionInfo(), iid.firstIdentifierOf(Node.class));
181             OvsdbConnectionInstance ovsdbConnectionInstance = connectedButCallBacksNotRegistered(client);
182             ovsdbConnectionInstance.setOvsdbNodeAugmentation(ovsdbNode);
183
184             // Register Cluster Ownership for ConnectionInfo
185             registerEntityForOwnership(ovsdbConnectionInstance);
186         } else {
187             LOG.warn("Failed to connect to OVSDB Node {}", ovsdbNode.getConnectionInfo());
188         }
189         return client;
190     }
191
192     public void disconnect(OvsdbNodeAugmentation ovsdbNode) throws UnknownHostException {
193         LOG.info("Disconnecting from {}", SouthboundUtil.connectionInfoToString(ovsdbNode.getConnectionInfo()));
194         OvsdbConnectionInstance client = getConnectionInstance(ovsdbNode.getConnectionInfo());
195         if (client != null) {
196             // Unregister Cluster Onwership for ConnectionInfo
197             unregisterEntityForOwnership(client);
198
199             client.disconnect();
200
201             removeInstanceIdentifier(ovsdbNode.getConnectionInfo());
202         } else {
203             LOG.debug("disconnect : connection instance not found for {}",ovsdbNode.getConnectionInfo());
204         }
205     }
206
207 /*    public void init(ConnectionInfo key) {
208         OvsdbConnectionInstance client = getConnectionInstance(key);
209
210         // TODO (FF): make sure that this cluster instance is the 'entity owner' fo the given OvsdbConnectionInstance ?
211
212         if (client != null) {
213
214              *  Note: registerCallbacks() is idemPotent... so if you call it repeatedly all is safe,
215              *  it only registersCallbacks on the *first* call.
216
217             client.registerCallbacks();
218         }
219     }
220 */
221     @Override
222     public void close() {
223         if (ovsdbDeviceEntityOwnershipListener != null) {
224             ovsdbDeviceEntityOwnershipListener.close();
225         }
226
227         for (OvsdbClient client: clients.values()) {
228             client.disconnect();
229         }
230     }
231
232     private void putConnectionInstance(ConnectionInfo key,OvsdbConnectionInstance instance) {
233         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
234         clients.put(connectionInfo, instance);
235     }
236
237     private void removeConnectionInstance(ConnectionInfo key) {
238         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
239         clients.remove(connectionInfo);
240     }
241
242     private void putInstanceIdentifier(ConnectionInfo key,InstanceIdentifier<Node> iid) {
243         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
244         instanceIdentifiers.put(connectionInfo, iid);
245     }
246
247     private void removeInstanceIdentifier(ConnectionInfo key) {
248         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
249         instanceIdentifiers.remove(connectionInfo);
250     }
251
252     public OvsdbConnectionInstance getConnectionInstance(ConnectionInfo key) {
253         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
254         return clients.get(connectionInfo);
255     }
256
257     public InstanceIdentifier<Node> getInstanceIdentifier(ConnectionInfo key) {
258         ConnectionInfo connectionInfo = SouthboundMapper.suppressLocalIpPort(key);
259         return instanceIdentifiers.get(connectionInfo);
260     }
261
262     public OvsdbConnectionInstance getConnectionInstance(OvsdbBridgeAttributes mn) {
263         Optional<OvsdbNodeAugmentation> optional = SouthboundUtil.getManagingNode(db, mn);
264         if (optional.isPresent()) {
265             return getConnectionInstance(optional.get().getConnectionInfo());
266         } else {
267             return null;
268         }
269     }
270
271     public OvsdbConnectionInstance getConnectionInstance(Node node) {
272         Preconditions.checkNotNull(node);
273         OvsdbNodeAugmentation ovsdbNode = node.getAugmentation(OvsdbNodeAugmentation.class);
274         OvsdbBridgeAugmentation ovsdbManagedNode = node.getAugmentation(OvsdbBridgeAugmentation.class);
275         if (ovsdbNode != null) {
276             return getConnectionInstance(ovsdbNode.getConnectionInfo());
277         } else if (ovsdbManagedNode != null) {
278             return getConnectionInstance(ovsdbManagedNode);
279         } else {
280             LOG.warn("This is not a node that gives any hint how to find its OVSDB Manager: {}",node);
281             return null;
282         }
283     }
284
285     public OvsdbConnectionInstance getConnectionInstance(InstanceIdentifier<Node> nodePath) {
286         try {
287             ReadOnlyTransaction transaction = db.newReadOnlyTransaction();
288             CheckedFuture<Optional<Node>, ReadFailedException> nodeFuture = transaction.read(
289                     LogicalDatastoreType.OPERATIONAL, nodePath);
290             transaction.close();
291             Optional<Node> optional = nodeFuture.get();
292             if (optional != null && optional.isPresent() && optional.get() != null) {
293                 return this.getConnectionInstance(optional.get());
294             } else {
295                 LOG.warn("Found non-topological node {} on path {}",optional);
296                 return null;
297             }
298         } catch (Exception e) {
299             LOG.warn("Failed to get Ovsdb Node {}",nodePath, e);
300             return null;
301         }
302     }
303
304     public OvsdbClient getClient(ConnectionInfo connectionInfo) {
305         return getConnectionInstance(connectionInfo);
306     }
307
308     public OvsdbClient getClient(OvsdbBridgeAttributes mn) {
309         return getConnectionInstance(mn);
310     }
311
312     public OvsdbClient getClient(Node node) {
313         return getConnectionInstance(node);
314     }
315
316     public Boolean getHasDeviceOwnership(ConnectionInfo connectionInfo) {
317         OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
318         if (ovsdbConnectionInstance == null) {
319             return Boolean.FALSE;
320         }
321         return ovsdbConnectionInstance.getHasDeviceOwnership();
322     }
323
324     private void handleOwnershipChanged(EntityOwnershipChange ownershipChange) {
325         OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(ownershipChange.getEntity());
326         LOG.debug("handleOwnershipChanged: {} event received for device {}",
327                 ownershipChange, ovsdbConnectionInstance != null ? ovsdbConnectionInstance.getConnectionInfo()
328                         : "that's currently NOT registered by *this* southbound plugin instance");
329
330         if (ovsdbConnectionInstance == null) {
331             if (ownershipChange.isOwner()) {
332                 LOG.warn("handleOwnershipChanged: *this* instance is elected as an owner of the device {} but it "
333                         + "is NOT registered for ownership", ownershipChange.getEntity());
334             } else {
335                 // EntityOwnershipService sends notification to all the nodes, irrespective of whether
336                 // that instance registered for the device ownership or not. It is to make sure that
337                 // If all the controller instance that was connected to the device are down, so the
338                 // running instance can clear up the operational data store even though it was not
339                 // connected to the device.
340                 LOG.debug("handleOwnershipChanged: No connection instance found for {}", ownershipChange.getEntity());
341             }
342
343             // If entity has no owner, clean up the operational data store (it's possible because owner controller
344             // might went down abruptly and didn't get a chance to clean up the operational data store.
345             if (!ownershipChange.hasOwner()) {
346                 LOG.info("{} has no owner, cleaning up the operational data store", ownershipChange.getEntity());
347                 cleanEntityOperationalData(ownershipChange.getEntity());
348             }
349             return;
350         }
351         //Connection detail need to be cached, irrespective of ownership result.
352         putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
353
354         if (ownershipChange.isOwner() == ovsdbConnectionInstance.getHasDeviceOwnership()) {
355             LOG.info("handleOwnershipChanged: no change in ownership for {}. Ownership status is : {}",
356                     ovsdbConnectionInstance.getConnectionInfo(), ovsdbConnectionInstance.getHasDeviceOwnership()
357                             ? SouthboundConstants.OWNERSHIPSTATES.OWNER.getState()
358                             : SouthboundConstants.OWNERSHIPSTATES.NONOWNER.getState());
359             return;
360         }
361
362         ovsdbConnectionInstance.setHasDeviceOwnership(ownershipChange.isOwner());
363         // You were not an owner, but now you are
364         if (ownershipChange.isOwner()) {
365             LOG.info("handleOwnershipChanged: *this* southbound plugin instance is an OWNER of the device {}",
366                     ovsdbConnectionInstance.getConnectionInfo());
367
368             //*this* instance of southbound plugin is owner of the device,
369             //so register for monitor callbacks
370             ovsdbConnectionInstance.registerCallbacks();
371
372         } else {
373             //You were owner of the device, but now you are not. With the current ownership
374             //grant mechanism, this scenario should not occur. Because this scenario will occur
375             //when this controller went down or switch flap the connection, but in both the case
376             //it will go through the re-registration process. We need to implement this condition
377             //when clustering service implement a ownership grant strategy which can revoke the
378             //device ownership for load balancing the devices across the instances.
379             //Once this condition occur, we should unregister the callback.
380             LOG.error("handleOwnershipChanged: *this* southbound plugin instance is no longer the owner of device {}."
381                     + "This should NOT happen.",
382                     ovsdbConnectionInstance.getNodeId().getValue());
383         }
384     }
385
386     private void cleanEntityOperationalData(Entity entity) {
387
388         //Do explicit cleanup rather than using OvsdbNodeRemoveCommand, because there
389         // are chances that other controller instance went down abruptly and it does
390         // not clear manager entry, which OvsdbNodeRemoveCommand look for before cleanup.
391
392         @SuppressWarnings("unchecked") final InstanceIdentifier<Node> nodeIid =
393                 (InstanceIdentifier<Node>) SouthboundUtil
394                         .getInstanceIdentifierCodec().bindingDeserializer(entity.getId());
395
396         txInvoker.invoke(new TransactionCommand() {
397             @Override
398             public void execute(ReadWriteTransaction transaction) {
399                 Optional<Node> ovsdbNodeOpt = SouthboundUtil.readNode(transaction, nodeIid);
400                 if (ovsdbNodeOpt.isPresent()) {
401                     Node ovsdbNode = ovsdbNodeOpt.get();
402                     OvsdbNodeAugmentation nodeAugmentation = ovsdbNode.getAugmentation(OvsdbNodeAugmentation.class);
403                     if (nodeAugmentation != null) {
404                         if (nodeAugmentation.getManagedNodeEntry() != null) {
405                             for (ManagedNodeEntry managedNode : nodeAugmentation.getManagedNodeEntry()) {
406                                 transaction.delete(
407                                         LogicalDatastoreType.OPERATIONAL, managedNode.getBridgeRef().getValue());
408                             }
409                         } else {
410                             LOG.debug("{} had no managed nodes", ovsdbNode.getNodeId().getValue());
411                         }
412                     }
413                     transaction.delete(LogicalDatastoreType.OPERATIONAL, nodeIid);
414                 }
415             }
416         });
417
418     }
419
420     private OpenVSwitch getOpenVswitchTableEntry(OvsdbConnectionInstance connectionInstance) {
421         DatabaseSchema dbSchema = null;
422         OpenVSwitch openVSwitchRow = null;
423         try {
424             dbSchema = connectionInstance.getSchema(OvsdbSchemaContants.databaseName).get();
425         } catch (InterruptedException | ExecutionException e) {
426             LOG.warn("Not able to fetch schema for database {} from device {}",
427                     OvsdbSchemaContants.databaseName,connectionInstance.getConnectionInfo(),e);
428         }
429         if (dbSchema != null) {
430             GenericTableSchema openVSwitchSchema = TyperUtils.getTableSchema(dbSchema, OpenVSwitch.class);
431
432             List<String> openVSwitchTableColumn = new ArrayList<>();
433             openVSwitchTableColumn.addAll(openVSwitchSchema.getColumns());
434             Select<GenericTableSchema> selectOperation = op.select(openVSwitchSchema);
435             selectOperation.setColumns(openVSwitchTableColumn);
436
437             List<Operation> operations = new ArrayList<>();
438             operations.add(selectOperation);
439             operations.add(op.comment("Fetching Open_VSwitch table rows"));
440             try {
441                 List<OperationResult> results = connectionInstance.transact(dbSchema, operations).get();
442                 if (results != null ) {
443                     OperationResult selectResult = results.get(0);
444                     openVSwitchRow = TyperUtils.getTypedRowWrapper(
445                             dbSchema,OpenVSwitch.class,selectResult.getRows().get(0));
446
447                 }
448             } catch (InterruptedException | ExecutionException e) {
449                 LOG.warn("Not able to fetch OpenVswitch table row from device {}",
450                         connectionInstance.getConnectionInfo(),e);
451             }
452         }
453         return openVSwitchRow;
454     }
455     private Entity getEntityFromConnectionInstance(@Nonnull OvsdbConnectionInstance ovsdbConnectionInstance) {
456         InstanceIdentifier<Node> iid = ovsdbConnectionInstance.getInstanceIdentifier();
457         if ( iid == null ) {
458             /* Switch initiated connection won't have iid, till it gets OpenVSwitch
459              * table update but update callback is always registered after ownership
460              * is granted. So we are explicitly fetch the row here to get the iid.
461              */
462             OpenVSwitch openvswitchRow = getOpenVswitchTableEntry(ovsdbConnectionInstance);
463             iid = SouthboundMapper.getInstanceIdentifier(openvswitchRow);
464             LOG.info("InstanceIdentifier {} generated for device "
465                     + "connection {}",iid,ovsdbConnectionInstance.getConnectionInfo());
466             ovsdbConnectionInstance.setInstanceIdentifier(iid);
467         }
468         YangInstanceIdentifier entityId = SouthboundUtil.getInstanceIdentifierCodec().getYangInstanceIdentifier(iid);
469         Entity deviceEntity = new Entity(ENTITY_TYPE, entityId);
470         LOG.debug("Entity {} created for device connection {}",
471                 deviceEntity, ovsdbConnectionInstance.getConnectionInfo());
472         return deviceEntity;
473     }
474
475     private OvsdbConnectionInstance getConnectionInstanceFromEntity(Entity entity) {
476         return entityConnectionMap.get(entity);
477     }
478
479     private void registerEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
480
481         Entity candidateEntity = getEntityFromConnectionInstance(ovsdbConnectionInstance);
482         entityConnectionMap.put(candidateEntity, ovsdbConnectionInstance);
483         ovsdbConnectionInstance.setConnectedEntity(candidateEntity);
484         try {
485             EntityOwnershipCandidateRegistration registration =
486                     entityOwnershipService.registerCandidate(candidateEntity);
487             ovsdbConnectionInstance.setDeviceOwnershipCandidateRegistration(registration);
488             LOG.info("OVSDB entity {} is registered for ownership.", candidateEntity);
489
490             //If entity already has owner, it won't get notification from EntityOwnershipService
491             //so cache the connection instances.
492             Optional<EntityOwnershipState> ownershipStateOpt =
493                     entityOwnershipService.getOwnershipState(candidateEntity);
494             if (ownershipStateOpt.isPresent()) {
495                 EntityOwnershipState ownershipState = ownershipStateOpt.get();
496                 if (ownershipState.hasOwner() && !ownershipState.isOwner()) {
497                     LOG.info("OVSDB entity {} is already owned by other southbound plugin "
498                                     + "instance, so *this* instance is NOT an OWNER of the device",
499                             ovsdbConnectionInstance.getConnectionInfo());
500                     putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
501                 }
502             }
503         } catch (CandidateAlreadyRegisteredException e) {
504             LOG.warn("OVSDB entity {} was already registered for ownership", candidateEntity, e);
505         }
506
507     }
508
509     private void unregisterEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
510         ovsdbConnectionInstance.closeDeviceOwnershipCandidateRegistration();
511         entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity());
512     }
513
514     private class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener {
515         private OvsdbConnectionManager cm;
516         private EntityOwnershipListenerRegistration listenerRegistration;
517
518         OvsdbDeviceEntityOwnershipListener(OvsdbConnectionManager cm, EntityOwnershipService entityOwnershipService) {
519             this.cm = cm;
520             listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
521         }
522         public void close() {
523             listenerRegistration.close();
524         }
525         @Override
526         public void ownershipChanged(EntityOwnershipChange ownershipChange) {
527             cm.handleOwnershipChanged(ownershipChange);
528         }
529     }
530 }