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