Update MRI projects for Aluminium
[netconf.git] / netconf / netconf-topology-singleton / src / test / java / org / opendaylight / netconf / topology / singleton / impl / NetconfNodeManagerTest.java
1 /*
2  * Copyright (c) 2018 Inocybe Technologies and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.topology.singleton.impl;
9
10 import static java.nio.charset.StandardCharsets.UTF_8;
11 import static org.mockito.ArgumentMatchers.any;
12 import static org.mockito.ArgumentMatchers.eq;
13 import static org.mockito.Mockito.after;
14 import static org.mockito.Mockito.doNothing;
15 import static org.mockito.Mockito.doReturn;
16 import static org.mockito.Mockito.mock;
17 import static org.mockito.Mockito.reset;
18 import static org.mockito.Mockito.timeout;
19 import static org.mockito.Mockito.verify;
20 import static org.mockito.Mockito.verifyNoMoreInteractions;
21 import static org.mockito.MockitoAnnotations.initMocks;
22 import static org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType.DELETE;
23 import static org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType.SUBTREE_MODIFIED;
24 import static org.opendaylight.mdsal.binding.api.DataObjectModification.ModificationType.WRITE;
25
26 import akka.actor.ActorSystem;
27 import akka.actor.Props;
28 import akka.cluster.Cluster;
29 import akka.dispatch.Dispatchers;
30 import akka.testkit.TestActorRef;
31 import akka.testkit.javadsl.TestKit;
32 import akka.util.Timeout;
33 import com.google.common.collect.ImmutableList;
34 import com.google.common.collect.Iterables;
35 import com.google.common.io.ByteSource;
36 import com.google.common.util.concurrent.Futures;
37 import com.typesafe.config.ConfigFactory;
38 import java.net.InetSocketAddress;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Map;
42 import java.util.concurrent.CompletableFuture;
43 import java.util.concurrent.ConcurrentHashMap;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.TimeUnit;
46 import java.util.concurrent.TimeoutException;
47 import java.util.stream.Collectors;
48 import org.junit.After;
49 import org.junit.Before;
50 import org.junit.Test;
51 import org.mockito.Mock;
52 import org.opendaylight.mdsal.binding.api.DataBroker;
53 import org.opendaylight.mdsal.binding.api.DataObjectModification;
54 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
55 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
56 import org.opendaylight.mdsal.dom.api.DOMActionService;
57 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
58 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
59 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
60 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
61 import org.opendaylight.mdsal.dom.api.DOMRpcService;
62 import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver;
63 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice;
64 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
65 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
66 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
67 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils;
68 import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint;
69 import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData;
70 import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized;
71 import org.opendaylight.netconf.topology.singleton.messages.YangTextSchemaSourceRequest;
72 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
73 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
74 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
75 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
76 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNode;
77 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeBuilder;
78 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.NetconfNodeConnectionStatus;
79 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.ClusteredConnectionStatusBuilder;
80 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId;
81 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
82 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeBuilder;
83 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.NodeKey;
84 import org.opendaylight.yangtools.concepts.ListenerRegistration;
85 import org.opendaylight.yangtools.concepts.ObjectRegistration;
86 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
87 import org.opendaylight.yangtools.yang.common.Uint16;
88 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
89 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
90 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
91 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
92 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
93 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
94 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
95
96 /**
97  * Unit tests for NetconfNodeManager.
98  *
99  * @author Thomas Pantelis
100  */
101 public class NetconfNodeManagerTest {
102     private static final String ACTOR_SYSTEM_NAME = "test";
103     private static final RemoteDeviceId DEVICE_ID = new RemoteDeviceId("device", new InetSocketAddress(65535));
104     private static final List<SourceIdentifier> SOURCE_IDENTIFIERS =
105             ImmutableList.of(RevisionSourceIdentifier.create("testID"));
106
107     @Mock
108     private DOMMountPointService mockMountPointService;
109
110     @Mock
111     private DOMMountPointService.DOMMountPointBuilder mockMountPointBuilder;
112
113     @Mock
114     private ObjectRegistration<DOMMountPoint> mockMountPointReg;
115
116     @Mock
117     private DataBroker mockDataBroker;
118
119     @Mock
120     private DOMDataBroker mockDeviceDataBroker;
121
122     @Mock
123     private DOMRpcService mockRpcService;
124
125     @Mock
126     private DOMActionService mockActionService;
127
128     @Mock
129     private NetconfDeviceSchemasResolver mockSchemasResolver;
130
131     @Mock
132     private EffectiveModelContextFactory mockSchemaContextFactory;
133
134     private ActorSystem slaveSystem;
135     private ActorSystem masterSystem;
136     private TestActorRef<TestMasterActor> testMasterActorRef;
137     private NetconfNodeManager netconfNodeManager;
138     private String masterAddress;
139
140     @Before
141     public void setup() {
142         initMocks(this);
143
144         final Timeout responseTimeout = Timeout.apply(1, TimeUnit.SECONDS);
145
146         slaveSystem = ActorSystem.create(ACTOR_SYSTEM_NAME, ConfigFactory.load().getConfig("Slave"));
147         masterSystem = ActorSystem.create(ACTOR_SYSTEM_NAME, ConfigFactory.load().getConfig("Master"));
148
149         masterAddress = Cluster.get(masterSystem).selfAddress().toString();
150
151         SharedSchemaRepository masterSchemaRepository = new SharedSchemaRepository("master");
152         masterSchemaRepository.registerSchemaSourceListener(
153                 TextToASTTransformer.create(masterSchemaRepository, masterSchemaRepository));
154
155         String yangTemplate =
156                   "module ID {"
157                 + "  namespace \"ID\";"
158                 + "  prefix ID;"
159                 + "}";
160
161         SOURCE_IDENTIFIERS.stream().map(
162             sourceId -> masterSchemaRepository.registerSchemaSource(
163                 id -> Futures.immediateFuture(YangTextSchemaSource.delegateForByteSource(id,
164                         ByteSource.wrap(yangTemplate.replaceAll("ID", id.getName()).getBytes(UTF_8)))),
165                 PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, 1)))
166         .collect(Collectors.toList());
167
168         NetconfTopologySetup masterSetup = new NetconfTopologySetup.NetconfTopologySetupBuilder()
169                 .setActorSystem(masterSystem).setDataBroker(mockDataBroker).setSchemaResourceDTO(
170                         new NetconfDevice.SchemaResourcesDTO(masterSchemaRepository, masterSchemaRepository,
171                                 mockSchemaContextFactory, mockSchemasResolver)).build();
172
173         testMasterActorRef = TestActorRef.create(masterSystem, Props.create(TestMasterActor.class, masterSetup,
174                 DEVICE_ID, responseTimeout, mockMountPointService).withDispatcher(Dispatchers.DefaultDispatcherId()),
175                 NetconfTopologyUtils.createMasterActorName(DEVICE_ID.getName(), masterAddress));
176
177         SharedSchemaRepository slaveSchemaRepository = new SharedSchemaRepository("slave");
178         slaveSchemaRepository.registerSchemaSourceListener(
179                 TextToASTTransformer.create(slaveSchemaRepository, slaveSchemaRepository));
180
181         NetconfTopologySetup slaveSetup = new NetconfTopologySetup.NetconfTopologySetupBuilder()
182                 .setActorSystem(slaveSystem).setDataBroker(mockDataBroker).setSchemaResourceDTO(
183                         new NetconfDevice.SchemaResourcesDTO(slaveSchemaRepository, slaveSchemaRepository,
184                                 mockSchemaContextFactory, mockSchemasResolver)).build();
185
186         netconfNodeManager = new NetconfNodeManager(slaveSetup, DEVICE_ID, responseTimeout,
187                 mockMountPointService);
188
189         setupMountPointMocks();
190     }
191
192     @After
193     public void teardown() {
194         TestKit.shutdownActorSystem(slaveSystem, true);
195         TestKit.shutdownActorSystem(masterSystem, true);
196     }
197
198     @SuppressWarnings("unchecked")
199     @Test
200     public void testSlaveMountPointRegistration() throws InterruptedException, ExecutionException, TimeoutException {
201         initializeMaster();
202
203         ListenerRegistration<?> mockListenerReg = mock(ListenerRegistration.class);
204         doReturn(mockListenerReg).when(mockDataBroker).registerDataTreeChangeListener(any(), any());
205
206         final NodeId nodeId = new NodeId("device");
207         final NodeKey nodeKey = new NodeKey(nodeId);
208         final String topologyId = "topology-netconf";
209         final InstanceIdentifier<Node> nodeListPath = NetconfTopologyUtils.createTopologyNodeListPath(
210                 nodeKey, topologyId);
211
212         netconfNodeManager.registerDataTreeChangeListener(topologyId, nodeKey);
213         verify(mockDataBroker).registerDataTreeChangeListener(any(), eq(netconfNodeManager));
214
215         // Invoke onDataTreeChanged with a NetconfNode WRITE to simulate the master writing the operational state to
216         // Connected. Expect the slave mount point created and registered.
217
218         final NetconfNode netconfNode = newNetconfNode();
219         final Node node = new NodeBuilder().setNodeId(nodeId).addAugmentation(netconfNode).build();
220
221         DataObjectModification<Node> mockDataObjModification = mock(DataObjectModification.class);
222         doReturn(Iterables.getLast(nodeListPath.getPathArguments())).when(mockDataObjModification).getIdentifier();
223         doReturn(WRITE).when(mockDataObjModification).getModificationType();
224         doReturn(node).when(mockDataObjModification).getDataAfter();
225
226         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
227                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
228                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
229
230         verify(mockMountPointBuilder, timeout(5000)).register();
231         verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), any());
232         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), any());
233         verify(mockMountPointBuilder).addService(eq(DOMNotificationService.class), any());
234         verify(mockMountPointService).createMountPoint(DEVICE_ID.getTopologyPath());
235
236         // Notify that the NetconfNode operational state was deleted. Expect the slave mount point closed.
237
238         doReturn(DELETE).when(mockDataObjModification).getModificationType();
239         doReturn(node).when(mockDataObjModification).getDataBefore();
240         doReturn(null).when(mockDataObjModification).getDataAfter();
241
242         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
243                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
244                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
245
246         verify(mockMountPointReg, timeout(5000)).close();
247
248         // Notify with a NetconfNode operational state WRITE. Expect the slave mount point re-created.
249
250         setupMountPointMocks();
251
252         doReturn(WRITE).when(mockDataObjModification).getModificationType();
253         doReturn(null).when(mockDataObjModification).getDataBefore();
254         doReturn(node).when(mockDataObjModification).getDataAfter();
255
256         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
257                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
258                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
259
260         verify(mockMountPointBuilder, timeout(5000)).register();
261
262         // Notify again with a NetconfNode operational state WRITE. Expect the prior slave mount point closed and
263         // and a new one registered.
264
265         setupMountPointMocks();
266
267         doReturn(node).when(mockDataObjModification).getDataBefore();
268
269         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
270                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
271                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
272
273         verify(mockMountPointReg, timeout(5000)).close();
274         verify(mockMountPointBuilder, timeout(5000)).register();
275
276         // Notify that the NetconfNode operational state was changed to UnableToConnect. Expect the slave mount point
277         // closed.
278
279         setupMountPointMocks();
280
281         final Node updatedNode = new NodeBuilder().setNodeId(nodeId)
282                 .addAugmentation(new NetconfNodeBuilder(netconfNode)
283                     .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.UnableToConnect)
284                     .build())
285                 .build();
286
287         doReturn(SUBTREE_MODIFIED).when(mockDataObjModification).getModificationType();
288         doReturn(node).when(mockDataObjModification).getDataBefore();
289         doReturn(updatedNode).when(mockDataObjModification).getDataAfter();
290
291         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
292                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
293                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
294
295         verify(mockMountPointReg, timeout(5000)).close();
296
297         netconfNodeManager.close();
298         verifyNoMoreInteractions(mockMountPointReg);
299     }
300
301     @SuppressWarnings("unchecked")
302     @Test
303     public void testSlaveMountPointRegistrationFailuresAndRetries()
304             throws InterruptedException, ExecutionException, TimeoutException {
305         final NodeId nodeId = new NodeId("device");
306         final NodeKey nodeKey = new NodeKey(nodeId);
307         final String topologyId = "topology-netconf";
308         final InstanceIdentifier<Node> nodeListPath = NetconfTopologyUtils.createTopologyNodeListPath(
309                 nodeKey, topologyId);
310
311         final NetconfNode netconfNode = newNetconfNode();
312         final Node node = new NodeBuilder().setNodeId(nodeId).addAugmentation(netconfNode).build();
313
314         DataObjectModification<Node> mockDataObjModification = mock(DataObjectModification.class);
315         doReturn(Iterables.getLast(nodeListPath.getPathArguments())).when(mockDataObjModification).getIdentifier();
316         doReturn(WRITE).when(mockDataObjModification).getModificationType();
317         doReturn(node).when(mockDataObjModification).getDataAfter();
318
319         // First try the registration where the perceived master hasn't been initialized as the master.
320
321         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
322                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
323                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
324
325         verify(mockMountPointBuilder, after(1000).never()).register();
326
327         // Initialize the master but drop the initial YangTextSchemaSourceRequest message sent to the master so
328         // it retries.
329
330         initializeMaster();
331
332         CompletableFuture<AskForMasterMountPoint> yangTextSchemaSourceRequestFuture = new CompletableFuture<>();
333         testMasterActorRef.underlyingActor().messagesToDrop.put(YangTextSchemaSourceRequest.class,
334                 yangTextSchemaSourceRequestFuture);
335
336         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
337                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
338                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
339
340         yangTextSchemaSourceRequestFuture.get(5, TimeUnit.SECONDS);
341         verify(mockMountPointBuilder, timeout(5000)).register();
342
343         // Initiate another registration but drop the initial AskForMasterMountPoint message sent to the master so
344         // it retries.
345
346         setupMountPointMocks();
347
348         CompletableFuture<AskForMasterMountPoint> askForMasterMountPointFuture = new CompletableFuture<>();
349         testMasterActorRef.underlyingActor().messagesToDrop.put(AskForMasterMountPoint.class,
350                 askForMasterMountPointFuture);
351
352         netconfNodeManager.onDataTreeChanged(Collections.singletonList(
353                 new NetconfTopologyManagerTest.CustomTreeModification(DataTreeIdentifier.create(
354                         LogicalDatastoreType.OPERATIONAL, nodeListPath), mockDataObjModification)));
355
356         askForMasterMountPointFuture.get(5, TimeUnit.SECONDS);
357         verify(mockMountPointReg, timeout(5000)).close();
358         verify(mockMountPointBuilder, timeout(5000)).register();
359
360         setupMountPointMocks();
361         netconfNodeManager.close();
362         verify(mockMountPointReg, timeout(5000)).close();
363     }
364
365     private NetconfNode newNetconfNode() {
366         return new NetconfNodeBuilder()
367                 .setHost(new Host(new IpAddress(new Ipv4Address("127.0.0.1"))))
368                 .setPort(new PortNumber(Uint16.valueOf(9999)))
369                 .setConnectionStatus(NetconfNodeConnectionStatus.ConnectionStatus.Connected)
370                 .setClusteredConnectionStatus(new ClusteredConnectionStatusBuilder()
371                         .setNetconfMasterNode(masterAddress).build())
372                 .build();
373     }
374
375     private void setupMountPointMocks() {
376         reset(mockMountPointService, mockMountPointBuilder, mockMountPointReg);
377
378         doNothing().when(mockMountPointReg).close();
379
380         doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addInitialSchemaContext(any());
381         doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addService(any(), any());
382         doReturn(mockMountPointReg).when(mockMountPointBuilder).register();
383
384         doReturn(mockMountPointBuilder).when(mockMountPointService).createMountPoint(any());
385     }
386
387     private void initializeMaster() {
388         TestKit kit = new TestKit(masterSystem);
389
390         testMasterActorRef.tell(new CreateInitialMasterActorData(mockDeviceDataBroker, SOURCE_IDENTIFIERS,
391                 mockRpcService, mockActionService), kit.getRef());
392
393         kit.expectMsgClass(MasterActorDataInitialized.class);
394     }
395
396     private static class TestMasterActor extends NetconfNodeActor {
397         final Map<Class<?>, CompletableFuture<? extends Object>> messagesToDrop = new ConcurrentHashMap<>();
398
399         TestMasterActor(final NetconfTopologySetup setup, final RemoteDeviceId deviceId,
400                 final Timeout actorResponseWaitTime, final DOMMountPointService mountPointService) {
401             super(setup, deviceId, actorResponseWaitTime, mountPointService);
402         }
403
404         @SuppressWarnings({ "rawtypes", "unchecked" })
405         @Override
406         public void handleReceive(final Object message) {
407             CompletableFuture dropFuture = messagesToDrop.remove(message.getClass());
408             if (dropFuture != null) {
409                 dropFuture.complete(message);
410             } else {
411                 super.handleReceive(message);
412             }
413         }
414     }
415 }