Bug 5596 Created lifecycle service
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / role / RoleManagerImpl.java
index b33b80249b1b26e7c1e04b970a4249da72436e26..716a9b38db51703378414e955d81d5611589b1ba 100644 (file)
@@ -20,6 +20,7 @@ import io.netty.util.TimerTask;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.Future;
@@ -68,6 +69,9 @@ import org.slf4j.LoggerFactory;
 public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, ServiceChangeListener {
     private static final Logger LOG = LoggerFactory.getLogger(RoleManagerImpl.class);
 
+    // Maximum limit of timeout retries when cleaning DS, to prevent infinite recursive loops
+    private static final int MAX_CLEAN_DS_RETRIES = 3;
+
     private DeviceInitializationPhaseHandler deviceInitializationPhaseHandler;
     private DeviceTerminationPhaseHandler deviceTerminationPhaseHandler;
     private final DataBroker dataBroker;
@@ -119,26 +123,26 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
             watchingEntities.remove(roleContext.getTxEntity());
             contexts.remove(roleContext.getDeviceInfo());
             if (roleContext.isTxCandidateRegistered()) {
-                LOG.info("Node {} was holder txEntity, so trying to remove device from operational DS.");
-                removeDeviceFromOperationalDS(roleContext.getDeviceInfo());
-            } else {
-                roleContext.close();
+                LOG.info("Node {} was holder txEntity, so trying to remove device from operational DS.", roleContext.getDeviceInfo().getNodeId().getValue());
+                removeDeviceFromOperationalDS(roleContext.getDeviceInfo(), MAX_CLEAN_DS_RETRIES);
             }
+            roleContext.close();
         }
     }
 
     @Override
     public void onDeviceContextLevelDown(final DeviceInfo deviceInfo) {
         LOG.trace("onDeviceContextLevelDown for node {}", deviceInfo.getNodeId());
-        final RoleContext roleContext = contexts.get(deviceInfo);
+        final RoleContext roleContext = contexts.remove(deviceInfo);
         if (roleContext != null) {
             LOG.debug("Found roleContext associated to deviceContext: {}, now trying close the roleContext", deviceInfo.getNodeId());
-            if (roleContext.isMainCandidateRegistered()) {
-                roleContext.unregisterCandidate(roleContext.getEntity());
-            } else {
-                contexts.remove(deviceInfo.getNodeId(), roleContext);
-                roleContext.close();
+            roleContext.setState(OFPContext.CONTEXT_STATE.TERMINATION);
+            roleContext.unregisterCandidate(roleContext.getEntity());
+            if (roleContext.isTxCandidateRegistered()) {
+                LOG.info("Node {} was holder txEntity, so trying to remove device from operational DS.", deviceInfo.getNodeId().getValue());
+                removeDeviceFromOperationalDS(roleContext.getDeviceInfo(), MAX_CLEAN_DS_RETRIES);
             }
+            roleContext.close();
         }
         deviceTerminationPhaseHandler.onDeviceContextLevelDown(deviceInfo);
     }
@@ -159,19 +163,24 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
         Preconditions.checkArgument(ownershipChange != null);
         final RoleContext roleContext = watchingEntities.get(ownershipChange.getEntity());
 
-        LOG.debug("Received EOS message: wasOwner:{} isOwner:{} hasOwner:{} inJeopardy:{} for entity type {} and node {}",
-                ownershipChange.wasOwner(), ownershipChange.isOwner(), ownershipChange.hasOwner(), ownershipChange.inJeopardy(),
-                ownershipChange.getEntity().getType(),
-                roleContext != null ? roleContext.getDeviceInfo().getNodeId() : "-> no watching entity, disregarding notification <-");
+        if (Objects.nonNull(roleContext) && !roleContext.getState().equals(OFPContext.CONTEXT_STATE.TERMINATION)) {
+
+            LOG.debug("Received EOS message: wasOwner:{} isOwner:{} hasOwner:{} inJeopardy:{} for entity type {} and node {}",
+                    ownershipChange.wasOwner(), ownershipChange.isOwner(), ownershipChange.hasOwner(), ownershipChange.inJeopardy(),
+                    ownershipChange.getEntity().getType(),
+                    roleContext.getDeviceInfo().getNodeId());
 
-        if (roleContext != null) {
             if (ownershipChange.getEntity().equals(roleContext.getEntity())) {
                 changeOwnershipForMainEntity(ownershipChange, roleContext);
             } else {
                 changeOwnershipForTxEntity(ownershipChange, roleContext);
             }
+
         } else {
-            LOG.debug("OwnershipChange {}", ownershipChange);
+
+            LOG.debug("Role context for entity type {} is in state closing, disregarding ownership change notification.", ownershipChange.getEntity().getType());
+            watchingEntities.remove(ownershipChange.getEntity());
+
         }
 
     }
@@ -204,7 +213,7 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
                 roleContext.unregisterCandidate(roleContext.getTxEntity());
                 if (ownershipChange.wasOwner() && !ownershipChange.isOwner() && !ownershipChange.hasOwner()) {
                     LOG.debug("Trying to remove from operational node: {}", roleContext.getDeviceInfo().getNodeId());
-                    removeDeviceFromOperationalDS(roleContext.getDeviceInfo());
+                    removeDeviceFromOperationalDS(roleContext.getDeviceInfo(), MAX_CLEAN_DS_RETRIES);
                 }
             } else {
                 contexts.remove(roleContext.getDeviceInfo(), roleContext);
@@ -242,9 +251,8 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
                     roleContext.unregisterCandidate(roleContext.getTxEntity());
                     if (!ownershipChange.hasOwner()) {
                         LOG.debug("Trying to remove from operational node: {}", roleContext.getDeviceInfo().getNodeId());
-                        removeDeviceFromOperationalDS(roleContext.getDeviceInfo());
+                        removeDeviceFromOperationalDS(roleContext.getDeviceInfo(), MAX_CLEAN_DS_RETRIES);
                     } else {
-                        final NodeId nodeId = roleContext.getDeviceInfo().getNodeId();
                         contexts.remove(roleContext.getDeviceInfo(), roleContext);
                         roleContext.close();
                         conductor.closeConnection(roleContext.getDeviceInfo());
@@ -252,7 +260,7 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
                 }
             }
         } else {
-            LOG.debug("Tx-EntityOwnershipRegistration is not active for entity {}", ownershipChange.getEntity().getType());
+            LOG.debug("Tx-EntityOwnershipRegistration is not active for entity type {} and node {}", ownershipChange.getEntity().getType(), roleContext.getDeviceInfo().getNodeId());
             watchingEntities.remove(roleContext.getTxEntity(), roleContext);
             contexts.remove(roleContext.getDeviceInfo(), roleContext);
             roleContext.close();
@@ -267,13 +275,15 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
             @Override
             public void onSuccess(@Nullable final RpcResult<SetRoleOutput> setRoleOutputRpcResult) {
                 LOG.info("Role {} successfully set on device {}", role, roleContext.getDeviceInfo().getNodeId());
-                notifyListenersRoleChangeOnDevice(roleContext.getDeviceInfo(), true, role, init);
+                if (!init) {
+                    notifyListenersRoleChangeOnDevice(roleContext.getDeviceInfo(), role);
+                }
             }
 
             @Override
             public void onFailure(@Nonnull final Throwable throwable) {
                 LOG.warn("Unable to set role {} on device {}", role, roleContext.getDeviceInfo().getNodeId());
-                notifyListenersRoleChangeOnDevice(roleContext.getDeviceInfo(), false, role, init);
+                conductor.closeConnection(roleContext.getDeviceInfo());
             }
         });
     }
