+
+ public Boolean getHasDeviceOwnership(ConnectionInfo connectionInfo) {
+ OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
+ if (ovsdbConnectionInstance == null) {
+ return Boolean.FALSE;
+ }
+ return ovsdbConnectionInstance.getHasDeviceOwnership();
+ }
+
+ public void setHasDeviceOwnership(ConnectionInfo connectionInfo, Boolean hasDeviceOwnership) {
+ OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstance(connectionInfo);
+ if (ovsdbConnectionInstance != null) {
+ ovsdbConnectionInstance.setHasDeviceOwnership(hasDeviceOwnership);
+ }
+ }
+
+ private void handleOwnershipChanged(EntityOwnershipChange ownershipChange) {
+ OvsdbConnectionInstance ovsdbConnectionInstance = getConnectionInstanceFromEntity(ownershipChange.getEntity());
+ LOG.info("handleOwnershipChanged: {} event received for device {}",
+ ownershipChange, ovsdbConnectionInstance != null ? ovsdbConnectionInstance.getConnectionInfo()
+ : "THAT'S NOT REGISTERED BY THIS SOUTHBOUND PLUGIN INSTANCE");
+
+ if (ovsdbConnectionInstance == null) {
+ if (ownershipChange.isOwner()) {
+ LOG.warn("handleOwnershipChanged: found no connection instance for {}", ownershipChange.getEntity());
+ } else {
+ // EntityOwnershipService sends notification to all the nodes, irrespective of whether
+ // that instance registered for the device ownership or not. It is to make sure that
+ // If all the controller instance that was connected to the device are down, so the
+ // running instance can clear up the operational data store even though it was not
+ // connected to the device.
+ LOG.debug("handleOwnershipChanged: found no connection instance for {}", ownershipChange.getEntity());
+ }
+
+ // If entity has no owner, clean up the operational data store (it's possible because owner controller
+ // might went down abruptly and didn't get a chance to clean up the operational data store.
+ if (!ownershipChange.hasOwner()) {
+ LOG.debug("{} has no onwer, cleaning up the operational data store", ownershipChange.getEntity());
+ // Below code might look weird but it's required. We want to give first opportunity to the
+ // previous owner of the device to clean up the operational data store if there is no owner now.
+ // That way we will avoid lot of nasty md-sal exceptions because of concurrent delete.
+ if (ownershipChange.wasOwner()) {
+ cleanEntityOperationalData(ownershipChange.getEntity());
+ }
+ // If first cleanEntityOperationalData() was called, this call will be no-op.
+ cleanEntityOperationalData(ownershipChange.getEntity());
+ }
+ return;
+ }
+ //Connection detail need to be cached, irrespective of ownership result.
+ putConnectionInstance(ovsdbConnectionInstance.getMDConnectionInfo(),ovsdbConnectionInstance);
+
+ if (ownershipChange.isOwner() == ovsdbConnectionInstance.getHasDeviceOwnership()) {
+ LOG.debug("handleOwnershipChanged: no change in ownership for {}. Ownership status is : {}",
+ ovsdbConnectionInstance.getConnectionInfo(), ovsdbConnectionInstance.getHasDeviceOwnership());
+ return;
+ }
+
+ ovsdbConnectionInstance.setHasDeviceOwnership(ownershipChange.isOwner());
+ // You were not an owner, but now you are
+ if (ownershipChange.isOwner()) {
+ LOG.info("handleOwnershipChanged: *this* southbound plugin instance is owner of device {}",
+ ovsdbConnectionInstance.getConnectionInfo());
+
+ //*this* instance of southbound plugin is owner of the device,
+ //so register for monitor callbacks
+ ovsdbConnectionInstance.registerCallbacks();
+
+ } else {
+ //You were owner of the device, but now you are not. With the current ownership
+ //grant mechanism, this scenario should not occur. Because this scenario will occur
+ //when this controller went down or switch flap the connection, but in both the case
+ //it will go through the re-registration process. We need to implement this condition
+ //when clustering service implement a ownership grant strategy which can revoke the
+ //device ownership for load balancing the devices across the instances.
+ //Once this condition occur, we should unregister the callback.
+ LOG.error("handleOwnershipChanged: *this* southbound plugin instance is no longer the owner of device {}",
+ ovsdbConnectionInstance.getNodeId().getValue());
+ }
+ }
+
+ private void cleanEntityOperationalData(Entity entity) {
+
+ InstanceIdentifier<Node> nodeIid = (InstanceIdentifier<Node>) SouthboundUtil
+ .getInstanceIdentifierCodec().bindingDeserializer(entity.getId());
+
+ final ReadWriteTransaction transaction = db.newReadWriteTransaction();
+ Optional<Node> node = SouthboundUtil.readNode(transaction, nodeIid);
+ if (node.isPresent()) {
+ SouthboundUtil.deleteNode(transaction, nodeIid);
+ }
+ }
+
+ private OpenVSwitch getOpenVswitchTableEntry(OvsdbConnectionInstance connectionInstance) {
+ DatabaseSchema dbSchema = null;
+ OpenVSwitch openVSwitchRow = null;
+ try {
+ dbSchema = connectionInstance.getSchema(OvsdbSchemaContants.databaseName).get();
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.warn("Not able to fetch schema for database {} from device {}",
+ OvsdbSchemaContants.databaseName,connectionInstance.getConnectionInfo(),e);
+ }
+ if (dbSchema != null) {
+ GenericTableSchema openVSwitchSchema = TyperUtils.getTableSchema(dbSchema, OpenVSwitch.class);
+
+ List<String> openVSwitchTableColumn = new ArrayList<String>();
+ openVSwitchTableColumn.addAll(openVSwitchSchema.getColumns());
+ Select<GenericTableSchema> selectOperation = op.select(openVSwitchSchema);
+ selectOperation.setColumns(openVSwitchTableColumn);;
+
+ ArrayList<Operation> operations = new ArrayList<Operation>();
+ operations.add(selectOperation);
+ operations.add(op.comment("Fetching Open_VSwitch table rows"));
+ List<OperationResult> results = null;
+ try {
+ results = connectionInstance.transact(dbSchema, operations).get();
+ if (results != null ) {
+ OperationResult selectResult = results.get(0);
+ openVSwitchRow = TyperUtils.getTypedRowWrapper(
+ dbSchema,OpenVSwitch.class,selectResult.getRows().get(0));
+
+ }
+ } catch (InterruptedException | ExecutionException e) {
+ LOG.warn("Not able to fetch OpenVswitch table row from device {}",
+ connectionInstance.getConnectionInfo(),e);
+ }
+ }
+ return openVSwitchRow;
+ }
+ private Entity getEntityFromConnectionInstance(@Nonnull OvsdbConnectionInstance ovsdbConnectionInstance) {
+ YangInstanceIdentifier entityId = null;
+ InstanceIdentifier<Node> iid = ovsdbConnectionInstance.getInstanceIdentifier();;
+ if ( iid == null ) {
+ /* Switch initiated connection won't have iid, till it gets OpenVSwitch
+ * table update but update callback is always registered after ownership
+ * is granted. So we are explicitly fetch the row here to get the iid.
+ */
+ OpenVSwitch openvswitchRow = getOpenVswitchTableEntry(ovsdbConnectionInstance);
+ iid = SouthboundMapper.getInstanceIdentifier(openvswitchRow);
+ LOG.info("InstanceIdentifier {} generated for device "
+ + "connection {}",iid,ovsdbConnectionInstance.getConnectionInfo());
+
+ }
+ entityId = SouthboundUtil.getInstanceIdentifierCodec().getYangInstanceIdentifier(iid);
+ Entity deviceEntity = new Entity(ENTITY_TYPE, entityId);
+ LOG.debug("Entity {} created for device connection {}",
+ deviceEntity, ovsdbConnectionInstance.getConnectionInfo());
+ return deviceEntity;
+ }
+
+ private OvsdbConnectionInstance getConnectionInstanceFromEntity(Entity entity) {
+ return entityConnectionMap.get(entity);
+ }
+
+ private void registerEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
+
+ Entity candidateEntity = getEntityFromConnectionInstance(ovsdbConnectionInstance);
+ entityConnectionMap.put(candidateEntity, ovsdbConnectionInstance);
+ ovsdbConnectionInstance.setConnectedEntity(candidateEntity);
+ try {
+ EntityOwnershipCandidateRegistration registration =
+ entityOwnershipService.registerCandidate(candidateEntity);
+ ovsdbConnectionInstance.setDeviceOwnershipCandidateRegistration(registration);
+ LOG.info("OVSDB entity {} is registred for ownership.", candidateEntity);
+ } catch (CandidateAlreadyRegisteredException e) {
+ LOG.warn("OVSDB entity {} was already registered for {} ownership", candidateEntity, e);
+ }
+
+ }
+
+ private void unregisterEntityForOwnership(OvsdbConnectionInstance ovsdbConnectionInstance) {
+ ovsdbConnectionInstance.closeDeviceOwnershipCandidateRegistration();
+ entityConnectionMap.remove(ovsdbConnectionInstance.getConnectedEntity());
+ }
+
+ private class OvsdbDeviceEntityOwnershipListener implements EntityOwnershipListener {
+ private OvsdbConnectionManager cm;
+ private EntityOwnershipListenerRegistration listenerRegistration;
+
+ OvsdbDeviceEntityOwnershipListener(OvsdbConnectionManager cm, EntityOwnershipService entityOwnershipService) {
+ this.cm = cm;
+ listenerRegistration = entityOwnershipService.registerListener(ENTITY_TYPE, this);
+ }
+ public void close() {
+ listenerRegistration.close();
+ }
+ @Override
+ public void ownershipChanged(EntityOwnershipChange ownershipChange) {
+ cm.handleOwnershipChanged(ownershipChange);
+ }
+ }