OPNFLWPLUG-1080: Nodes not populated in operational/opendaylight-inventory:nodes... 72/89072/9
authorVenkataSatya Jonnadula <rsankar@luminanetworks.com>
Thu, 16 Apr 2020 11:14:52 +0000 (16:44 +0530)
committerArunprakash D <d.arunprakash@ericsson.com>
Wed, 29 Apr 2020 11:17:32 +0000 (11:17 +0000)
OPNFLWPLUG-1082: When switch changes owner, all cluster members delete switch inventory operational

Signed-off-by: VenkataSatya Jonnadula <rsankar@luminanetworks.com>
Change-Id: I6ceb3e71dcf86bd83fc9a3f03621502ac5144e95

openflowplugin-api/src/main/java/org/opendaylight/openflowplugin/api/openflow/configuration/ConfigurationProperty.java
openflowplugin-api/src/main/yang/openflow-provider-config.yang
openflowplugin-blueprint-config/src/main/resources/initial/openflowplugin.cfg
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/OpenFlowPluginProviderImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/lifecycle/ContextChainHolderImpl.java
openflowplugin-impl/src/main/java/org/opendaylight/openflowplugin/impl/services/AbstractRequestCallback.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/ConfigurationServiceFactoryImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/configuration/OpenFlowProviderConfigImplTest.java
openflowplugin-impl/src/test/java/org/opendaylight/openflowplugin/impl/lifecycle/ContextChainHolderImplTest.java

index 9084eb4f29801b13f2640f8fb05767af3437b6b4..4a5a58a563fd8e18f05403d30bd908611e7b7ecd 100644 (file)
@@ -119,7 +119,11 @@ public enum ConfigurationProperty {
     /**
      * Device connection hold time property type.
      */
-    DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS;
+    DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS,
+    /**
+     * Delay for Device removal from Operational DataStore.
+     */
+    DEVICE_DATASTORE_REMOVAL_DELAY;
 
     private static final Map<String, ConfigurationProperty> KEY_VALUE_MAP;
 
index 527a23b36bb1f32ef2de7fc42e31f6c9f0ce3ccb..66645a52f72a2c7f0017598a43a2c7b143df026e 100644 (file)
@@ -212,5 +212,14 @@ module openflow-provider-config {
             type uint16;
             default 0;
         }
+
+        leaf device-datastore-removal-delay {
+            description "Delay (in milliseconds) before device is removed from
+            the operational data store in the event of device disconnection
+            from the controller.";
+
+            type non-zero-uint32-type;
+            default 500;
+        }
     }
 }
index 31889a49b85dd90df881a0bc0bfd16688081fc99..9306919e728a211a201515effba390874603aba2 100644 (file)
 #
 # device-connection-hold-time-in-seconds=0
 
+#
+# Delay (in milliseconds) before device is removed from the operational data
+# store in the event of device disconnection from the controller.
+#
+# device-datastore-removal-delay=500
+
 #############################################################################
 #                                                                           #
 #            Forwarding Rule Manager Application Configuration              #
index f62184a85d4bd609ad20e0998bc8041e572b7dad..073a12df16c433b1ae61db8987054f351bb37c54 100644 (file)
@@ -275,7 +275,8 @@ public class OpenFlowPluginProviderImpl implements
                 executorService,
                 singletonServicesProvider,
                 entityOwnershipService,
-                mastershipChangeServiceManager);
+                mastershipChangeServiceManager,
+                config);
 
         contextChainHolder.addManager(deviceManager);
         contextChainHolder.addManager(statisticsManager);
index d7d33f6237bfa888433a02e6334226df4f6d1086..58344301c38d105788f451d242ff15eae92efc32 100644 (file)
@@ -88,6 +88,8 @@ public class ConfigurationServiceFactoryImpl implements ConfigurationServiceFact
                             providerConfig.getDeviceConnectionRateLimitPerMin().toString())
                     .put(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString(),
                             providerConfig.getDeviceConnectionHoldTimeInSeconds().toString())
+                    .put(ConfigurationProperty.DEVICE_DATASTORE_REMOVAL_DELAY.toString(),
+                            providerConfig.getDeviceDatastoreRemovalDelay().getValue().toString())
                     .build());
         }
 
index e1283c7e5e2d1c3c47b81eabd0eef6aa361bf7ad..a959f6972c97bc1cfffdc1647c8e7e18351f29f0 100644 (file)
@@ -185,4 +185,13 @@ public class OpenFlowProviderConfigImpl implements OpenflowProviderConfig {
         return service.getProperty(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString(),
                 Uint16::valueOf);
     }
