Bug 8152: Transaction is already opened 73/55873/3
authorAndrej Mak <andrej.mak@pantheon.tech>
Wed, 19 Apr 2017 06:12:36 +0000 (08:12 +0200)
committerAndrej Mak <andrej.mak@pantheon.tech>
Mon, 24 Apr 2017 12:20:05 +0000 (14:20 +0200)
This issue happens, when for some reason transaction
submit or cancel message isn't delivered to master
node. With current implementation, only one device
transaction can be opened at the time, so submit or
cancel delivery failure will lock device forever.

To prevent this, this patch introduces write trancaction
idle timeout. Write transaction actor will be stopped
and its device transaction cancelled, when no message
is received for given time. Cancellation unlocks device,
so mountpouint is usable again.

Change-Id: I37bef30038cf6fd10fa5149a3fa949540ac16eab
Signed-off-by: Andrej Mak <andrej.mak@pantheon.tech>
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManager.java
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/NetconfNodeActor.java
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/actors/WriteTransactionActor.java
netconf/netconf-topology-singleton/src/main/java/org/opendaylight/netconf/topology/singleton/impl/utils/NetconfTopologySetup.java
netconf/netconf-topology-singleton/src/main/resources/org/opendaylight/blueprint/netconf-topology-singleton.xml
netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java
netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/actors/WriteTransactionActorTest.java
netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/tx/WriteOnlyTransactionTest.java

index cfd9dd81131d438f55665b2c473ee652bc270d00..eeff4d2c59756594f3e372c84de88e260709a5a4 100644 (file)
@@ -16,6 +16,7 @@ import io.netty.util.concurrent.EventExecutor;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.Nonnull;
 import org.opendaylight.controller.cluster.ActorSystemProvider;
 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