@@ -306,13 +316,12 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
     }
 
     @VisibleForTesting
-    CheckedFuture<Void, TransactionCommitFailedException> removeDeviceFromOperationalDS(final DeviceInfo deviceInfo) {
-
+    CheckedFuture<Void, TransactionCommitFailedException> removeDeviceFromOperationalDS(final DeviceInfo deviceInfo, final int numRetries) {
         final WriteTransaction delWtx = dataBroker.newWriteOnlyTransaction();
         delWtx.delete(LogicalDatastoreType.OPERATIONAL, DeviceStateUtil.createNodeInstanceIdentifier(deviceInfo.getNodeId()));
         final CheckedFuture<Void, TransactionCommitFailedException> delFuture = delWtx.submit();
-        Futures.addCallback(delFuture, new FutureCallback<Void>() {
 
+        Futures.addCallback(delFuture, new FutureCallback<Void>() {
             @Override
             public void onSuccess(final Void result) {
                 LOG.debug("Delete Node {} was successful", deviceInfo);
@@ -324,14 +333,25 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
 
             @Override
             public void onFailure(@Nonnull final Throwable t) {
-                LOG.warn("Delete Node {} failed. {}", deviceInfo, t);
-                contexts.remove(deviceInfo);
+                // If we have any retries left, we will try to clean the datastore again
+                if (numRetries > 0) {
+                    // We "used" one retry here, so decrement it
+                    final int curRetries = numRetries - 1;
+                    LOG.debug("Delete node {} failed with exception {}. Trying again (retries left: {})", deviceInfo.getNodeId(), t, curRetries);
+                    // Recursive call to this method with "one less" retry
+                    removeDeviceFromOperationalDS(deviceInfo, curRetries);
+                    return;
+                }
+
+                // No retries left, so we will just close the role context, and ignore datastore cleanup
+                LOG.warn("Delete node {} failed with exception {}. No retries left, aborting", deviceInfo.getNodeId(), t);
                 final RoleContext roleContext = contexts.remove(deviceInfo);
                 if (roleContext != null) {
                     roleContext.close();
                 }
             }
         });
+
         return delFuture;
     }
 
@@ -387,20 +407,18 @@ public class RoleManagerImpl implements RoleManager, EntityOwnershipListener, Se
      * Notifies registered listener on role change. Role is the new role on device
      * If initialization phase is true, we may skip service starting
      * @param deviceInfo
-     * @param success true if role change on device done ok, false otherwise
      * @param role new role meant to be set on device
-     * @param initializationPhase if true, then skipp services start
      */
     @VisibleForTesting
-    void notifyListenersRoleChangeOnDevice(final DeviceInfo deviceInfo, final boolean success, final OfpRole role, final boolean initializationPhase){
+    void notifyListenersRoleChangeOnDevice(final DeviceInfo deviceInfo, final OfpRole role){
         LOG.debug("Notifying registered listeners for role change, no. of listeners {}", listeners.size());
         for (final RoleChangeListener listener : listeners) {
-            listener.roleChangeOnDevice(deviceInfo, success, role, initializationPhase);
+            listener.roleChangeOnDevice(deviceInfo, role);
         }
     }
 
     @Override
-    public <T extends OFPContext> T gainContext(DeviceInfo deviceInfo) {
+    public <T extends OFPContext> T gainContext(final DeviceInfo deviceInfo) {
         return (T) contexts.get(deviceInfo);
     }
 }