+
+    @Override
+    public NonZeroUint32Type getDeviceDatastoreRemovalDelay() {
+        final Long property = service.getProperty(
+                ConfigurationProperty.DEVICE_DATASTORE_REMOVAL_DELAY.toString(),
+                Long::valueOf);
+
+        return new NonZeroUint32Type(property);
+    }
 }
index 53f750d83032c0d65c2c7097750cfc1e86394cfb..4393b96537ecd575d075f3dd05702a1bf8c8d1fa 100644 (file)
@@ -12,6 +12,7 @@ import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.HashMap;
 import java.util.List;
@@ -21,6 +22,9 @@ import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 import java.util.stream.Collectors;
@@ -28,6 +32,7 @@ import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipChange;
 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipListenerRegistration;
 import org.opendaylight.mdsal.eos.binding.api.EntityOwnershipService;
+import org.opendaylight.mdsal.eos.common.api.EntityOwnershipState;
 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
 import org.opendaylight.openflowplugin.api.openflow.OFPManager;
 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
@@ -52,6 +57,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.N
 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.mdsal.core.general.entity.rev150930.Entity;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.rf.state.rev170713.ResultState;
 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
 import org.opendaylight.yangtools.yang.common.Uint8;
@@ -65,6 +71,7 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
     private static final String CONTEXT_CREATED_FOR_CONNECTION = " context created for connection: {}";
     private static final long REMOVE_DEVICE_FROM_DS_TIMEOUT = 5000L;
     private static final String ASYNC_SERVICE_ENTITY_TYPE = "org.opendaylight.mdsal.AsyncServiceCloseEntityType";
+    private static final String SERVICE_ENTITY_TYPE = "org.opendaylight.mdsal.ServiceEntityType";
     private static final String SEPARATOR = ":";
     private final Map<DeviceInfo, ContextChain> contextChainMap = new ConcurrentHashMap<>();
     private final Map<DeviceInfo, ? super ConnectionContext> connectingDevices = new ConcurrentHashMap<>();
@@ -72,6 +79,14 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
     private final ClusterSingletonServiceProvider singletonServiceProvider;
     private final ExecutorService executorService;
     private final OwnershipChangeListener ownershipChangeListener;
+    private final ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(true)
+            .setNameFormat("node-cleaner-%d").setUncaughtExceptionHandler((thread, throwable) -> {
+                LOG.warn("Uncaught exception while removing node data from operational datastore.", throwable);
+            }).build();
+    private final ScheduledExecutorService nodeCleanerExecutor = Executors.newScheduledThreadPool(
+            Runtime.getRuntime().availableProcessors() , threadFactory);
+    private final EntityOwnershipService entityOwnershipService;
+    private final OpenflowProviderConfig config;
     private DeviceManager deviceManager;
     private RpcManager rpcManager;
     private StatisticsManager statisticsManager;
@@ -80,11 +95,14 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
     public ContextChainHolderImpl(final ExecutorService executorService,
                                   final ClusterSingletonServiceProvider singletonServiceProvider,
                                   final EntityOwnershipService entityOwnershipService,
-                                  final OwnershipChangeListener ownershipChangeListener) {
+                                  final OwnershipChangeListener ownershipChangeListener,
+                                  final OpenflowProviderConfig config) {
         this.singletonServiceProvider = singletonServiceProvider;
         this.executorService = executorService;
         this.ownershipChangeListener = ownershipChangeListener;
         this.ownershipChangeListener.setMasterChecker(this);
+        this.entityOwnershipService = entityOwnershipService;
+        this.config = config;
         this.eosListenerRegistration = Objects
                 .requireNonNull(entityOwnershipService.registerListener(ASYNC_SERVICE_ENTITY_TYPE, this));
     }
@@ -275,11 +293,17 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
         contextChainMap.clear();
         eosListenerRegistration.close();
         OF_EVENT_LOG.debug("EOS registration closed for all devices");
