6e6d3d6f5ec11288fa33e97c2200089eceb5cdee
[netconf.git] / netconf / netconf-topology-singleton / src / test / java / org / opendaylight / netconf / topology / singleton / impl / NetconfNodeActorTest.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. 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.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNull;
13 import static org.junit.Assert.assertTrue;
14 import static org.mockito.ArgumentMatchers.any;
15 import static org.mockito.ArgumentMatchers.anyCollection;
16 import static org.mockito.ArgumentMatchers.argThat;
17 import static org.mockito.ArgumentMatchers.eq;
18 import static org.mockito.Mockito.after;
19 import static org.mockito.Mockito.doAnswer;
20 import static org.mockito.Mockito.doNothing;
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.reset;
24 import static org.mockito.Mockito.timeout;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.verifyNoMoreInteractions;
28 import static org.mockito.MockitoAnnotations.initMocks;
29 import static org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologyUtils.DEFAULT_SCHEMA_REPOSITORY;
30
31 import akka.actor.ActorRef;
32 import akka.actor.ActorSystem;
33 import akka.actor.Props;
34 import akka.actor.Status.Failure;
35 import akka.actor.Status.Success;
36 import akka.pattern.AskTimeoutException;
37 import akka.pattern.Patterns;
38 import akka.testkit.TestActorRef;
39 import akka.testkit.javadsl.TestKit;
40 import akka.util.Timeout;
41 import com.google.common.collect.ImmutableList;
42 import com.google.common.collect.Lists;
43 import com.google.common.io.ByteSource;
44 import com.google.common.net.InetAddresses;
45 import com.google.common.util.concurrent.Futures;
46 import com.google.common.util.concurrent.SettableFuture;
47 import java.io.InputStream;
48 import java.net.InetSocketAddress;
49 import java.util.ArrayList;
50 import java.util.Collections;
51 import java.util.List;
52 import java.util.Scanner;
53 import java.util.concurrent.ExecutionException;
54 import java.util.concurrent.TimeUnit;
55 import org.junit.After;
56 import org.junit.Before;
57 import org.junit.Rule;
58 import org.junit.Test;
59 import org.junit.rules.ExpectedException;
60 import org.mockito.ArgumentCaptor;
61 import org.mockito.Mock;
62 import org.opendaylight.controller.cluster.schema.provider.impl.YangTextSchemaSourceSerializationProxy;
63 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
64 import org.opendaylight.mdsal.dom.api.DOMActionException;
65 import org.opendaylight.mdsal.dom.api.DOMActionResult;
66 import org.opendaylight.mdsal.dom.api.DOMActionService;
67 import org.opendaylight.mdsal.dom.api.DOMDataBroker;
68 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
69 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
70 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
71 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
72 import org.opendaylight.mdsal.dom.api.DOMMountPoint;
73 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
74 import org.opendaylight.mdsal.dom.api.DOMNotificationService;
75 import org.opendaylight.mdsal.dom.api.DOMRpcException;
76 import org.opendaylight.mdsal.dom.api.DOMRpcResult;
77 import org.opendaylight.mdsal.dom.api.DOMRpcService;
78 import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
79 import org.opendaylight.mdsal.dom.spi.SimpleDOMActionResult;
80 import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO;
81 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
82 import org.opendaylight.netconf.topology.singleton.impl.actors.NetconfNodeActor;
83 import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringActionException;
84 import org.opendaylight.netconf.topology.singleton.impl.utils.ClusteringRpcException;
85 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup;
86 import org.opendaylight.netconf.topology.singleton.impl.utils.NetconfTopologySetup.NetconfTopologySetupBuilder;
87 import org.opendaylight.netconf.topology.singleton.messages.AskForMasterMountPoint;
88 import org.opendaylight.netconf.topology.singleton.messages.CreateInitialMasterActorData;
89 import org.opendaylight.netconf.topology.singleton.messages.MasterActorDataInitialized;
90 import org.opendaylight.netconf.topology.singleton.messages.NotMasterException;
91 import org.opendaylight.netconf.topology.singleton.messages.RefreshSetupMasterActorData;
92 import org.opendaylight.netconf.topology.singleton.messages.RegisterMountPoint;
93 import org.opendaylight.netconf.topology.singleton.messages.UnregisterSlaveMountPoint;
94 import org.opendaylight.yangtools.concepts.ObjectRegistration;
95 import org.opendaylight.yangtools.util.concurrent.FluentFutures;
96 import org.opendaylight.yangtools.yang.common.QName;
97 import org.opendaylight.yangtools.yang.common.RpcError;
98 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
99 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
100 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
101 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
102 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
103 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
104 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
105 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
106 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
107 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
108 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
109 import org.opendaylight.yangtools.yang.model.repo.api.RevisionSourceIdentifier;
110 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
111 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
112 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
113 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
114 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
115 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
116 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
117 import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
118 import org.opendaylight.yangtools.yang.parser.rfc7950.repo.TextToASTTransformer;
119 import scala.concurrent.Await;
120 import scala.concurrent.Future;
121 import scala.concurrent.duration.Duration;
122
123 public class NetconfNodeActorTest {
124
125     private static final Timeout TIMEOUT = new Timeout(Duration.create(5, "seconds"));
126     private static final RevisionSourceIdentifier SOURCE_IDENTIFIER1 = RevisionSourceIdentifier.create("yang1");
127     private static final RevisionSourceIdentifier SOURCE_IDENTIFIER2 = RevisionSourceIdentifier.create("yang2");
128
129     private ActorSystem system = ActorSystem.create();
130     private final TestKit testKit = new TestKit(system);
131
132     @Rule
133     public final ExpectedException exception = ExpectedException.none();
134
135     private ActorRef masterRef;
136     private RemoteDeviceId remoteDeviceId;
137     private final SharedSchemaRepository masterSchemaRepository = new SharedSchemaRepository("master");
138
139     @Mock
140     private DOMRpcService mockDOMRpcService;
141
142     @Mock
143     private DOMActionService mockDOMActionService;
144
145     @Mock
146     private DOMMountPointService mockMountPointService;
147
148     @Mock
149     private DOMMountPointService.DOMMountPointBuilder mockMountPointBuilder;
150
151     @Mock
152     private ObjectRegistration<DOMMountPoint> mockMountPointReg;
153
154     @Mock
155     private DOMDataBroker mockDOMDataBroker;
156
157     @Mock
158     private SchemaSourceRegistration<?> mockSchemaSourceReg1;
159
160     @Mock
161     private SchemaSourceRegistration<?> mockSchemaSourceReg2;
162
163     @Mock
164     private SchemaSourceRegistry mockRegistry;
165
166     @Mock
167     private EffectiveModelContextFactory mockSchemaContextFactory;
168
169     @Mock
170     private SchemaRepository mockSchemaRepository;
171
172     @Mock
173     private EffectiveModelContext mockSchemaContext;
174
175     @Mock
176     private SchemaResourcesDTO schemaResourceDTO;
177
178     @Before
179     public void setup() {
180         initMocks(this);
181
182         remoteDeviceId = new RemoteDeviceId("netconf-topology",
183                 new InetSocketAddress(InetAddresses.forString("127.0.0.1"), 9999));
184
185         masterSchemaRepository.registerSchemaSourceListener(
186                 TextToASTTransformer.create(masterSchemaRepository, masterSchemaRepository));
187
188         doReturn(masterSchemaRepository).when(schemaResourceDTO).getSchemaRepository();
189         doReturn(mockRegistry).when(schemaResourceDTO).getSchemaRegistry();
190         final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system)
191                 .setIdleTimeout(Duration.apply(1, TimeUnit.SECONDS)).setSchemaResourceDTO(schemaResourceDTO).build();
192
193         final Props props = NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT, mockMountPointService);
194
195         masterRef = TestActorRef.create(system, props, "master_messages");
196
197         resetMountPointMocks();
198
199         doReturn(mockMountPointBuilder).when(mockMountPointService).createMountPoint(any());
200
201         doReturn(mockSchemaSourceReg1).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
202         doReturn(mockSchemaSourceReg2).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER2));
203
204         doReturn(mockSchemaContextFactory).when(mockSchemaRepository)
205                 .createEffectiveModelContextFactory();
206     }
207
208     @After
209     public void teardown() {
210         TestKit.shutdownActorSystem(system, true);
211         system = null;
212     }
213
214     @Test
215     public void testInitializeAndRefreshMasterData() {
216
217         // Test CreateInitialMasterActorData.
218
219         initializeMaster(new ArrayList<>());
220
221         // Test RefreshSetupMasterActorData.
222
223         final RemoteDeviceId newRemoteDeviceId = new RemoteDeviceId("netconf-topology2",
224                 new InetSocketAddress(InetAddresses.forString("127.0.0.2"), 9999));
225
226         final NetconfTopologySetup newSetup = NetconfTopologySetupBuilder.create()
227                 .setSchemaResourceDTO(schemaResourceDTO).setActorSystem(system).build();
228
229         masterRef.tell(new RefreshSetupMasterActorData(newSetup, newRemoteDeviceId), testKit.getRef());
230
231         testKit.expectMsgClass(MasterActorDataInitialized.class);
232     }
233
234     @Test
235     public void tesAskForMasterMountPoint() {
236
237         // Test with master not setup yet.
238
239         final TestKit kit = new TestKit(system);
240
241         masterRef.tell(new AskForMasterMountPoint(kit.getRef()), kit.getRef());
242
243         final Failure failure = kit.expectMsgClass(Failure.class);
244         assertTrue(failure.cause() instanceof NotMasterException);
245
246         // Now initialize - master should send the RegisterMountPoint message.
247
248         List<SourceIdentifier> sourceIdentifiers = Lists.newArrayList(RevisionSourceIdentifier.create("testID"));
249         initializeMaster(sourceIdentifiers);
250
251         masterRef.tell(new AskForMasterMountPoint(kit.getRef()), kit.getRef());
252
253         final RegisterMountPoint registerMountPoint = kit.expectMsgClass(RegisterMountPoint.class);
254
255         assertEquals(sourceIdentifiers, registerMountPoint.getSourceIndentifiers());
256     }
257
258     @Test
259     public void testRegisterAndUnregisterMountPoint() throws Exception {
260
261         ActorRef slaveRef = registerSlaveMountPoint();
262
263         // Unregister
264
265         slaveRef.tell(new UnregisterSlaveMountPoint(), testKit.getRef());
266
267         verify(mockMountPointReg, timeout(5000)).close();
268         verify(mockSchemaSourceReg1, timeout(1000)).close();
269         verify(mockSchemaSourceReg2, timeout(1000)).close();
270
271         // Test registration with another interleaved registration that completes while the first registration
272         // is resolving the schema context.
273
274         reset(mockSchemaSourceReg1, mockRegistry, mockSchemaRepository);
275         resetMountPointMocks();
276
277         doReturn(mockSchemaSourceReg1).when(mockRegistry).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
278
279         doReturn(mockSchemaContextFactory).when(mockSchemaRepository)
280                 .createEffectiveModelContextFactory();
281
282         final SchemaSourceRegistration<?> newMockSchemaSourceReg = mock(SchemaSourceRegistration.class);
283
284         final EffectiveModelContextFactory newMockSchemaContextFactory = mock(EffectiveModelContextFactory.class);
285         doReturn(Futures.immediateFuture(mockSchemaContext))
286                 .when(newMockSchemaContextFactory).createEffectiveModelContext(anyCollection());
287
288         doAnswer(unused -> {
289             SettableFuture<SchemaContext> future = SettableFuture.create();
290             new Thread(() -> {
291                 doReturn(newMockSchemaSourceReg).when(mockRegistry).registerSchemaSource(any(),
292                         withSourceId(SOURCE_IDENTIFIER1));
293
294                 doReturn(newMockSchemaContextFactory).when(mockSchemaRepository)
295                         .createEffectiveModelContextFactory();
296
297                 slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1), masterRef),
298                         testKit.getRef());
299
300                 future.set(mockSchemaContext);
301             }).start();
302             return future;
303         }).when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
304
305         doReturn(mockSchemaContextFactory).when(mockSchemaRepository)
306                 .createEffectiveModelContextFactory();
307
308         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1), masterRef), testKit.getRef());
309
310         verify(mockMountPointBuilder, timeout(5000)).register();
311         verify(mockMountPointBuilder, after(500)).addInitialSchemaContext(mockSchemaContext);
312         verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), any());
313         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), any());
314         verify(mockMountPointBuilder).addService(eq(DOMActionService.class), any());
315         verify(mockMountPointBuilder).addService(eq(DOMNotificationService.class), any());
316         verify(mockSchemaSourceReg1).close();
317         verify(mockRegistry, times(2)).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
318         verify(mockSchemaRepository, times(2)).createEffectiveModelContextFactory();
319         verifyNoMoreInteractions(mockMountPointBuilder, newMockSchemaSourceReg);
320
321         // Stop the slave actor and verify schema source registrations are closed.
322
323         final Future<Boolean> stopFuture = Patterns.gracefulStop(slaveRef, TIMEOUT.duration());
324         Await.result(stopFuture, TIMEOUT.duration());
325
326         verify(mockMountPointReg).close();
327         verify(newMockSchemaSourceReg).close();
328     }
329
330     @SuppressWarnings("unchecked")
331     @Test
332     public void testRegisterMountPointWithSchemaFailures() throws Exception {
333         SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
334         doReturn(mockRegistry).when(schemaResourceDTO2).getSchemaRegistry();
335         doReturn(mockSchemaRepository).when(schemaResourceDTO2).getSchemaRepository();
336         final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setSchemaResourceDTO(schemaResourceDTO2)
337                 .setActorSystem(system).build();
338
339         final ActorRef slaveRef = system.actorOf(NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT,
340                 mockMountPointService));
341
342         // Test unrecoverable failure.
343
344         doReturn(Futures.immediateFailedFuture(new SchemaResolutionException("mock")))
345                 .when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
346
347         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1, SOURCE_IDENTIFIER2),
348                 masterRef), testKit.getRef());
349
350         testKit.expectMsgClass(Success.class);
351
352         verify(mockRegistry, timeout(5000)).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER1));
353         verify(mockRegistry, timeout(5000)).registerSchemaSource(any(), withSourceId(SOURCE_IDENTIFIER2));
354
355         verify(mockMountPointBuilder, after(1000).never()).register();
356         verify(mockSchemaSourceReg1, timeout(1000)).close();
357         verify(mockSchemaSourceReg2, timeout(1000)).close();
358
359         // Test recoverable AskTimeoutException - schema context resolution should be retried.
360
361         reset(mockSchemaSourceReg1, mockSchemaSourceReg2);
362
363         doReturn(Futures.immediateFailedFuture(new SchemaResolutionException("mock",
364                 new AskTimeoutException("timeout"))))
365             .doReturn(Futures.immediateFuture(mockSchemaContext))
366             .when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
367
368         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1, SOURCE_IDENTIFIER2),
369                 masterRef), testKit.getRef());
370
371         testKit.expectMsgClass(Success.class);
372
373         verify(mockMountPointBuilder, timeout(5000)).register();
374         verifyNoMoreInteractions(mockSchemaSourceReg1, mockSchemaSourceReg2);
375
376         // Test AskTimeoutException with an interleaved successful registration. The first schema context resolution
377         // attempt should not be retried.
378
379         reset(mockSchemaSourceReg1, mockSchemaSourceReg2, mockSchemaRepository, mockSchemaContextFactory);
380         resetMountPointMocks();
381
382         final EffectiveModelContextFactory mockSchemaContextFactorySuccess = mock(EffectiveModelContextFactory.class);
383         doReturn(Futures.immediateFuture(mockSchemaContext))
384                 .when(mockSchemaContextFactorySuccess).createEffectiveModelContext(anyCollection());
385
386         doAnswer(unused -> {
387             SettableFuture<SchemaContext> future = SettableFuture.create();
388             new Thread(() -> {
389                 doReturn(mockSchemaContextFactorySuccess).when(mockSchemaRepository)
390                     .createEffectiveModelContextFactory();
391
392                 slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1, SOURCE_IDENTIFIER2),
393                         masterRef), testKit.getRef());
394
395                 future.setException(new SchemaResolutionException("mock", new AskTimeoutException("timeout")));
396             }).start();
397             return future;
398         }).when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
399
400         doReturn(mockSchemaContextFactory).when(mockSchemaRepository).createEffectiveModelContextFactory();
401
402         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1, SOURCE_IDENTIFIER2),
403                 masterRef), testKit.getRef());
404
405         verify(mockMountPointBuilder, timeout(5000)).register();
406         verify(mockSchemaRepository, times(2)).createEffectiveModelContextFactory();
407     }
408
409     @Test(expected = MissingSchemaSourceException.class)
410     public void testMissingSchemaSourceOnMissingProvider() throws Exception {
411         SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
412         doReturn(DEFAULT_SCHEMA_REPOSITORY).when(schemaResourceDTO2).getSchemaRegistry();
413         doReturn(DEFAULT_SCHEMA_REPOSITORY).when(schemaResourceDTO2).getSchemaRepository();
414         final NetconfTopologySetup setup = NetconfTopologySetupBuilder.create().setActorSystem(system)
415                 .setSchemaResourceDTO(schemaResourceDTO2).setIdleTimeout(Duration.apply(1, TimeUnit.SECONDS)).build();
416         final Props props = NetconfNodeActor.props(setup, remoteDeviceId, TIMEOUT, mockMountPointService);
417         ActorRef actor = TestActorRef.create(system, props, "master_messages_2");
418
419         final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("testID");
420
421         final ProxyYangTextSourceProvider proxyYangProvider =
422                 new ProxyYangTextSourceProvider(actor, system.dispatcher(), TIMEOUT);
423
424         final Future<YangTextSchemaSourceSerializationProxy> resolvedSchemaFuture =
425                 proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
426         Await.result(resolvedSchemaFuture, TIMEOUT.duration());
427     }
428
429     @Test
430     public void testYangTextSchemaSourceRequest() throws Exception {
431         final SourceIdentifier sourceIdentifier = RevisionSourceIdentifier.create("testID");
432
433         final ProxyYangTextSourceProvider proxyYangProvider =
434                 new ProxyYangTextSourceProvider(masterRef, system.dispatcher(), TIMEOUT);
435
436         final YangTextSchemaSource yangTextSchemaSource = YangTextSchemaSource.delegateForByteSource(sourceIdentifier,
437                 ByteSource.wrap("YANG".getBytes(UTF_8)));
438
439         // Test success.
440
441         final SchemaSourceRegistration<YangTextSchemaSource> schemaSourceReg = masterSchemaRepository
442                 .registerSchemaSource(id -> Futures.immediateFuture(yangTextSchemaSource),
443                      PotentialSchemaSource.create(sourceIdentifier, YangTextSchemaSource.class, 1));
444
445         final Future<YangTextSchemaSourceSerializationProxy> resolvedSchemaFuture =
446                 proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
447
448         final YangTextSchemaSourceSerializationProxy success = Await.result(resolvedSchemaFuture, TIMEOUT.duration());
449
450         assertEquals(sourceIdentifier, success.getRepresentation().getIdentifier());
451         assertEquals("YANG", convertStreamToString(success.getRepresentation().openStream()));
452
453         // Test missing source failure.
454
455         exception.expect(MissingSchemaSourceException.class);
456
457         schemaSourceReg.close();
458
459         final Future<YangTextSchemaSourceSerializationProxy> failedSchemaFuture =
460                 proxyYangProvider.getYangTextSchemaSource(sourceIdentifier);
461
462         Await.result(failedSchemaFuture, TIMEOUT.duration());
463     }
464
465     @Test
466     @SuppressWarnings({"checkstyle:AvoidHidingCauseException", "checkstyle:IllegalThrows"})
467     public void testSlaveInvokeRpc() throws Throwable {
468
469         final List<SourceIdentifier> sourceIdentifiers =
470                 Lists.newArrayList(RevisionSourceIdentifier.create("testID"));
471
472         initializeMaster(sourceIdentifiers);
473         registerSlaveMountPoint();
474
475         ArgumentCaptor<DOMRpcService> domRPCServiceCaptor = ArgumentCaptor.forClass(DOMRpcService.class);
476         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), domRPCServiceCaptor.capture());
477
478         final DOMRpcService slaveDomRPCService = domRPCServiceCaptor.getValue();
479         assertTrue(slaveDomRPCService instanceof ProxyDOMRpcService);
480
481         final QName testQName = QName.create("", "TestQname");
482         final SchemaPath schemaPath = SchemaPath.create(true, testQName);
483         final NormalizedNode<?, ?> outputNode = ImmutableContainerNodeBuilder.create()
484                 .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(testQName))
485                 .withChild(ImmutableNodes.leafNode(testQName, "foo")).build();
486         final RpcError rpcError = RpcResultBuilder.newError(RpcError.ErrorType.RPC, null, "Rpc invocation failed.");
487
488         // RPC with no response output.
489
490         doReturn(FluentFutures.immediateNullFluentFuture()).when(mockDOMRpcService).invokeRpc(any(), any());
491
492         DOMRpcResult result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
493
494         assertEquals(null, result);
495
496         // RPC with response output.
497
498         doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult(outputNode)))
499                 .when(mockDOMRpcService).invokeRpc(any(), any());
500
501         result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
502
503         assertEquals(outputNode, result.getResult());
504         assertTrue(result.getErrors().isEmpty());
505
506         // RPC with response error.
507
508         doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult(rpcError)))
509                 .when(mockDOMRpcService).invokeRpc(any(), any());
510
511         result = slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
512
513         assertNull(result.getResult());
514         assertEquals(rpcError, result.getErrors().iterator().next());
515
516         // RPC with response output and error.
517
518         doReturn(FluentFutures.immediateFluentFuture(new DefaultDOMRpcResult(outputNode, rpcError)))
519                 .when(mockDOMRpcService).invokeRpc(any(), any());
520
521         final DOMRpcResult resultOutputError =
522                 slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
523
524         assertEquals(outputNode, resultOutputError.getResult());
525         assertEquals(rpcError, resultOutputError.getErrors().iterator().next());
526
527         // RPC failure.
528
529         exception.expect(DOMRpcException.class);
530
531         doReturn(FluentFutures.immediateFailedFluentFuture(new ClusteringRpcException("mock")))
532                 .when(mockDOMRpcService).invokeRpc(any(), any());
533
534         try {
535             slaveDomRPCService.invokeRpc(schemaPath, outputNode).get(2, TimeUnit.SECONDS);
536         } catch (ExecutionException e) {
537             throw e.getCause();
538         }
539     }
540
541     @Test
542     @SuppressWarnings({"checkstyle:AvoidHidingCauseException", "checkstyle:IllegalThrows"})
543     public void testSlaveInvokeAction() throws Throwable {
544         final List<SourceIdentifier> sourceIdentifiers = Lists
545             .newArrayList(RevisionSourceIdentifier.create("testActionID"));
546         initializeMaster(sourceIdentifiers);
547         registerSlaveMountPoint();
548
549         ArgumentCaptor<DOMActionService> domActionServiceCaptor = ArgumentCaptor.forClass(DOMActionService.class);
550         verify(mockMountPointBuilder).addService(eq(DOMActionService.class), domActionServiceCaptor.capture());
551
552         final DOMActionService slaveDomActionService = domActionServiceCaptor.getValue();
553         assertTrue(slaveDomActionService instanceof ProxyDOMActionService);
554
555         final QName testQName = QName.create("test", "2019-08-16", "TestActionQname");
556         final SchemaPath schemaPath = SchemaPath.create(true, testQName);
557
558         final YangInstanceIdentifier yangIIdPath = YangInstanceIdentifier
559             .create(new YangInstanceIdentifier.NodeIdentifier(testQName));
560
561         final DOMDataTreeIdentifier domDataTreeIdentifier = new DOMDataTreeIdentifier(LogicalDatastoreType.OPERATIONAL,
562             yangIIdPath);
563
564         final ContainerNode outputNode = ImmutableContainerNodeBuilder.create()
565             .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(testQName))
566             .withChild(ImmutableNodes.leafNode(testQName, "foo")).build();
567
568         // Action with no response output.
569         doReturn(FluentFutures.immediateNullFluentFuture()).when(mockDOMActionService)
570             .invokeAction(any(), any(), any());
571         DOMActionResult result = slaveDomActionService.invokeAction(schemaPath, domDataTreeIdentifier, outputNode)
572             .get(2, TimeUnit.SECONDS);
573         assertEquals(null, result);
574
575         // Action with response output.
576         doReturn(FluentFutures.immediateFluentFuture(new SimpleDOMActionResult(outputNode))).when(mockDOMActionService)
577             .invokeAction(any(), any(), any());
578         result = slaveDomActionService.invokeAction(schemaPath, domDataTreeIdentifier, outputNode)
579             .get(2, TimeUnit.SECONDS);
580
581         assertEquals(outputNode, result.getOutput().get());
582         assertTrue(result.getErrors().isEmpty());
583
584         // Action failure.
585         exception.expect(DOMActionException.class);
586         doReturn(FluentFutures.immediateFailedFluentFuture(new ClusteringActionException("mock")))
587             .when(mockDOMActionService).invokeAction(any(), any(), any());
588         try {
589             slaveDomActionService.invokeAction(schemaPath, domDataTreeIdentifier, outputNode).get(2, TimeUnit.SECONDS);
590         } catch (ExecutionException e) {
591             throw e.getCause();
592         }
593     }
594
595     @Test
596     public void testSlaveNewTransactionRequests() {
597
598         doReturn(mock(DOMDataTreeReadTransaction.class)).when(mockDOMDataBroker).newReadOnlyTransaction();
599         doReturn(mock(DOMDataTreeReadWriteTransaction.class)).when(mockDOMDataBroker).newReadWriteTransaction();
600         doReturn(mock(DOMDataTreeWriteTransaction.class)).when(mockDOMDataBroker).newWriteOnlyTransaction();
601
602         initializeMaster(Collections.emptyList());
603         registerSlaveMountPoint();
604
605         ArgumentCaptor<DOMDataBroker> domDataBrokerCaptor = ArgumentCaptor.forClass(DOMDataBroker.class);
606         verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), domDataBrokerCaptor.capture());
607
608         final DOMDataBroker slaveDOMDataBroker = domDataBrokerCaptor.getValue();
609         assertTrue(slaveDOMDataBroker instanceof ProxyDOMDataBroker);
610
611         slaveDOMDataBroker.newReadOnlyTransaction();
612         verify(mockDOMDataBroker).newReadOnlyTransaction();
613
614         slaveDOMDataBroker.newReadWriteTransaction();
615         verify(mockDOMDataBroker).newReadWriteTransaction();
616
617         slaveDOMDataBroker.newWriteOnlyTransaction();
618         verify(mockDOMDataBroker).newWriteOnlyTransaction();
619     }
620
621     private ActorRef registerSlaveMountPoint() {
622         SchemaResourcesDTO schemaResourceDTO2 = mock(SchemaResourcesDTO.class);
623         doReturn(mockRegistry).when(schemaResourceDTO2).getSchemaRegistry();
624         doReturn(mockSchemaRepository).when(schemaResourceDTO2).getSchemaRepository();
625         final ActorRef slaveRef = system.actorOf(NetconfNodeActor.props(
626                 NetconfTopologySetupBuilder.create().setSchemaResourceDTO(schemaResourceDTO2).setActorSystem(system)
627                 .build(), remoteDeviceId, TIMEOUT, mockMountPointService));
628
629         doReturn(Futures.immediateFuture(mockSchemaContext))
630                 .when(mockSchemaContextFactory).createEffectiveModelContext(anyCollection());
631
632         slaveRef.tell(new RegisterMountPoint(ImmutableList.of(SOURCE_IDENTIFIER1, SOURCE_IDENTIFIER2),
633                 masterRef), testKit.getRef());
634
635         verify(mockMountPointBuilder, timeout(5000)).register();
636         verify(mockMountPointBuilder).addInitialSchemaContext(mockSchemaContext);
637         verify(mockMountPointBuilder).addService(eq(DOMDataBroker.class), any());
638         verify(mockMountPointBuilder).addService(eq(DOMRpcService.class), any());
639         verify(mockMountPointBuilder).addService(eq(DOMNotificationService.class), any());
640
641         testKit.expectMsgClass(Success.class);
642
643         return slaveRef;
644     }
645
646     private void initializeMaster(final List<SourceIdentifier> sourceIdentifiers) {
647         masterRef.tell(new CreateInitialMasterActorData(mockDOMDataBroker, sourceIdentifiers,
648                 mockDOMRpcService, mockDOMActionService), testKit.getRef());
649
650         testKit.expectMsgClass(MasterActorDataInitialized.class);
651     }
652
653     private void resetMountPointMocks() {
654         reset(mockMountPointReg, mockMountPointBuilder);
655
656         doNothing().when(mockMountPointReg).close();
657
658         doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addInitialSchemaContext(any());
659         doReturn(mockMountPointBuilder).when(mockMountPointBuilder).addService(any(), any());
660         doReturn(mockMountPointReg).when(mockMountPointBuilder).register();
661     }
662
663     private static PotentialSchemaSource<?> withSourceId(final SourceIdentifier identifier) {
664         return argThat(argument -> identifier.equals(argument.getSourceIdentifier()));
665     }
666
667     private static String convertStreamToString(final InputStream is) {
668         try (Scanner scanner = new Scanner(is)) {
669             return scanner.useDelimiter("\\A").hasNext() ? scanner.next() : "";
670         }
671     }
672 }