a95eed54b784b1b8ef01ee7030ee60f5805df57e
[netconf.git] / plugins / netconf-client-mdsal / src / test / java / org / opendaylight / netconf / client / mdsal / NetconfDeviceTest.java
1 /*
2  * Copyright (c) 2014, 2015 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.client.mdsal;
9
10 import static org.hamcrest.CoreMatchers.instanceOf;
11 import static org.hamcrest.MatcherAssert.assertThat;
12 import static org.junit.Assert.assertEquals;
13 import static org.junit.Assert.assertFalse;
14 import static org.junit.Assert.assertTrue;
15 import static org.mockito.ArgumentMatchers.any;
16 import static org.mockito.ArgumentMatchers.anyCollection;
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.spy;
24 import static org.mockito.Mockito.timeout;
25 import static org.mockito.Mockito.times;
26 import static org.mockito.Mockito.verify;
27
28 import com.google.common.collect.Sets;
29 import com.google.common.util.concurrent.Futures;
30 import com.google.common.util.concurrent.MoreExecutors;
31 import com.google.common.util.concurrent.SettableFuture;
32 import java.net.InetSocketAddress;
33 import java.util.ArrayList;
34 import java.util.Collection;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import org.junit.BeforeClass;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.mockito.ArgumentCaptor;
43 import org.mockito.Mock;
44 import org.mockito.junit.MockitoJUnitRunner;
45 import org.opendaylight.mdsal.dom.api.DOMNotification;
46 import org.opendaylight.netconf.api.CapabilityURN;
47 import org.opendaylight.netconf.api.messages.NetconfMessage;
48 import org.opendaylight.netconf.api.xml.XmlUtil;
49 import org.opendaylight.netconf.client.mdsal.NetconfDevice.EmptySchemaContextException;
50 import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
51 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
52 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceHandler;
53 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
54 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
55 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
58 import org.opendaylight.yangtools.concepts.Registration;
59 import org.opendaylight.yangtools.yang.common.QName;
60 import org.opendaylight.yangtools.yang.model.api.Module;
61 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
62 import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
63 import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
64 import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
65 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
66 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
67 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
68 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
69
70 @RunWith(MockitoJUnitRunner.StrictStubs.class)
71 public class NetconfDeviceTest extends AbstractTestModelTest {
72     public static final String TEST_NAMESPACE = "test:namespace";
73     public static final String TEST_MODULE = "test-module";
74     public static final String TEST_REVISION = "2013-07-22";
75     public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, TEST_REVISION);
76     public static final String TEST_CAPABILITY =
77             TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION;
78
79     public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", TEST_REVISION);
80     public static final String TEST_CAPABILITY2 =
81             TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION;
82
83     private static final NetconfDeviceSchemasResolver STATE_SCHEMAS_RESOLVER =
84         (deviceRpc, remoteSessionCapabilities, id, schemaContext) -> NetconfStateSchemas.EMPTY;
85
86     private static NetconfMessage NOTIFICATION;
87
88     @Mock
89     private SchemaSourceRegistry schemaRegistry;
90
91     @BeforeClass
92     public static final void setupNotification() throws Exception {
93         NOTIFICATION = new NetconfMessage(XmlUtil.readXmlToDocument(
94             NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
95     }
96
97     @Test
98     public void testNetconfDeviceFlawedModelFailedResolution() throws Exception {
99         final RemoteDeviceHandler facade = getFacade();
100         final NetconfDeviceCommunicator listener = getListener();
101
102         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
103         final SchemaRepository schemaRepository = getSchemaRepository();
104
105         final SchemaResolutionException schemaResolutionException =
106                 new SchemaResolutionException("fail first", TEST_SID, new Throwable("YangTools parser fail"));
107         doAnswer(invocation -> {
108             if (((Collection<?>) invocation.getArguments()[0]).size() == 2) {
109                 return Futures.immediateFailedFuture(schemaResolutionException);
110             } else {
111                 return Futures.immediateFuture(SCHEMA_CONTEXT);
112             }
113         }).when(schemaFactory).createEffectiveModelContext(anyCollection());
114
115         final NetconfDeviceSchemasResolver stateSchemasResolver = (deviceRpc, remoteSessionCapabilities, id,
116                 schemaContext) -> {
117             final Module first = SCHEMA_CONTEXT.getModules().iterator().next();
118             final QName qName = QName.create(first.getQNameModule(), first.getName());
119             final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName);
120             final NetconfStateSchemas.RemoteYangSchema source2 =
121                     new NetconfStateSchemas.RemoteYangSchema(QName.create(first.getQNameModule(), "test-module2"));
122             return new NetconfStateSchemas(Sets.newHashSet(source1, source2));
123         };
124
125         doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
126         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
127                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
128
129         final NetconfDevice device = new NetconfDeviceBuilder()
130                 .setReconnectOnSchemasChange(true)
131                 .setSchemaResourcesDTO(schemaResourcesDTO)
132                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
133                 .setId(getId())
134                 .setSalFacade(facade)
135                 .setBaseSchemas(BASE_SCHEMAS)
136                 .build();
137         // Monitoring supported
138         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
139         device.onRemoteSessionUp(sessionCaps, listener);
140
141         verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
142             any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
143         verify(schemaFactory, times(2)).createEffectiveModelContext(anyCollection());
144     }
145
146     @Test
147     public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
148         final RemoteDeviceHandler facade = getFacade();
149         final NetconfDeviceCommunicator listener = getListener();
150
151         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
152         final SchemaRepository schemaRepository = getSchemaRepository();
153
154         // Make fallback attempt to fail due to empty resolved sources
155         final SchemaResolutionException schemaResolutionException = new SchemaResolutionException("fail first",
156             new SourceIdentifier("test-module", "2013-07-22"), new Throwable());
157         doReturn(Futures.immediateFailedFuture(schemaResolutionException))
158                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
159
160         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
161                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, STATE_SCHEMAS_RESOLVER);
162         final NetconfDevice device = new NetconfDeviceBuilder()
163                 .setReconnectOnSchemasChange(true)
164                 .setSchemaResourcesDTO(schemaResourcesDTO)
165                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
166                 .setId(getId())
167                 .setSalFacade(facade)
168                 .setBaseSchemas(BASE_SCHEMAS)
169                 .build();
170
171         // Monitoring not supported
172         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(TEST_CAPABILITY));
173         device.onRemoteSessionUp(sessionCaps, listener);
174
175         final var captor = ArgumentCaptor.forClass(Throwable.class);
176         verify(facade, timeout(5000)).onDeviceFailed(captor.capture());
177         assertThat(captor.getValue(), instanceOf(EmptySchemaContextException.class));
178
179         verify(listener, timeout(5000)).close();
180         verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
181     }
182
183     @Test
184     public void testNetconfDeviceMissingSource() throws Exception {
185         final RemoteDeviceHandler facade = getFacade();
186         final NetconfDeviceCommunicator listener = getListener();
187
188         final EffectiveModelContextFactory schemaFactory = getSchemaFactory();
189         final SchemaRepository schemaRepository = getSchemaRepository();
190
191         // Make fallback attempt to fail due to empty resolved sources
192         final MissingSchemaSourceException schemaResolutionException =
193                 new MissingSchemaSourceException(TEST_SID, "fail first");
194         doReturn(Futures.immediateFailedFuture(schemaResolutionException))
195                 .when(schemaRepository).getSchemaSource(eq(TEST_SID), eq(YangTextSource.class));
196         doAnswer(invocation -> {
197             if (invocation.getArgument(0, Collection.class).size() == 2) {
198                 return Futures.immediateFailedFuture(schemaResolutionException);
199             } else {
200                 return Futures.immediateFuture(SCHEMA_CONTEXT);
201             }
202         }).when(schemaFactory).createEffectiveModelContext(anyCollection());
203
204         final NetconfDeviceSchemasResolver stateSchemasResolver = (deviceRpc, remoteSessionCapabilities, id,
205             schemaContext) -> {
206             final Module first = SCHEMA_CONTEXT.getModules().iterator().next();
207             final QName qName = QName.create(first.getQNameModule(), first.getName());
208             final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName);
209             final NetconfStateSchemas.RemoteYangSchema source2 =
210                     new NetconfStateSchemas.RemoteYangSchema(QName.create(first.getQNameModule(), "test-module2"));
211             return new NetconfStateSchemas(Sets.newHashSet(source1, source2));
212         };
213
214         doReturn(mock(Registration.class)).when(schemaRegistry).registerSchemaSource(any(), any());
215         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice
216                 .SchemaResourcesDTO(schemaRegistry, schemaRepository, schemaFactory, stateSchemasResolver);
217
218         final NetconfDevice device = new NetconfDeviceBuilder()
219                 .setReconnectOnSchemasChange(true)
220                 .setSchemaResourcesDTO(schemaResourcesDTO)
221                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
222                 .setBaseSchemas(BASE_SCHEMAS)
223                 .setId(getId())
224                 .setSalFacade(facade)
225                 .build();
226         // Monitoring supported
227         final NetconfSessionPreferences sessionCaps =
228                 getSessionCaps(true, List.of(TEST_CAPABILITY, TEST_CAPABILITY2));
229         device.onRemoteSessionUp(sessionCaps, listener);
230
231         verify(facade, timeout(5000)).onDeviceConnected(any(NetconfDeviceSchema.class),
232             any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
233         verify(schemaFactory, times(1)).createEffectiveModelContext(anyCollection());
234     }
235
236     private static SchemaRepository getSchemaRepository() {
237         final SchemaRepository mock = mock(SchemaRepository.class);
238         final YangTextSource mockRep = mock(YangTextSource.class);
239         doReturn(Futures.immediateFuture(mockRep))
240                 .when(mock).getSchemaSource(any(SourceIdentifier.class), eq(YangTextSource.class));
241         return mock;
242     }
243
244     @Test
245     public void testNotificationBeforeSchema() throws Exception {
246         final RemoteDeviceHandler facade = getFacade();
247         final NetconfDeviceCommunicator listener = getListener();
248         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
249         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
250         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
251         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
252             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
253         final NetconfDevice device = new NetconfDeviceBuilder()
254                 .setReconnectOnSchemasChange(true)
255                 .setSchemaResourcesDTO(schemaResourcesDTO)
256                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
257                 .setId(getId())
258                 .setSalFacade(facade)
259                 .setBaseSchemas(BASE_SCHEMAS)
260                 .build();
261
262         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, List.of(TEST_CAPABILITY));
263         device.onRemoteSessionUp(sessionCaps, listener);
264
265         device.onNotification(NOTIFICATION);
266         device.onNotification(NOTIFICATION);
267         verify(facade, times(0)).onNotification(any(DOMNotification.class));
268
269         verify(facade, times(0)).onNotification(any(DOMNotification.class));
270         schemaFuture.set(NetconfToNotificationTest.getNotificationSchemaContext(getClass(), false));
271         verify(facade, timeout(10000).times(2)).onNotification(any(DOMNotification.class));
272
273         device.onNotification(NOTIFICATION);
274         verify(facade, timeout(10000).times(3)).onNotification(any(DOMNotification.class));
275     }
276
277     @Test
278     public void testNetconfDeviceReconnect() throws Exception {
279         final RemoteDeviceHandler facade = getFacade();
280         final NetconfDeviceCommunicator listener = getListener();
281
282         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
283
284         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
285                 schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
286         final NetconfDevice device = new NetconfDeviceBuilder()
287                 .setReconnectOnSchemasChange(true)
288                 .setSchemaResourcesDTO(schemaResourcesDTO)
289                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
290                 .setId(getId())
291                 .setSalFacade(facade)
292                 .setBaseSchemas(BASE_SCHEMAS)
293                 .build();
294         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
295                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
296         device.onRemoteSessionUp(sessionCaps, listener);
297
298         verify(schemaContextProviderFactory, timeout(5000)).createEffectiveModelContext(anyCollection());
299         verify(facade, timeout(5000)).onDeviceConnected(
300                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
301
302         device.onRemoteSessionDown();
303         verify(facade, timeout(5000)).onDeviceDisconnected();
304
305         device.onRemoteSessionUp(sessionCaps, listener);
306
307         verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
308         verify(facade, timeout(5000).times(2)).onDeviceConnected(
309                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
310     }
311
312     @Test
313     public void testNetconfDeviceDisconnectListenerCallCancellation() throws Exception {
314         final RemoteDeviceHandler facade = getFacade();
315         final NetconfDeviceCommunicator listener = getListener();
316         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
317         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
318         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
319         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
320             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
321         final NetconfDevice device = new NetconfDeviceBuilder()
322                 .setReconnectOnSchemasChange(true)
323                 .setSchemaResourcesDTO(schemaResourcesDTO)
324                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
325                 .setId(getId())
326                 .setSalFacade(facade)
327                 .setBaseSchemas(BASE_SCHEMAS)
328                 .build();
329         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
330                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
331         //session up, start schema resolution
332         device.onRemoteSessionUp(sessionCaps, listener);
333         //session closed
334         device.onRemoteSessionDown();
335         verify(facade, timeout(5000)).onDeviceDisconnected();
336         //complete schema setup
337         schemaFuture.set(SCHEMA_CONTEXT);
338         //facade.onDeviceDisconnected() was called, so facade.onDeviceConnected() shouldn't be called anymore
339         verify(facade, after(1000).never()).onDeviceConnected(any(), any(), any(RemoteDeviceServices.class));
340     }
341
342     @Test
343     public void testNetconfDeviceReconnectBeforeSchemaSetup() throws Exception {
344         final RemoteDeviceHandler facade = getFacade();
345
346         final EffectiveModelContextFactory schemaContextProviderFactory = mock(EffectiveModelContextFactory.class);
347         final SettableFuture<SchemaContext> schemaFuture = SettableFuture.create();
348         doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
349
350         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(
351             schemaRegistry, getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
352         final NetconfDevice device = new NetconfDeviceBuilder()
353             .setReconnectOnSchemasChange(true)
354             .setSchemaResourcesDTO(schemaResourcesDTO)
355             .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
356             .setId(getId())
357             .setSalFacade(facade)
358             .setBaseSchemas(BASE_SCHEMAS)
359             .build();
360         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
361             List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
362
363         final NetconfDeviceCommunicator listener = getListener();
364         // session up, start schema resolution
365         device.onRemoteSessionUp(sessionCaps, listener);
366         // session down
367         device.onRemoteSessionDown();
368         verify(facade, timeout(5000)).onDeviceDisconnected();
369         // session back up, start another schema resolution
370         device.onRemoteSessionUp(sessionCaps, listener);
371         // complete schema setup
372         schemaFuture.set(SCHEMA_CONTEXT);
373         // schema setup performed twice
374         verify(schemaContextProviderFactory, timeout(5000).times(2)).createEffectiveModelContext(anyCollection());
375         // onDeviceConnected called once
376         verify(facade, timeout(5000)).onDeviceConnected(
377             any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
378     }
379
380     @Test
381     public void testNetconfDeviceAvailableCapabilitiesBuilding() throws Exception {
382         final RemoteDeviceHandler facade = getFacade();
383         final NetconfDeviceCommunicator listener = getListener();
384
385         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
386
387         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
388             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
389         final NetconfDevice device = new NetconfDeviceBuilder()
390                 .setReconnectOnSchemasChange(true)
391                 .setSchemaResourcesDTO(schemaResourcesDTO)
392                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
393                 .setId(getId())
394                 .setSalFacade(facade)
395                 .setBaseSchemas(BASE_SCHEMAS)
396                 .build();
397         final NetconfDevice netconfSpy = spy(device);
398
399         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
400                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
401         final Map<QName, CapabilityOrigin> moduleBasedCaps = new HashMap<>();
402         moduleBasedCaps.putAll(sessionCaps.moduleBasedCaps());
403         moduleBasedCaps
404                 .put(QName.create("(test:qname:side:loading)test"), CapabilityOrigin.UserDefined);
405
406         netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
407
408         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
409         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
410             any(RemoteDeviceServices.class));
411         argument.getValue().capabilities().resolvedCapabilities()
412                 .forEach(entry -> assertEquals("Builded 'AvailableCapability' schemas should match input capabilities.",
413                         moduleBasedCaps.get(
414                                 QName.create(entry.getCapability())).getName(), entry.getCapabilityOrigin().getName()));
415     }
416
417     @Test
418     public void testNetconfDeviceNotificationsModelNotPresentWithCapability() throws Exception {
419         final RemoteDeviceHandler facade = getFacade();
420         final NetconfDeviceCommunicator listener = getListener();
421         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
422
423         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
424             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
425         final NetconfDevice device = new NetconfDeviceBuilder()
426                 .setSchemaResourcesDTO(schemaResourcesDTO)
427                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
428                 .setId(getId())
429                 .setSalFacade(facade)
430                 .setBaseSchemas(BASE_SCHEMAS)
431                 .build();
432         final NetconfDevice netconfSpy = spy(device);
433
434         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of(CapabilityURN.NOTIFICATION));
435
436         netconfSpy.onRemoteSessionUp(sessionCaps, listener);
437
438         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
439         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
440                 any(RemoteDeviceServices.class));
441
442         List<String> notificationModulesName = List.of(
443                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
444                         .YangModuleInfoImpl.getInstance().getName().toString(),
445                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
446                         .YangModuleInfoImpl.getInstance().getName().toString());
447
448         final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
449
450         assertEquals(2, resolvedCapabilities.size());
451         assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
452                 .contains(entry.getCapability())));
453     }
454
455     @Test
456     public void testNetconfDeviceNotificationsCapabilityIsNotPresent() throws Exception {
457         final RemoteDeviceHandler facade = getFacade();
458         final NetconfDeviceCommunicator listener = getListener();
459         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
460
461         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
462             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
463         final NetconfDevice device = new NetconfDeviceBuilder()
464                 .setSchemaResourcesDTO(schemaResourcesDTO)
465                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
466                 .setId(getId())
467                 .setSalFacade(facade)
468                 .setBaseSchemas(BASE_SCHEMAS)
469                 .build();
470         final NetconfDevice netconfSpy = spy(device);
471
472         final NetconfSessionPreferences sessionCaps = getSessionCaps(false,
473                 List.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
474
475         netconfSpy.onRemoteSessionUp(sessionCaps, listener);
476
477         final ArgumentCaptor<NetconfDeviceSchema> argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
478         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
479                 any(RemoteDeviceServices.class));
480         final NetconfDeviceCapabilities netconfDeviceCaps = argument.getValue().capabilities();
481
482         List<String> notificationModulesName = List.of(
483                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
484                         .YangModuleInfoImpl.getInstance().getName().toString(),
485                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
486                         .YangModuleInfoImpl.getInstance().getName().toString());
487
488         assertFalse(netconfDeviceCaps.resolvedCapabilities().stream()
489             .anyMatch(entry -> notificationModulesName.contains(entry.getCapability())));
490     }
491
492     @Test
493     public void testNetconfDeviceNotificationsModelIsPresent() throws Exception {
494         final RemoteDeviceHandler facade = getFacade();
495         final NetconfDeviceCommunicator listener = getListener();
496         final EffectiveModelContextFactory schemaContextProviderFactory = getSchemaFactory();
497
498         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry,
499             getSchemaRepository(), schemaContextProviderFactory, STATE_SCHEMAS_RESOLVER);
500         final NetconfDevice device = new NetconfDeviceBuilder()
501                 .setSchemaResourcesDTO(schemaResourcesDTO)
502                 .setGlobalProcessingExecutor(MoreExecutors.directExecutor())
503                 .setId(getId())
504                 .setSalFacade(facade)
505                 .setBaseSchemas(BASE_SCHEMAS)
506                 .build();
507         final NetconfDevice netconfSpy = spy(device);
508
509         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, List.of());
510
511         final var moduleBasedCaps = new HashMap<QName, CapabilityOrigin>();
512         moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
513                         .YangModuleInfoImpl.getInstance().getName(),
514                 CapabilityOrigin.DeviceAdvertised);
515         moduleBasedCaps.put(org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
516                         .YangModuleInfoImpl.getInstance().getName(),
517                 CapabilityOrigin.DeviceAdvertised);
518
519
520         netconfSpy.onRemoteSessionUp(sessionCaps.replaceModuleCaps(moduleBasedCaps), listener);
521
522         final var argument = ArgumentCaptor.forClass(NetconfDeviceSchema.class);
523         verify(facade, timeout(5000)).onDeviceConnected(argument.capture(), any(NetconfSessionPreferences.class),
524                 any(RemoteDeviceServices.class));
525         final Set<AvailableCapability> resolvedCapabilities = argument.getValue().capabilities().resolvedCapabilities();
526
527         final var notificationModulesName = List.of(
528                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
529                         .YangModuleInfoImpl.getInstance().getName().toString(),
530                 org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
531                         .YangModuleInfoImpl.getInstance().getName().toString());
532
533         assertEquals(2, resolvedCapabilities.size());
534         assertTrue(resolvedCapabilities.stream().anyMatch(entry -> notificationModulesName
535                 .contains(entry.getCapability())));
536     }
537
538     private static EffectiveModelContextFactory getSchemaFactory() throws Exception {
539         final EffectiveModelContextFactory schemaFactory = mock(EffectiveModelContextFactory.class);
540         doReturn(Futures.immediateFuture(SCHEMA_CONTEXT))
541                 .when(schemaFactory).createEffectiveModelContext(anyCollection());
542         return schemaFactory;
543     }
544
545     private static RemoteDeviceHandler getFacade() throws Exception {
546         final RemoteDeviceHandler remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
547         doNothing().when(remoteDeviceHandler).onDeviceConnected(
548                 any(NetconfDeviceSchema.class), any(NetconfSessionPreferences.class), any(RemoteDeviceServices.class));
549         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
550         doNothing().when(remoteDeviceHandler).onNotification(any(DOMNotification.class));
551         return remoteDeviceHandler;
552     }
553
554     private static <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass)
555             throws Exception {
556         final T mock = mock(remoteDeviceHandlerClass);
557         doNothing().when(mock).close();
558         return mock;
559     }
560
561     public RemoteDeviceId getId() {
562         return new RemoteDeviceId("test-D", InetSocketAddress.createUnresolved("localhost", 22));
563     }
564
565     public NetconfSessionPreferences getSessionCaps(final boolean addMonitor,
566                                                     final Collection<String> additionalCapabilities) {
567         final var capabilities = new ArrayList<String>();
568         capabilities.add(CapabilityURN.BASE);
569         capabilities.add(CapabilityURN.BASE_1_1);
570         if (addMonitor) {
571             capabilities.add(NetconfState.QNAME.getNamespace().toString());
572         }
573         capabilities.addAll(additionalCapabilities);
574         return NetconfSessionPreferences.fromStrings(capabilities);
575     }
576
577     public NetconfDeviceCommunicator getListener() throws Exception {
578         return mockCloseableClass(NetconfDeviceCommunicator.class);
579     }
580 }