+        nodeCleanerExecutor.shutdownNow();
     }
 
     @Override
     @SuppressFBWarnings("BC_UNCONFIRMED_CAST_OF_RETURN_VALUE")
     public void ownershipChanged(final EntityOwnershipChange entityOwnershipChange) {
+        LOG.info("Entity ownership change received for node : {}", entityOwnershipChange);
+        if (entityOwnershipChange.inJeopardy()) {
+            LOG.warn("Controller is in Jeopardy, ignore ownership change notification. {}", entityOwnershipChange);
+            return;
+        }
         if (entityOwnershipChange.getState().hasOwner()) {
             return;
         }
@@ -294,33 +318,46 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
                 .getName();
 
         if (entityName != null && entityName.startsWith("openflow:")) {
-            LOG.debug("Entity {} has no owner", entityName);
+            if (nodeCleanerExecutor.isShutdown()) {
+                LOG.warn("Node cleaner executor thread-pool is down.");
+                return;
+            }
+            LOG.debug("Device {} will be removed from datastore in {} msec, if it's not transient notification.",
+                    entityName, config.getDeviceDatastoreRemovalDelay().getValue());
             final String dpnId = getDpnIdFromNodeName(entityName);
-            try {
-                //TODO:Remove notifications
-                final KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier =
-                        DeviceStateUtil.createNodeInstanceIdentifier(new NodeId(entityName));
-                deviceManager.sendNodeRemovedNotification(nodeInstanceIdentifier);
-                LOG.info("Try to remove device {} from operational DS", entityName);
-                ListenableFuture<?> future = deviceManager.removeDeviceFromOperationalDS(nodeInstanceIdentifier);
-                Futures.addCallback(future, new FutureCallback<Object>() {
-                    @Override
-                    public void onSuccess(final Object result) {
-                        LOG.debug("Node removed from Oper DS, Node: {}", dpnId);
-                        OF_EVENT_LOG.debug("Node removed from Oper DS, Node: {}", dpnId);
+            nodeCleanerExecutor.schedule(() -> {
+                try {
+                    Optional<EntityOwnershipState> ownershipState = getCurrentOwnershipStatus(entityName);
+                    if (!ownershipState.isPresent()
+                            || Objects.equals(ownershipState.get(), EntityOwnershipState.NO_OWNER)) {
+                        LOG.debug("Entity {} has no owner", entityName);
+                        final KeyedInstanceIdentifier<Node, NodeKey> nodeInstanceIdentifier =
+                                DeviceStateUtil.createNodeInstanceIdentifier(new NodeId(entityName));
+                        deviceManager.sendNodeRemovedNotification(nodeInstanceIdentifier);
+                        LOG.info("Try to remove device {} from operational DS", entityName);
+                        ListenableFuture<?> future =
+                                deviceManager.removeDeviceFromOperationalDS(nodeInstanceIdentifier);
+                        Futures.addCallback(future, new FutureCallback<Object>() {
+                            @Override
+                            public void onSuccess(final Object result) {
+                                LOG.debug("Node removed from Oper DS, Node: {}", dpnId);
+                                OF_EVENT_LOG.debug("Node removed from Oper DS, Node: {}", dpnId);
+                            }
+
+                            @Override
+                            public void onFailure(final Throwable throwable) {
+                                LOG.error("Could not remove device {} from operational DS", dpnId, throwable);
+                            }
+                        }, MoreExecutors.directExecutor());
+                        future.get(REMOVE_DEVICE_FROM_DS_TIMEOUT, TimeUnit.MILLISECONDS);
+                    } else {
+                        LOG.warn("Seems like device is still owned by other controller instance. Skip deleting {} "
+                                + "node from operational datastore.", entityName);
                     }
-
-                    @Override
-                    public void onFailure(final Throwable throwable) {
-                        LOG.error("Could not remove device {} from operational DS", dpnId, throwable);
-                    }
-                }, MoreExecutors.directExecutor());
-                future.get(REMOVE_DEVICE_FROM_DS_TIMEOUT, TimeUnit.MILLISECONDS);
-            } catch (TimeoutException | ExecutionException | NullPointerException | InterruptedException e) {
-                LOG.warn("Not able to remove device {} from operational DS. ", entityName, e);
-            }
-            OF_EVENT_LOG.debug("Node removed, Node: {}", dpnId);
-            LOG.info("Removing device from operational DS {} was successful", dpnId);
+                } catch (TimeoutException | ExecutionException | NullPointerException | InterruptedException e) {
+                    LOG.warn("Not able to remove device {} from operational DS. ", entityName, e);
+                }
+            }, config.getDeviceDatastoreRemovalDelay().getValue().toJava(), TimeUnit.MILLISECONDS);
         }
     }
 
@@ -388,4 +425,22 @@ public class ContextChainHolderImpl implements ContextChainHolder, MasterChecker
     private String getDpnIdFromNodeName(final String nodeName) {
         return nodeName.substring(nodeName.lastIndexOf(SEPARATOR) + 1);
     }
+
+    private Optional<EntityOwnershipState> getCurrentOwnershipStatus(final String nodeId) {
+        org.opendaylight.mdsal.eos.binding.api.Entity entity = createNodeEntity(nodeId);
+        Optional<EntityOwnershipState> ownershipStatus
+                = entityOwnershipService.getOwnershipState(entity);
+
+        if (ownershipStatus.isPresent()) {
+            LOG.debug("Current ownership status for node {} is {}", nodeId, ownershipStatus.get());
+            return Optional.of(ownershipStatus.get());
+        } else {
+            LOG.trace("Ownership status is not available for node {}", nodeId);
+        }
+        return Optional.empty();
+    }
+
+    private org.opendaylight.mdsal.eos.binding.api.Entity createNodeEntity(final String nodeId) {
+        return new org.opendaylight.mdsal.eos.binding.api.Entity(ASYNC_SERVICE_ENTITY_TYPE, nodeId);
+    }
 }
