2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.netconf.topology.singleton.impl;
11 import static junit.framework.TestCase.assertFalse;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.Matchers.any;
15 import static org.mockito.Mockito.doNothing;
16 import static org.mockito.Mockito.doReturn;
17 import static org.mockito.Mockito.doThrow;
18 import static org.mockito.Mockito.mock;
19 import static org.mockito.Mockito.times;
20 import static org.mockito.Mockito.verify;
21 import static org.mockito.MockitoAnnotations.initMocks;
22 import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.DELETE;
23 import static org.opendaylight.controller.md.sal.binding.api.DataObjectModification.ModificationType.WRITE;
25 import com.google.common.util.concurrent.Futures;
26 import io.netty.util.concurrent.EventExecutor;
27 import java.lang.reflect.Field;
28 import java.util.ArrayList;
29 import java.util.Collection;
31 import javax.annotation.Nonnull;
32 import org.junit.Before;
33 import org.junit.Test;
34 import org.mockito.Mock;
35 import org.opendaylight.aaa.encrypt.AAAEncryptionService;
36 import org.opendaylight.controller.cluster.ActorSystemProvider;
37 import org.opendaylight.controller.config.threadpool.ScheduledThreadPool;
38 import org.opendaylight.controller.config.threadpool.ThreadPool;
39 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
40 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
41 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
42 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
43 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
44 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
45 import org.opendaylight.controller.md.sal.dom.api.DOMMountPointService;
46 import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
47 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
48 import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
49 import org.opendaylight.netconf.client.NetconfClientDispatcher;
50 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
53 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
54 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.Config;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.topology.singleton.config.rev170419.ConfigBuilder;
59 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
60 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
61 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
62 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
63 import org.opendaylight.yangtools.yang.binding.Identifier;
64 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
66 public class NetconfTopologyManagerTest {
68 private final String topologyId = "topologyID";
69 private NetconfTopologyManager netconfTopologyManager;
72 private DataBroker dataBroker;
75 private ClusterSingletonServiceProvider clusterSingletonServiceProvider;
81 final RpcProviderRegistry rpcProviderRegistry = mock(RpcProviderRegistry.class);
82 final ScheduledThreadPool keepaliveExecutor = mock(ScheduledThreadPool.class);
83 final ThreadPool processingExecutor = mock(ThreadPool.class);
84 final ActorSystemProvider actorSystemProvider = mock(ActorSystemProvider.class);
85 final EventExecutor eventExecutor = mock(EventExecutor.class);
86 final NetconfClientDispatcher clientDispatcher = mock(NetconfClientDispatcher.class);
87 final DOMMountPointService mountPointService = mock(DOMMountPointService.class);
88 final AAAEncryptionService encryptionService = mock(AAAEncryptionService.class);
90 final Config config = new ConfigBuilder().setWriteTransactionIdleTimeout(0).build();
91 netconfTopologyManager = new NetconfTopologyManager(dataBroker, rpcProviderRegistry,
92 clusterSingletonServiceProvider, keepaliveExecutor, processingExecutor,
93 actorSystemProvider, eventExecutor, clientDispatcher, topologyId, config,
94 mountPointService, encryptionService);
98 public void testWriteConfiguration() throws Exception {
99 writeConfiguration(false);
103 public void testWriteConfigurationFail() throws Exception {
104 writeConfiguration(true);
108 public void testRegisterDataTreeChangeListener() {
110 final WriteTransaction wtx = mock(WriteTransaction.class);
112 doReturn(wtx).when(dataBroker).newWriteOnlyTransaction();
113 doNothing().when(wtx).merge(any(), any(), any());
114 doReturn(Futures.immediateCheckedFuture(null)).when(wtx).submit();
116 netconfTopologyManager.init();
118 // verify if listener is called with right parameters = registered on right path
120 verify(dataBroker, times(1)).registerDataTreeChangeListener(
121 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, NetconfTopologyUtils
122 .createTopologyListPath(topologyId).child(Node.class)), netconfTopologyManager);
127 public void testClose() throws Exception {
129 final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts");
130 fieldContexts.setAccessible(true);
131 @SuppressWarnings("unchecked") final Map<InstanceIdentifier<Node>, NetconfTopologyContext> contexts =
132 (Map<InstanceIdentifier<Node>, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager);
134 final Field fieldClusterRegistrations =
135 NetconfTopologyManager.class.getDeclaredField("clusterRegistrations");
136 fieldClusterRegistrations.setAccessible(true);
137 @SuppressWarnings("unchecked")
138 final Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration> clusterRegistrations =
139 (Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration>)
140 fieldClusterRegistrations.get(netconfTopologyManager);
142 final InstanceIdentifier<Node> instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath(
143 new NodeKey(new NodeId("node-id-1")), "topology-1");
146 final NetconfTopologyContext context = mock(NetconfTopologyContext.class);
147 final ClusterSingletonServiceRegistration clusterRegistration =
148 mock(ClusterSingletonServiceRegistration.class);
149 contexts.put(instanceIdentifier, context);
150 clusterRegistrations.put(instanceIdentifier, clusterRegistration);
152 doNothing().when(context).closeFinal();
153 doNothing().when(clusterRegistration).close();
155 netconfTopologyManager.close();
156 verify(context, times(1)).closeFinal();
157 verify(clusterRegistration, times(1)).close();
159 assertTrue(contexts.isEmpty());
160 assertTrue(clusterRegistrations.isEmpty());
164 private void writeConfiguration(final boolean fail) throws Exception {
166 final ClusterSingletonServiceRegistration clusterRegistration = mock(ClusterSingletonServiceRegistration.class);
168 final Field fieldContexts = NetconfTopologyManager.class.getDeclaredField("contexts");
169 fieldContexts.setAccessible(true);
170 @SuppressWarnings("unchecked") final Map<InstanceIdentifier<Node>, NetconfTopologyContext> contexts =
171 (Map<InstanceIdentifier<Node>, NetconfTopologyContext>) fieldContexts.get(netconfTopologyManager);
173 final Field fieldClusterRegistrations =
174 NetconfTopologyManager.class.getDeclaredField("clusterRegistrations");
175 fieldClusterRegistrations.setAccessible(true);
176 @SuppressWarnings("unchecked")
177 final Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration> clusterRegistrations =
178 (Map<InstanceIdentifier<Node>, ClusterSingletonServiceRegistration>)
179 fieldClusterRegistrations.get(netconfTopologyManager);
181 final Collection<DataTreeModification<Node>> changes = new ArrayList<>();
183 final InstanceIdentifier<Node> instanceIdentifier = NetconfTopologyUtils.createTopologyNodeListPath(
184 new NodeKey(new NodeId("node-id-1")), "topology-1");
186 final InstanceIdentifier<Node> instanceIdentifierDiferent = NetconfTopologyUtils.createTopologyNodeListPath(
187 new NodeKey(new NodeId("node-id-2")), "topology-2");
189 final DataTreeIdentifier<Node> rootIdentifier =
190 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifier);
192 final DataTreeIdentifier<Node> rootIdentifierDifferent =
193 new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, instanceIdentifierDiferent);
195 @SuppressWarnings("unchecked") final DataObjectModification<Node> objectModification =
196 mock(DataObjectModification.class);
198 final NetconfNode netconfNode = new NetconfNodeBuilder()
199 .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
200 .setPort(new PortNumber(9999))
201 .setReconnectOnChangedSchema(true)
202 .setDefaultRequestTimeoutMillis(1000L)
203 .setBetweenAttemptsTimeoutMillis(100)
204 .setSchemaless(false)
206 .setActorResponseWaitTime(10)
208 final Node node = new NodeBuilder().setNodeId(new NodeId("node-id"))
209 .addAugmentation(NetconfNode.class, netconfNode).build();
211 final Identifier key = new NodeKey(new NodeId("node-id"));
213 @SuppressWarnings("unchecked") final InstanceIdentifier.IdentifiableItem<Node, NodeKey> pathArgument =
214 new InstanceIdentifier.IdentifiableItem(Node.class, key);
217 // testing WRITE on two identical rootIdentifiers and one different
219 changes.add(new CustomTreeModification(rootIdentifier, objectModification));
221 changes.add(new CustomTreeModification(rootIdentifier, objectModification));
222 changes.add(new CustomTreeModification(rootIdentifier, objectModification));
223 changes.add(new CustomTreeModification(rootIdentifierDifferent, objectModification));
225 doReturn(WRITE).when(objectModification).getModificationType();
226 doReturn(node).when(objectModification).getDataAfter();
227 doReturn(pathArgument).when(objectModification).getIdentifier();
230 doThrow(new RuntimeException("error")).when(clusterSingletonServiceProvider)
231 .registerClusterSingletonService(any());
233 doReturn(clusterRegistration).when(clusterSingletonServiceProvider).registerClusterSingletonService(any());
235 netconfTopologyManager.onDataTreeChanged(changes);
238 verify(clusterSingletonServiceProvider, times(3))
239 .registerClusterSingletonService(any());
240 assertTrue(contexts.isEmpty());
241 assertTrue(clusterRegistrations.isEmpty());
243 verify(clusterSingletonServiceProvider, times(2))
244 .registerClusterSingletonService(any());
246 // only two created contexts
247 assertEquals(2, contexts.size());
248 assertTrue(contexts.containsKey(rootIdentifier.getRootIdentifier()));
249 assertTrue(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier()));
251 // only two created cluster registrations
252 assertEquals(2, clusterRegistrations.size());
253 assertTrue(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier()));
254 assertTrue(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier()));
256 // after delete there should be no context and clustered registrations
257 doReturn(DELETE).when(objectModification).getModificationType();
259 doNothing().when(clusterRegistration).close();
261 netconfTopologyManager.onDataTreeChanged(changes);
263 verify(clusterRegistration, times(2)).close();
265 // empty map of contexts
266 assertTrue(contexts.isEmpty());
267 assertFalse(contexts.containsKey(rootIdentifier.getRootIdentifier()));
268 assertFalse(contexts.containsKey(rootIdentifierDifferent.getRootIdentifier()));
270 // empty map of clustered registrations
271 assertTrue(clusterRegistrations.isEmpty());
272 assertFalse(clusterRegistrations.containsKey(rootIdentifier.getRootIdentifier()));
273 assertFalse(clusterRegistrations.containsKey(rootIdentifierDifferent.getRootIdentifier()));
277 private class CustomTreeModification implements DataTreeModification<Node> {
279 private final DataTreeIdentifier<Node> rootPath;
280 private final DataObjectModification<Node> rootNode;
282 CustomTreeModification(final DataTreeIdentifier<Node> rootPath, final DataObjectModification<Node> rootNode) {
283 this.rootPath = rootPath;
284 this.rootNode = rootNode;
289 public DataTreeIdentifier<Node> getRootPath() {
295 public DataObjectModification<Node> getRootNode() {