@@ -51,6 +52,7 @@ import org.opendaylight.yangtools.concepts.ListenerRegistration;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import scala.concurrent.duration.Duration;
 
 public class NetconfTopologyManager
         implements ClusteredDataTreeChangeListener<Node>, NetconfTopologySingletonService, AutoCloseable {
@@ -74,13 +76,15 @@ public class NetconfTopologyManager
     private final EventExecutor eventExecutor;
     private final NetconfClientDispatcher clientDispatcher;
     private final String topologyId;
+    private final Duration writeTxIdleTimeout;
 
     public NetconfTopologyManager(final DataBroker dataBroker, final RpcProviderRegistry rpcProviderRegistry,
-                           final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
-                           final BindingAwareBroker bindingAwareBroker,
-                           final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
-                           final Broker domBroker, final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor,
-                           final NetconfClientDispatcher clientDispatcher, final String topologyId) {
+                                  final ClusterSingletonServiceProvider clusterSingletonServiceProvider,
+                                  final BindingAwareBroker bindingAwareBroker,
+                                  final ScheduledThreadPool keepaliveExecutor, final ThreadPool processingExecutor,
+                                  final Broker domBroker, final ActorSystemProvider actorSystemProvider, final EventExecutor eventExecutor,
+                                  final NetconfClientDispatcher clientDispatcher, final String topologyId,
+                                  final int writeTxIdleTimeout) {
         this.dataBroker = Preconditions.checkNotNull(dataBroker);
         this.rpcProviderRegistry = Preconditions.checkNotNull(rpcProviderRegistry);
         this.clusterSingletonServiceProvider = Preconditions.checkNotNull(clusterSingletonServiceProvider);
@@ -92,6 +96,7 @@ public class NetconfTopologyManager
         this.eventExecutor = Preconditions.checkNotNull(eventExecutor);
         this.clientDispatcher = Preconditions.checkNotNull(clientDispatcher);
         this.topologyId = Preconditions.checkNotNull(topologyId);
+        this.writeTxIdleTimeout = Duration.apply(writeTxIdleTimeout, TimeUnit.SECONDS);
     }
 
     // Blueprint init method
@@ -101,7 +106,7 @@ public class NetconfTopologyManager
 
     @Override
     public void onDataTreeChanged(@Nonnull final Collection<DataTreeModification<Node>> changes) {
-        for (DataTreeModification<Node> change : changes) {
+        for (final DataTreeModification<Node> change : changes) {
             final DataObjectModification<Node> rootNode = change.getRootNode();
             final InstanceIdentifier<Node> dataModifIdent = change.getRootPath().getRootIdentifier();
             final NodeId nodeId = NetconfTopologyUtils.getNodeId(rootNode.getIdentifier());
@@ -129,7 +134,7 @@ public class NetconfTopologyManager
         }
     }
 
-    private void refreshNetconfDeviceContext(InstanceIdentifier<Node> instanceIdentifier, Node node) {
+    private void refreshNetconfDeviceContext(final InstanceIdentifier<Node> instanceIdentifier, final Node node) {
         final NetconfTopologyContext context = contexts.get(instanceIdentifier);
         context.refresh(createSetup(instanceIdentifier, node));
     }
@@ -158,7 +163,7 @@ public class NetconfTopologyManager
             try {
                 clusterRegistrations.get(instanceIdentifier).close();
                 contexts.get(instanceIdentifier).closeFinal();
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 LOG.warn("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier);
             }
             contexts.remove(instanceIdentifier);
@@ -175,14 +180,14 @@ public class NetconfTopologyManager
         contexts.forEach((instanceIdentifier, netconfTopologyContext) -> {
             try {
                 netconfTopologyContext.closeFinal();
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 LOG.error("Error at closing topology context. InstanceIdentifier: " + instanceIdentifier, e);
             }
         });
         clusterRegistrations.forEach((instanceIdentifier, clusterSingletonServiceRegistration) -> {
             try {
                 clusterSingletonServiceRegistration.close();
-            } catch (Exception e) {
+            } catch (final Exception e) {
                 LOG.error("Error at unregistering from cluster. InstanceIdentifier: " + instanceIdentifier, e);
             }
         });
@@ -190,18 +195,18 @@ public class NetconfTopologyManager
         clusterRegistrations.clear();
     }
 
-    private ListenerRegistration<NetconfTopologyManager> registerDataTreeChangeListener(String topologyId) {
+    private ListenerRegistration<NetconfTopologyManager> registerDataTreeChangeListener(final String topologyId) {
         final WriteTransaction wtx = dataBroker.newWriteOnlyTransaction();
         initTopology(wtx, LogicalDatastoreType.CONFIGURATION, topologyId);
         initTopology(wtx, LogicalDatastoreType.OPERATIONAL, topologyId);
         Futures.addCallback(wtx.submit(), new FutureCallback<Void>() {
             @Override
-            public void onSuccess(Void result) {
+            public void onSuccess(final Void result) {
                 LOG.debug("topology initialization successful");
             }
 
             @Override
-            public void onFailure(@Nonnull Throwable throwable) {
+            public void onFailure(@Nonnull final Throwable throwable) {
                 LOG.error("Unable to initialize netconf-topology, {}", throwable);
             }
         });
@@ -212,7 +217,7 @@ public class NetconfTopologyManager
                                 NetconfTopologyUtils.createTopologyListPath(topologyId).child(Node.class)), this);
     }
 
-    private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType, String topologyId) {
+    private void initTopology(final WriteTransaction wtx, final LogicalDatastoreType datastoreType, final String topologyId) {
         final NetworkTopology networkTopology = new NetworkTopologyBuilder().build();
         final InstanceIdentifier<NetworkTopology> networkTopologyId =
                 InstanceIdentifier.builder(NetworkTopology.class).build();
@@ -236,7 +241,8 @@ public class NetconfTopologyManager
                 .setKeepaliveExecutor(keepaliveExecutor)
                 .setProcessingExecutor(processingExecutor)
                 .setTopologyId(topologyId)
-                .setNetconfClientDispatcher(clientDispatcher);
+                .setNetconfClientDispatcher(clientDispatcher)
+                .setIdleTimeout(writeTxIdleTimeout);
 
         return builder.build();
     }
index b12b6f6107d78663a8422aa13925cd7d965b4c53..571483afa9ba24e64a9c7b6f4529479620bf6a05 100644 (file)
@@ -61,16 +61,18 @@ import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import scala.concurrent.duration.Duration;
 
 public class NetconfNodeActor extends UntypedActor {
 
     private static final Logger LOG = LoggerFactory.getLogger(NetconfNodeActor.class);
 
-    private NetconfTopologySetup setup;
-    private RemoteDeviceId id;
     private final SchemaSourceRegistry schemaRegistry;
     private final SchemaRepository schemaRepository;
+    private final Duration writeTxIdleTimeout;
 
+    private RemoteDeviceId id;
+    private NetconfTopologySetup setup;
     private List<SourceIdentifier> sourceIdentifiers;
     private DOMRpcService deviceRpc;
     private SlaveSalFacade slaveSalManager;
@@ -86,12 +88,13 @@ public class NetconfNodeActor extends UntypedActor {
     }
 
     private NetconfNodeActor(final NetconfTopologySetup setup,
-                             final RemoteDeviceId id, SchemaSourceRegistry schemaRegistry,
+                             final RemoteDeviceId id, final SchemaSourceRegistry schemaRegistry,
                              final SchemaRepository schemaRepository) {
         this.setup = setup;
         this.id = id;
         this.schemaRegistry = schemaRegistry;
         this.schemaRepository = schemaRepository;
+        this.writeTxIdleTimeout = setup.getIdleTimeout();
     }
 
     @Override
@@ -131,7 +134,7 @@ public class NetconfNodeActor extends UntypedActor {
         } else if (message instanceof NewWriteTransactionRequest) { // master
             try {
                 final DOMDataWriteTransaction tx = deviceDataBroker.newWriteOnlyTransaction();
-                final ActorRef txActor = context().actorOf(WriteTransactionActor.props(tx));
+                final ActorRef txActor = context().actorOf(WriteTransactionActor.props(tx, writeTxIdleTimeout));
                 sender().tell(new NewWriteTransactionReply(txActor), self());
             } catch (final Throwable t) {
                 sender().tell(t, self());
@@ -166,7 +169,7 @@ public class NetconfNodeActor extends UntypedActor {
             public void onSuccess(final YangTextSchemaSource yangTextSchemaSource) {
                 try {
                     sender.tell(new YangTextSchemaSourceSerializationProxy(yangTextSchemaSource), getSelf());
-                } catch (IOException exception) {
+                } catch (final IOException exception) {
                     sender.tell(exception.getCause(), getSelf());
                 }
             }
@@ -206,7 +209,7 @@ public class NetconfNodeActor extends UntypedActor {
         });
     }
 
-    private void registerSlaveMountPoint(ActorRef masterReference) {
+    private void registerSlaveMountPoint(final ActorRef masterReference) {
         if (this.slaveSalManager != null) {
             slaveSalManager.close();
         }
@@ -230,11 +233,11 @@ public class NetconfNodeActor extends UntypedActor {
         });
     }
 
-    private DOMRpcService getDOMRpcService(ActorRef masterReference) {
+    private DOMRpcService getDOMRpcService(final ActorRef masterReference) {
         return new ProxyDOMRpcService(setup.getActorSystem(), masterReference, id);
     }
 
-    private CheckedFuture<SchemaContext, SchemaResolutionException> getSchemaContext(ActorRef masterReference) {
+    private CheckedFuture<SchemaContext, SchemaResolutionException> getSchemaContext(final ActorRef masterReference) {
 
         final RemoteYangTextSourceProvider remoteYangTextSourceProvider =
                 new ProxyYangTextSourceProvider(masterReference, getContext());
index a729a127e3ab5b5f593af0535032865bde77f674..aa9ddf3e1bab882ebb11ef73f94ae4cdb4ce1880 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.netconf.topology.singleton.impl.actors;
 
 import akka.actor.ActorRef;
 import akka.actor.Props;
+import akka.actor.ReceiveTimeout;
 import akka.actor.UntypedActor;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
@@ -25,26 +26,37 @@ import org.opendaylight.netconf.topology.singleton.messages.transactions.MergeRe
 import org.opendaylight.netconf.topology.singleton.messages.transactions.PutRequest;
 import org.opendaylight.netconf.topology.singleton.messages.transactions.SubmitReply;
 import org.opendaylight.netconf.topology.singleton.messages.transactions.SubmitRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import scala.concurrent.duration.Duration;
 
 /**
  * WriteTransactionActor is an interface to device's {@link DOMDataReadOnlyTransaction} for cluster nodes.
  */
 public class WriteTransactionActor extends UntypedActor {
 
+    private static final Logger LOG = LoggerFactory.getLogger(WriteTransactionActor.class);
+
     private final DOMDataWriteTransaction tx;
+    private final long idleTimeout;
 
     /**
      * Creates new actor Props.
      *
      * @param tx delegate device write transaction
+     * @param idleTimeout idle time in seconds, after which transaction is closed automatically
      * @return props
      */
-    static Props props(final DOMDataWriteTransaction tx) {
-        return Props.create(WriteTransactionActor.class, () -> new WriteTransactionActor(tx));
+    static Props props(final DOMDataWriteTransaction tx, final Duration idleTimeout) {
+        return Props.create(WriteTransactionActor.class, () -> new WriteTransactionActor(tx, idleTimeout));
     }
 
-    private WriteTransactionActor(final DOMDataWriteTransaction tx) {
+    private WriteTransactionActor(final DOMDataWriteTransaction tx, final Duration idleTimeout) {
         this.tx = tx;
+        this.idleTimeout = idleTimeout.toSeconds();
+        if (this.idleTimeout > 0) {
+            context().setReceiveTimeout(idleTimeout);
+        }
     }
 
     @Override
@@ -64,6 +76,11 @@ public class WriteTransactionActor extends UntypedActor {
             cancel();
         } else if (message instanceof SubmitRequest) {
             submit(sender(), self());
+        } else if (message instanceof ReceiveTimeout) {
+            LOG.warn("Haven't received any message for {} seconds, cancelling transaction and stopping actor",
+                    idleTimeout);
+            tx.cancel();
+            context().stop(self());
         } else {
             unhandled(message);
         }
index d607f33d2bc4d87eda5ede5148f262dcbf6b77a4..c143e3066c8f6f30d85183bcac52a924925a160b 100644 (file)
@@ -20,6 +20,7 @@ import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvid
 import org.opendaylight.netconf.client.NetconfClientDispatcher;
 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import scala.concurrent.duration.Duration;
 
 public class NetconfTopologySetup {
 
@@ -36,6 +37,7 @@ public class NetconfTopologySetup {
     private final EventExecutor eventExecutor;
     private final NetconfClientDispatcher netconfClientDispatcher;
     private final String topologyId;
+    private final Duration idleTimeout;
     private NetconfTopologySetup(final NetconfTopologySetupBuilder builder) {
         this.clusterSingletonServiceProvider = builder.getClusterSingletonServiceProvider();
         this.rpcProviderRegistry = builder.getRpcProviderRegistry();
@@ -50,6 +52,7 @@ public class NetconfTopologySetup {
         this.eventExecutor = builder.getEventExecutor();
         this.netconfClientDispatcher = builder.getNetconfClientDispatcher();
         this.topologyId = builder.getTopologyId();
+        this.idleTimeout = builder.getIdleTimeout();
     }
 
     public ClusterSingletonServiceProvider getClusterSingletonServiceProvider() {
@@ -104,6 +107,10 @@ public class NetconfTopologySetup {
         return netconfClientDispatcher;
     }
 
+    public Duration getIdleTimeout() {
+        return idleTimeout;
+    }
+
     public static class NetconfTopologySetupBuilder {
 
         private ClusterSingletonServiceProvider clusterSingletonServiceProvider;
@@ -119,6 +126,7 @@ public class NetconfTopologySetup {
         private EventExecutor eventExecutor;
         private String topologyId;
         private NetconfClientDispatcher netconfClientDispatcher;
+        private Duration idleTimeout;
 
         public NetconfTopologySetupBuilder(){
         }
@@ -177,7 +185,7 @@ public class NetconfTopologySetup {
             return bindingAwareBroker;
         }
 
-        public NetconfTopologySetupBuilder setBindingAwareBroker(BindingAwareBroker bindingAwareBroker) {
+        public NetconfTopologySetupBuilder setBindingAwareBroker(final BindingAwareBroker bindingAwareBroker) {
             this.bindingAwareBroker = bindingAwareBroker;
             return this;
         }
@@ -186,7 +194,7 @@ public class NetconfTopologySetup {
             return keepaliveExecutor;
         }
 
-        public NetconfTopologySetupBuilder setKeepaliveExecutor(ScheduledThreadPool keepaliveExecutor) {
+        public NetconfTopologySetupBuilder setKeepaliveExecutor(final ScheduledThreadPool keepaliveExecutor) {
             this.keepaliveExecutor = keepaliveExecutor;
             return this;
         }
@@ -195,7 +203,7 @@ public class NetconfTopologySetup {
             return processingExecutor;
         }
 
-        public NetconfTopologySetupBuilder setProcessingExecutor(ThreadPool processingExecutor) {
+        public NetconfTopologySetupBuilder setProcessingExecutor(final ThreadPool processingExecutor) {
             this.processingExecutor = processingExecutor;
             return this;
         }
@@ -204,7 +212,7 @@ public class NetconfTopologySetup {
             return domBroker;
         }
 
-        public NetconfTopologySetupBuilder setDomBroker(Broker domBroker) {
+        public NetconfTopologySetupBuilder setDomBroker(final Broker domBroker) {
             this.domBroker = domBroker;
             return this;
         }
@@ -213,7 +221,7 @@ public class NetconfTopologySetup {
             return actorSystem;
         }
 
-        public NetconfTopologySetupBuilder setActorSystem(ActorSystem actorSystem) {
+        public NetconfTopologySetupBuilder setActorSystem(final ActorSystem actorSystem) {
             this.actorSystem = actorSystem;
             return this;
         }
@@ -222,7 +230,7 @@ public class NetconfTopologySetup {
             return eventExecutor;
         }
 
-        public NetconfTopologySetupBuilder setEventExecutor(EventExecutor eventExecutor) {
+        public NetconfTopologySetupBuilder setEventExecutor(final EventExecutor eventExecutor) {
             this.eventExecutor = eventExecutor;
             return this;
         }
@@ -231,7 +239,7 @@ public class NetconfTopologySetup {
             return topologyId;
         }
 
-        public NetconfTopologySetupBuilder setTopologyId(String topologyId) {
+        public NetconfTopologySetupBuilder setTopologyId(final String topologyId) {
             this.topologyId = topologyId;
             return this;
         }
@@ -240,11 +248,20 @@ public class NetconfTopologySetup {
             return netconfClientDispatcher;
         }
 
-        public NetconfTopologySetupBuilder setNetconfClientDispatcher(NetconfClientDispatcher clientDispatcher) {
+        public NetconfTopologySetupBuilder setNetconfClientDispatcher(final NetconfClientDispatcher clientDispatcher) {
             this.netconfClientDispatcher = clientDispatcher;
             return this;
         }
 
+        public NetconfTopologySetupBuilder setIdleTimeout(final Duration idleTimeout) {
+            this.idleTimeout = idleTimeout;
+            return this;
+        }
+
+        private Duration getIdleTimeout() {
+            return idleTimeout;
+        }
+
         public static NetconfTopologySetupBuilder create() {
             return new NetconfTopologySetupBuilder();
         }
index 8fced1956ea89d516b1ecb6c83ec60a3f3233bb8..c9811be00c30b8dd8d2a6235658e7b4416f39f7f 100644 (file)
@@ -47,6 +47,7 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
         <argument ref="eventExecutor"/>
         <argument ref="clientDispatcherDependency"/>
         <argument value="topology-netconf"/>
+        <argument value="0"/>
     </bean>
     <service ref="netconfTopologyManager"
              interface="org.opendaylight.netconf.topology.singleton.api.NetconfTopologySingletonService"/>
index 9a3749c14e4e59dc225a25d581150e105c45c9d5..beb3f277bbb758043b2cb2b57c9802b1e4a67157 100644 (file)
@@ -86,8 +86,9 @@ public class NetconfTopologyManagerTest {
 
         netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry,
                 clusterSingletonServiceProvider, bindingAwareBroker, keepaliveExecutor, processingExecutor, domBroker,
-                actorSystemProvider, eventExecutor, clientDispatcher, topologyId);
+                actorSystemProvider, eventExecutor, clientDispatcher, topologyId, 0);
     }
+
     @Test
     public void testWriteConfiguration() throws Exception {
 
index d95d15840b5d7ece72fffcc5353911749e1a431e..158ee468ba2ef37bd4236d3e8b60e653865c1ab8 100644 (file)
@@ -8,6 +8,7 @@
 
 package org.opendaylight.netconf.topology.singleton.impl.actors;
 
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -44,6 +45,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import scala.concurrent.Await;
 import scala.concurrent.Future;
+import scala.concurrent.duration.Duration;
 
 public class WriteTransactionActorTest {
     private static final YangInstanceIdentifier PATH = YangInstanceIdentifier.EMPTY;
@@ -65,7 +67,8 @@ public class WriteTransactionActorTest {
         node = Builders.containerBuilder()
                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(QName.create("cont")))
                 .build();
-        actorRef = TestActorRef.create(system, WriteTransactionActor.props(deviceWriteTx), "testA");
+        actorRef = TestActorRef.create(system, WriteTransactionActor.props(deviceWriteTx,
+                Duration.apply(2, TimeUnit.SECONDS)), "testA");
     }
 
     @After
@@ -124,4 +127,12 @@ public class WriteTransactionActorTest {
         verify(deviceWriteTx).submit();
     }
 
+    @Test
+    public void testIdleTimeout() throws Exception {
+        final TestProbe probe = new TestProbe(system);
+        probe.watch(actorRef);
+        verify(deviceWriteTx, timeout(3000)).cancel();
+        probe.expectTerminated(actorRef, TIMEOUT.duration());
+    }
+
 }
\ No newline at end of file
index 293fa0dec01a5711bddbf687e07c9b8293d4009a..deb8235d7d0d8d0863769a35f85d278cfa10fa13 100644 (file)
@@ -92,6 +92,7 @@ public class WriteOnlyTransactionTest {
                 new InetSocketAddress(InetAddress.getByName("127.0.0.1"), 9999));
 
         final NetconfTopologySetup setup = mock(NetconfTopologySetup.class);
+        doReturn(Duration.apply(0, TimeUnit.SECONDS)).when(setup).getIdleTimeout();
         final Props props = NetconfNodeActor.props(setup, remoteDeviceId, DEFAULT_SCHEMA_REPOSITORY,
                 DEFAULT_SCHEMA_REPOSITORY);