index e589fcb2ad7352ea31e56cc2e04271de700601a8..a146d8bda3b199657b66e8581595399f23c8a0f0 100644 (file)
@@ -68,8 +68,15 @@ public abstract class AbstractRequestCallback<T> implements FutureCallback<OfHea
             builder = RpcResultBuilder.<T>failed().withError(RpcError.ErrorType.APPLICATION, errorString, throwable);
             spyMessage(StatisticsGroup.TO_SWITCH_SUBMIT_FAILURE);
         } else {
-            builder = RpcResultBuilder.<T>failed()
-                    .withError(RpcError.ErrorType.APPLICATION, throwable.getMessage(), throwable);
+            if (throwable != null) {
+                builder = RpcResultBuilder.<T>failed()
+                        .withError(RpcError.ErrorType.APPLICATION, throwable.getMessage(), throwable);
+            } else {
+                Throwable deviceReadFailedThrowable = new Throwable("Failed to read from device.");
+                builder = RpcResultBuilder.<T>failed()
+                        .withError(RpcError.ErrorType.APPLICATION, deviceReadFailedThrowable.getMessage(),
+                                deviceReadFailedThrowable);
+            }
             spyMessage(StatisticsGroup.TO_SWITCH_SUBMIT_ERROR);
         }
 
index 703f5773ff04597745ea85c5b56ebd90298c4ebe..7039c5edc5586d20e3f5c3f1f3f8bdc7fc31bc23 100644 (file)
@@ -34,7 +34,7 @@ import org.opendaylight.yangtools.yang.common.Uint32;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ConfigurationServiceFactoryImplTest {
-    private static final int CONFIG_PROP_COUNT = 24;
+    private static final int CONFIG_PROP_COUNT = 25;
     private static final boolean IS_STATISTICS_POLLING_ON = true;
     private static final int BARRIER_COUNT_LIMIT = 2000;
     private static final long BARRIER_INTERVAL_TIMEOUT_LIMIT = 3000;
@@ -54,6 +54,7 @@ public class ConfigurationServiceFactoryImplTest {
     private static final Uint32 THREAD_POOL_TIMEOUT = Uint32.valueOf(60);
     private static final Uint16 DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = Uint16.ZERO;
     private static final Uint16 DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = Uint16.valueOf(60);
+    private static final long DEVICE_DATASTORE_REMOVAL_DELAY = 500;
 
     @Mock
     private OpenflowProviderConfig config;
@@ -90,6 +91,7 @@ public class ConfigurationServiceFactoryImplTest {
         when(config.getThreadPoolTimeout()).thenReturn(THREAD_POOL_TIMEOUT);
         when(config.getDeviceConnectionRateLimitPerMin()).thenReturn(DEVICE_CONNECTION_RATE_LIMIT_PER_MIN);
         when(config.getDeviceConnectionHoldTimeInSeconds()).thenReturn(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS);
+        when(config.getDeviceDatastoreRemovalDelay()).thenReturn(new NonZeroUint32Type(DEVICE_DATASTORE_REMOVAL_DELAY));
 
         final Map<String, String> properties = new Hashtable<>();
         properties.put(ConfigurationProperty.IS_STATISTICS_POLLING_ON.toString(),
index 18b8a8f55ff815f36c84d37f864623d2c8957906..e16908634b3a0d9cc4aed21b0ee6ef0ffac5e435 100644 (file)
@@ -44,6 +44,7 @@ public class OpenFlowProviderConfigImplTest {
     private static final Uint32 THREAD_POOL_TIMEOUT = Uint32.valueOf(60);
     private static final Uint16 DEVICE_CONNECTION_RATE_LIMIT_PER_MIN = Uint16.ZERO;
     private static final Uint16 DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS = Uint16.valueOf(60);
+    private static final long DEVICE_DATASTORE_REMOVAL_DELAY = 500L;
 
     @Mock
     private ConfigurationService configurationService;
@@ -88,6 +89,8 @@ public class OpenFlowProviderConfigImplTest {
         when(configurationService.getProperty(
                 eq(ConfigurationProperty.DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS.toString()),
                 any())).thenReturn(DEVICE_CONNECTION_HOLD_TIME_IN_SECONDS);
+        when(configurationService.getProperty(eq(ConfigurationProperty.DEVICE_DATASTORE_REMOVAL_DELAY.toString()),
+                any())).thenReturn(DEVICE_DATASTORE_REMOVAL_DELAY);
         openflowProviderConfig = new OpenFlowProviderConfigImpl(configurationService);
     }
 
@@ -188,4 +191,10 @@ public class OpenFlowProviderConfigImplTest {
                 openflowProviderConfig.getDeviceConnectionHoldTimeInSeconds());
     }
 
+    @Test
+    public void getDeviceDatastoreRemovalDelay() {
+        assertEquals(DEVICE_DATASTORE_REMOVAL_DELAY,
+                openflowProviderConfig.getDeviceDatastoreRemovalDelay().getValue().toJava());
+    }
+
 }
\ No newline at end of file
index 59157d89adf1e353f9978963652f19d5135ad382..96dcb510bc5fbda32e07a51999b387ee44e91f72 100644 (file)
@@ -40,6 +40,8 @@ import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsContext
 import org.opendaylight.openflowplugin.api.openflow.statistics.StatisticsManager;
 import org.opendaylight.openflowplugin.impl.mastership.MastershipChangeServiceManagerImpl;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.protocol.rev130731.FeaturesReply;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.NonZeroUint32Type;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflow.provider.config.rev160510.OpenflowProviderConfig;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.openflowplugin.rf.state.rev170713.ResultState;
 import org.opendaylight.yangtools.yang.common.Uint8;
 
@@ -49,6 +51,7 @@ public class ContextChainHolderImplTest {
     private static final String ENTITY_TEST = "EntityTest";
     private static final String OPENFLOW_TEST = "openflow:test";
     private static final Uint8 AUXILIARY_ID = Uint8.ZERO;
+    private static final Long DEVICE_DATASTORE_REMOVAL_DELAY = 500L;
     @Mock
     private StatisticsManager statisticsManager;
     @Mock
@@ -83,6 +86,8 @@ public class ContextChainHolderImplTest {
     private ReconciliationFrameworkEvent reconciliationFrameworkEvent;
     @Mock
     private FeaturesReply featuresReply;
+    @Mock
+    private OpenflowProviderConfig config;
 
     private ContextChainHolderImpl contextChainHolder;
     private ReconciliationFrameworkRegistration registration;
@@ -104,6 +109,8 @@ public class ContextChainHolderImplTest {
                 .thenReturn(entityOwnershipListenerRegistration);
         Mockito.when(connectionContext.getFeatures()).thenReturn(featuresReply);
         Mockito.when(featuresReply.getAuxiliaryId()).thenReturn(AUXILIARY_ID);
+        Mockito.when(config.getDeviceDatastoreRemovalDelay())
+                .thenReturn(new NonZeroUint32Type(DEVICE_DATASTORE_REMOVAL_DELAY));
 
         registration = manager.reconciliationFrameworkRegistration(reconciliationFrameworkEvent);
 
@@ -111,7 +118,8 @@ public class ContextChainHolderImplTest {
                 executorService,
                 singletonServicesProvider,
                 entityOwnershipService,
-                manager);
+                manager,
+                config);
         contextChainHolder.addManager(statisticsManager);
         contextChainHolder.addManager(rpcManager);
         contextChainHolder.addManager(deviceManager);
@@ -258,7 +266,7 @@ public class ContextChainHolderImplTest {
                 EntityOwnershipChangeState.LOCAL_OWNERSHIP_LOST_NO_OWNER
         );
         contextChainHolder.ownershipChanged(ownershipChange);
-        Mockito.verify(deviceManager).removeDeviceFromOperationalDS(Mockito.any());
+        Mockito.verify(deviceManager, Mockito.timeout(1000)).removeDeviceFromOperationalDS(Mockito.any());
     }
 
     @Test