X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fnetconf-topology-singleton%2Fsrc%2Ftest%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Ftopology%2Fsingleton%2Fimpl%2FNetconfTopologyManagerTest.java;h=c61d1f8e93961c7511990a873079aa12028a1a8b;hb=4f0f6f3a97b17d1e6914149c4c999d8801596c41;hp=060ed0e987c964b666864add576c872dafc26773;hpb=5c963db1eea6d898d040aba5d48cd3ff7590fae0;p=netconf.git diff --git a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java index 060ed0e987..c61d1f8e93 100644 --- a/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java +++ b/netconf/netconf-topology-singleton/src/test/java/org/opendaylight/netconf/topology/singleton/impl/NetconfTopologyManagerTest.java @@ -5,32 +5,43 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.netconf.topology.singleton.impl; -import static junit.framework.TestCase.assertFalse; +import static org.awaitility.Awaitility.await; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.MockitoAnnotations.initMocks; import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE; +import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.SUBTREE_MODIFIED; import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE; -import com.google.common.util.concurrent.Futures; +import akka.util.Timeout; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableSet; import io.netty.util.concurrent.EventExecutor; -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; +import java.util.HashMap; import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; import javax.annotation.Nonnull; import org.junit.Before; import org.junit.Test; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.opendaylight.aaa.encrypt.AAAEncryptionService; import org.opendaylight.controller.cluster.ActorSystemProvider; import org.opendaylight.controller.config.threadpool.ScheduledThreadPool; import org.opendaylight.controller.config.threadpool.ThreadPool; @@ -38,14 +49,17 @@ import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; -import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; +import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction; +import org.opendaylight.controller.md.sal.binding.test.ConstantSchemaAbstractDataBrokerTest; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; -import org.opendaylight.controller.sal.binding.api.BindingAwareBroker; +import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService; import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry; -import org.opendaylight.controller.sal.core.api.Broker; +import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider; import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration; +import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier; import org.opendaylight.netconf.client.NetconfClientDispatcher; +import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup; import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress; @@ -55,203 +69,310 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev15 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.Config; import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.ConfigBuilder; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NetworkTopology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; +import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.Topology; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey; -import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.binding.YangModuleInfo; public class NetconfTopologyManagerTest { + private static final int ACTOR_RESPONSE_WAIT_TIME = 10; + private static final String TOPOLOGY_ID = "topologyID"; - private final String topologyId = "topologyID"; private NetconfTopologyManager netconfTopologyManager; @Mock - private DataBroker dataBroker; + private ClusterSingletonServiceProvider clusterSingletonServiceProvider; @Mock - private ClusterSingletonServiceProvider clusterSingletonServiceProvider; + private ListenerRegistration mockListenerReg; + + private DataBroker dataBroker; + + private final Map, Function> + mockContextMap = new HashMap<>(); @Before - public void setUp() { + public void setUp() throws Exception { initMocks(this); + ConstantSchemaAbstractDataBrokerTest dataBrokerTest = new ConstantSchemaAbstractDataBrokerTest(false) { + @Override + protected Set getModuleInfos() throws Exception { + return ImmutableSet.of(BindingReflections.getModuleInfo(NetworkTopology.class), + BindingReflections.getModuleInfo(Topology.class)); + } + }; + + dataBrokerTest.setup(); + dataBroker = spy(dataBrokerTest.getDataBroker()); + final RpcProviderRegistry rpcProviderRegistry = mock(RpcProviderRegistry.class); - final BindingAwareBroker bindingAwareBroker = mock(BindingAwareBroker.class); final ScheduledThreadPool keepaliveExecutor = mock(ScheduledThreadPool.class); - final ThreadPool processingExecutor = mock(ThreadPool.class); - final Broker domBroker = mock(Broker.class); + final ThreadPool processingThreadPool = mock(ThreadPool.class); + final ExecutorService processingService = mock(ExecutorService.class); + doReturn(processingService).when(processingThreadPool).getExecutor(); final ActorSystemProvider actorSystemProvider = mock(ActorSystemProvider.class); final EventExecutor eventExecutor = mock(EventExecutor.class); final NetconfClientDispatcher clientDispatcher = mock(NetconfClientDispatcher.class); + final DOMMountPointService mountPointService = mock(DOMMountPointService.class); + final AAAEncryptionService encryptionService = mock(AAAEncryptionService.class); final Config config = new ConfigBuilder().setWriteTransactionIdleTimeout(0).build(); netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry, - clusterSingletonServiceProvider, bindingAwareBroker, keepaliveExecutor, processingExecutor, domBroker, - actorSystemProvider, eventExecutor, clientDispatcher, topologyId, config); + clusterSingletonServiceProvider, keepaliveExecutor, processingThreadPool, + actorSystemProvider, eventExecutor, clientDispatcher, TOPOLOGY_ID, config, + mountPointService, encryptionService) { + @Override + protected NetconfTopologyContext newNetconfTopologyContext(final NetconfTopologySetup setup, + final ServiceGroupIdentifier serviceGroupIdent, final Timeout actorResponseWaitTime) { + assertEquals(ACTOR_RESPONSE_WAIT_TIME, actorResponseWaitTime.duration().toSeconds()); + return Objects.requireNonNull(mockContextMap.get(setup.getInstanceIdentifier()), + "No mock context for " + setup.getInstanceIdentifier()).apply(setup); + } + }; + + doNothing().when(mockListenerReg).close(); + doReturn(mockListenerReg).when(dataBroker).registerDataTreeChangeListener(any(), any()); } @Test - public void testWriteConfiguration() throws Exception { + public void testRegisterDataTreeChangeListener() throws Exception { - final ClusterSingletonServiceRegistration clusterRegistration = mock(ClusterSingletonServiceRegistration.class); + netconfTopologyManager.init(); - final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts"); - fieldContexts.setAccessible(true); - @SuppressWarnings("unchecked") - final Map, NetconfTopologyContext> contexts = - (Map, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager); + await().atMost(5, TimeUnit.SECONDS).until(() -> { + ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction(); + Optional config = readTx.read(LogicalDatastoreType.CONFIGURATION, + NetconfTopologyUtils.createTopologyListPath(TOPOLOGY_ID)).get(3, TimeUnit.SECONDS); + Optional oper = readTx.read(LogicalDatastoreType.OPERATIONAL, + NetconfTopologyUtils.createTopologyListPath(TOPOLOGY_ID)).get(3, TimeUnit.SECONDS); + return config.isPresent() && oper.isPresent(); + }); - final Field fieldClusterRegistrations = NetconfTopologyManager.class.getDeclaredField("clusterRegistrations"); - fieldClusterRegistrations.setAccessible(true); - @SuppressWarnings("unchecked") - final Map, ClusterSingletonServiceRegistration> clusterRegistrations = - (Map, ClusterSingletonServiceRegistration>) - fieldClusterRegistrations.get(netconfTopologyManager); + // verify registration is called with right parameters - final Collection> changes = new ArrayList<>(); + verify(dataBroker).registerDataTreeChangeListener( + new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, NetconfTopologyUtils + .createTopologyListPath(TOPOLOGY_ID).child(Node.class)), netconfTopologyManager); - final InstanceIdentifier instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath( - new NodeKey(new NodeId("node-id-1")),"topology-1"); + netconfTopologyManager.close(); + verify(mockListenerReg).close(); - final InstanceIdentifier instanceIdentifierDiferent = NetconfTopologyUtils.createTopologyNodeListPath( - new NodeKey(new NodeId("node-id-2")),"topology-2"); + netconfTopologyManager.close(); + verifyNoMoreInteractions(mockListenerReg); + } + + @SuppressWarnings("unchecked") + @Test + public void testOnDataTreeChanged() throws Exception { - final DataTreeIdentifier rootIdentifier = - new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifier); + // Notify of 2 created Node objects. - final DataTreeIdentifier rootIdentifierDifferent = - new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifierDiferent); + final NodeId nodeId1 = new NodeId("node-id-1"); + final InstanceIdentifier nodeInstanceId1 = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(nodeId1), TOPOLOGY_ID); - @SuppressWarnings("unchecked") - final DataObjectModification objectModification = mock(DataObjectModification.class); + final NodeId nodeId2 = new NodeId("node-id-2"); + final InstanceIdentifier nodeInstanceId2 = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(nodeId2), TOPOLOGY_ID); - final NetconfNode netconfNode = new NetconfNodeBuilder() + final NetconfNode netconfNode1 = new NetconfNodeBuilder() .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) - .setPort(new PortNumber(9999)) - .setReconnectOnChangedSchema(true) - .setDefaultRequestTimeoutMillis(1000L) - .setBetweenAttemptsTimeoutMillis(100) - .setSchemaless(false) - .setTcpOnly(false) - .setActorResponseWaitTime(10) + .setPort(new PortNumber(1111)) + .setActorResponseWaitTime(ACTOR_RESPONSE_WAIT_TIME) .build(); - final Node node = new NodeBuilder().setNodeId(new NodeId("node-id")) - .addAugmentation(NetconfNode.class, netconfNode).build(); + final Node node1 = new NodeBuilder().setNodeId(nodeId1).addAugmentation(NetconfNode.class, + netconfNode1).build(); - final Identifier key = new NodeKey(new NodeId("node-id")); + final DataObjectModification dataObjectModification1 = mock(DataObjectModification.class); + doReturn(WRITE).when(dataObjectModification1).getModificationType(); + doReturn(node1).when(dataObjectModification1).getDataAfter(); + doReturn(InstanceIdentifier.IdentifiableItem.of(Node.class, new NodeKey(nodeId1))) + .when(dataObjectModification1).getIdentifier(); - @SuppressWarnings("unchecked") - final InstanceIdentifier.IdentifiableItem pathArgument = - new InstanceIdentifier.IdentifiableItem(Node.class, key); + final NetconfNode netconfNode2 = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(2222)) + .setActorResponseWaitTime(ACTOR_RESPONSE_WAIT_TIME) + .build(); + final Node node2 = new NodeBuilder().setNodeId(nodeId2).addAugmentation(NetconfNode.class, + netconfNode2).build(); + final DataObjectModification dataObjectModification2 = mock(DataObjectModification.class); + doReturn(WRITE).when(dataObjectModification2).getModificationType(); + doReturn(node2).when(dataObjectModification2).getDataAfter(); + doReturn(InstanceIdentifier.IdentifiableItem.of(Node.class, new NodeKey(nodeId2))) + .when(dataObjectModification2).getIdentifier(); - // testing WRITE on two identical rootIdentifiers and one different + final NetconfTopologyContext mockContext1 = mock(NetconfTopologyContext.class); + mockContextMap.put(nodeInstanceId1, setup -> { + assertEquals(node1, setup.getNode()); + assertEquals(TOPOLOGY_ID, setup.getTopologyId()); + return mockContext1; + }); - changes.add(new CustomTreeModification(rootIdentifier, objectModification)); - changes.add(new CustomTreeModification(rootIdentifier, objectModification)); - changes.add(new CustomTreeModification(rootIdentifierDifferent, objectModification)); + final NetconfTopologyContext mockContext2 = mock(NetconfTopologyContext.class); + mockContextMap.put(nodeInstanceId2, setup -> { + assertEquals(node2, setup.getNode()); + assertEquals(TOPOLOGY_ID, setup.getTopologyId()); + return mockContext2; + }); - doReturn(WRITE).when(objectModification).getModificationType(); - doReturn(node).when(objectModification).getDataAfter(); - doReturn(pathArgument).when(objectModification).getIdentifier(); - doReturn(clusterRegistration).when(clusterSingletonServiceProvider).registerClusterSingletonService(any()); + ClusterSingletonServiceRegistration mockClusterRegistration1 = mock(ClusterSingletonServiceRegistration.class); + ClusterSingletonServiceRegistration mockClusterRegistration2 = mock(ClusterSingletonServiceRegistration.class); - netconfTopologyManager.onDataTreeChanged(changes); + doReturn(mockClusterRegistration1).when(clusterSingletonServiceProvider) + .registerClusterSingletonService(mockContext1); + doReturn(mockClusterRegistration2).when(clusterSingletonServiceProvider) + .registerClusterSingletonService(mockContext2); - verify(clusterSingletonServiceProvider, times(2)).registerClusterSingletonService(any()); + netconfTopologyManager.onDataTreeChanged(Arrays.asList( + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId1), dataObjectModification1), + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId2), dataObjectModification2))); - // only two created contexts - assertEquals(2, contexts.size()); - assertTrue(contexts.containsKey(rootIdentifier.getRootIdentifier())); - assertTrue(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier())); + verify(clusterSingletonServiceProvider).registerClusterSingletonService(mockContext1); + verify(clusterSingletonServiceProvider).registerClusterSingletonService(mockContext2); - // only two created cluster registrations - assertEquals(2, contexts.size()); - assertTrue(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier())); - assertTrue(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier())); + // Notify of Node 1 replaced and Node 2 subtree modified. - // after delete there should be no context and clustered registrations - doReturn(DELETE).when(objectModification).getModificationType(); + mockContextMap.clear(); - doNothing().when(clusterRegistration).close(); + final NetconfNode updatedNetconfNode1 = new NetconfNodeBuilder(netconfNode1) + .setPort(new PortNumber(33333)).build(); + final Node updatedNode1 = new NodeBuilder().setNodeId(nodeId1).addAugmentation(NetconfNode.class, + updatedNetconfNode1).build(); - netconfTopologyManager.onDataTreeChanged(changes); + doReturn(WRITE).when(dataObjectModification1).getModificationType(); + doReturn(node1).when(dataObjectModification1).getDataBefore(); + doReturn(updatedNode1).when(dataObjectModification1).getDataAfter(); - verify(clusterRegistration, times(2)).close(); + doReturn(SUBTREE_MODIFIED).when(dataObjectModification2).getModificationType(); + doReturn(node2).when(dataObjectModification2).getDataBefore(); + doReturn(node2).when(dataObjectModification2).getDataAfter(); - // empty map of contexts - assertTrue(contexts.isEmpty()); - assertFalse(contexts.containsKey(rootIdentifier.getRootIdentifier())); - assertFalse(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier())); + doNothing().when(mockContext1).refresh(any()); + doNothing().when(mockContext2).refresh(any()); - // empty map of clustered registrations - assertTrue(clusterRegistrations.isEmpty()); - assertFalse(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier())); - assertFalse(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier())); + netconfTopologyManager.onDataTreeChanged(Arrays.asList( + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId1), dataObjectModification1), + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId2), dataObjectModification2))); - } + ArgumentCaptor mockContext1Setup = ArgumentCaptor.forClass(NetconfTopologySetup.class); + verify(mockContext1).refresh(mockContext1Setup.capture()); + assertEquals(updatedNode1, mockContext1Setup.getValue().getNode()); - @Test - public void testRegisterDataTreeChangeListener() { + verify(mockContext2).refresh(any()); - final WriteTransaction wtx = mock(WriteTransaction.class); + verifyNoMoreInteractions(clusterSingletonServiceProvider); - doReturn(wtx).when(dataBroker).newWriteOnlyTransaction(); - doNothing().when(wtx).merge(any(), any(), any()); - doReturn(Futures.immediateCheckedFuture(null)).when(wtx).submit(); - doReturn(null).when(dataBroker).registerDataChangeListener(any(), any(), any(), any()); + // Notify of Node 1 deleted. - netconfTopologyManager.init(); + doReturn(DELETE).when(dataObjectModification1).getModificationType(); + doReturn(updatedNode1).when(dataObjectModification1).getDataBefore(); + doReturn(null).when(dataObjectModification1).getDataAfter(); - // verify if listener is called with right parameters = registered on right path + netconfTopologyManager.onDataTreeChanged(Arrays.asList( + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId1), dataObjectModification1))); - verify(dataBroker, times(1)).registerDataTreeChangeListener( - new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, NetconfTopologyUtils - .createTopologyListPath(topologyId).child(Node.class)), netconfTopologyManager); + verify(mockClusterRegistration1).close(); + verify(mockContext1).close(); + verifyNoMoreInteractions(clusterSingletonServiceProvider, mockClusterRegistration2, mockContext2); - } + // Notify of Node 1 created again. - @Test - public void testClose() throws Exception { + reset(clusterSingletonServiceProvider); + + final NetconfTopologyContext newMockContext1 = mock(NetconfTopologyContext.class); + final ClusterSingletonServiceRegistration newMockClusterRegistration1 = + mock(ClusterSingletonServiceRegistration.class); - final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts"); - fieldContexts.setAccessible(true); - @SuppressWarnings("unchecked") - final Map, NetconfTopologyContext> contexts = - (Map, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager); + doThrow(new RuntimeException("mock error")).doReturn(newMockClusterRegistration1) + .when(clusterSingletonServiceProvider).registerClusterSingletonService(newMockContext1); - final Field fieldClusterRegistrations = NetconfTopologyManager.class.getDeclaredField("clusterRegistrations"); - fieldClusterRegistrations.setAccessible(true); - @SuppressWarnings("unchecked") - final Map, ClusterSingletonServiceRegistration> clusterRegistrations = - (Map, ClusterSingletonServiceRegistration>) - fieldClusterRegistrations.get(netconfTopologyManager); + doReturn(WRITE).when(dataObjectModification1).getModificationType(); + doReturn(null).when(dataObjectModification1).getDataBefore(); + doReturn(node1).when(dataObjectModification1).getDataAfter(); - final InstanceIdentifier instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath( - new NodeKey(new NodeId("node-id-1")),"topology-1"); + mockContextMap.put(nodeInstanceId1, setup -> { + assertEquals(node1, setup.getNode()); + assertEquals(TOPOLOGY_ID, setup.getTopologyId()); + return newMockContext1; + }); + netconfTopologyManager.onDataTreeChanged(Arrays.asList( + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId1), dataObjectModification1))); - final NetconfTopologyContext context = mock(NetconfTopologyContext.class); - final ClusterSingletonServiceRegistration clusterRegistration = - mock(ClusterSingletonServiceRegistration.class); - contexts.put(instanceIdentifier, context); - clusterRegistrations.put(instanceIdentifier, clusterRegistration); + verify(clusterSingletonServiceProvider, times(2)).registerClusterSingletonService(newMockContext1); + + verifyNoMoreInteractions(mockClusterRegistration1, mockContext1, mockClusterRegistration2, mockContext2, + newMockContext1, newMockClusterRegistration1, clusterSingletonServiceProvider); - doNothing().when(context).closeFinal(); - doNothing().when(clusterRegistration).close(); + // Test close. netconfTopologyManager.close(); - verify(context, times(1)).closeFinal(); - verify(clusterRegistration, times(1)).close(); - assertTrue(contexts.isEmpty()); - assertTrue(clusterRegistrations.isEmpty()); + verify(newMockClusterRegistration1).close(); + verify(newMockContext1).close(); + verify(mockClusterRegistration2).close(); + verify(mockContext2).close(); + + netconfTopologyManager.close(); + + verifyNoMoreInteractions(mockClusterRegistration1, mockContext1, mockClusterRegistration2, mockContext2, + newMockContext1, newMockClusterRegistration1, clusterSingletonServiceProvider); + } + + @Test + public void testClusterSingletonServiceRegistrationFailure() throws Exception { + final NodeId nodeId = new NodeId("node-id"); + final InstanceIdentifier nodeInstanceId = NetconfTopologyUtils.createTopologyNodeListPath( + new NodeKey(nodeId), TOPOLOGY_ID); + final NetconfNode netconfNode = new NetconfNodeBuilder() + .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1")))) + .setPort(new PortNumber(10)) + .setActorResponseWaitTime(ACTOR_RESPONSE_WAIT_TIME).build(); + final Node node = new NodeBuilder().setNodeId(nodeId).addAugmentation(NetconfNode.class, + netconfNode).build(); + + final DataObjectModification dataObjectModification = mock(DataObjectModification.class); + doReturn(WRITE).when(dataObjectModification).getModificationType(); + doReturn(node).when(dataObjectModification).getDataAfter(); + doReturn(InstanceIdentifier.IdentifiableItem.of(Node.class, new NodeKey(nodeId))) + .when(dataObjectModification).getIdentifier(); + + final NetconfTopologyContext mockContext = mock(NetconfTopologyContext.class); + mockContextMap.put(nodeInstanceId, setup -> mockContext); + + doThrow(new RuntimeException("mock error")).when(clusterSingletonServiceProvider) + .registerClusterSingletonService(mockContext); + + netconfTopologyManager.init(); + + netconfTopologyManager.onDataTreeChanged(Arrays.asList( + new CustomTreeModification(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, + nodeInstanceId), dataObjectModification))); + + verify(clusterSingletonServiceProvider, times(3)).registerClusterSingletonService(mockContext); + verify(mockContext).close(); + verifyNoMoreInteractions(mockListenerReg); + + netconfTopologyManager.close(); + verifyNoMoreInteractions(mockContext); } - private class CustomTreeModification implements DataTreeModification { + static class CustomTreeModification implements DataTreeModification { private final DataTreeIdentifier rootPath; private final DataObjectModification rootNode;