Merge "Fix modules Restconf call for mounted devices"
[controller.git] / opendaylight / md-sal / sal-netconf-connector / src / test / java / org / opendaylight / controller / sal / connect / netconf / NetconfDeviceTest.java
1 /*
2 * Copyright (c) 2014 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.controller.sal.connect.netconf;
9
10 import static org.mockito.Matchers.any;
11 import static org.mockito.Matchers.anyCollectionOf;
12 import static org.mockito.Mockito.doAnswer;
13 import static org.mockito.Mockito.doNothing;
14 import static org.mockito.Mockito.doReturn;
15 import static org.mockito.Mockito.mock;
16 import static org.mockito.Mockito.timeout;
17 import static org.mockito.Mockito.times;
18 import static org.mockito.Mockito.verify;
19
20 import com.google.common.base.Optional;
21 import com.google.common.collect.HashMultimap;
22 import com.google.common.collect.Lists;
23 import com.google.common.util.concurrent.Futures;
24 import java.io.InputStream;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Set;
30 import java.util.concurrent.ExecutorService;
31 import java.util.concurrent.Executors;
32 import org.junit.Test;
33 import org.mockito.Mockito;
34 import org.mockito.invocation.InvocationOnMock;
35 import org.mockito.stubbing.Answer;
36 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
37 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
38 import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult;
39 import org.opendaylight.controller.netconf.api.NetconfMessage;
40 import org.opendaylight.controller.netconf.api.xml.XmlNetconfConstants;
41 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
42 import org.opendaylight.controller.sal.connect.api.MessageTransformer;
43 import org.opendaylight.controller.sal.connect.api.RemoteDeviceHandler;
44 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
45 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
46 import org.opendaylight.controller.sal.connect.netconf.sal.NetconfDeviceRpc;
47 import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil;
48 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
49 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
50 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
51 import org.opendaylight.yangtools.yang.model.api.Module;
52 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
53 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
54 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
55 import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
56 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
57 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
58 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
59 import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource;
60 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration;
61 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
62 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
63
64 public class NetconfDeviceTest {
65
66     private static final NetconfMessage notification;
67
68     private static final ContainerNode compositeNode;
69
70     static {
71         try {
72             compositeNode = mockClass(ContainerNode.class);
73         } catch (final Exception e) {
74             throw new RuntimeException(e);
75         }
76         try {
77             notification = new NetconfMessage(XmlUtil.readXmlToDocument(NetconfDeviceTest.class.getResourceAsStream("/notification-payload.xml")));
78         } catch (Exception e) {
79             throw new ExceptionInInitializerError(e);
80         }
81     }
82
83     private static final DOMRpcResult rpcResultC = new DefaultDOMRpcResult(compositeNode);
84
85     public static final String TEST_NAMESPACE = "test:namespace";
86     public static final String TEST_MODULE = "test-module";
87     public static final String TEST_REVISION = "2013-07-22";
88     public static final SourceIdentifier TEST_SID = new SourceIdentifier(TEST_MODULE, Optional.of(TEST_REVISION));
89     public static final String TEST_CAPABILITY = TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION;
90
91     public static final SourceIdentifier TEST_SID2 = new SourceIdentifier(TEST_MODULE + "2", Optional.of(TEST_REVISION));
92     public static final String TEST_CAPABILITY2 = TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION;
93
94     private static final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() {
95
96         @Override
97         public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) {
98             return NetconfStateSchemas.EMPTY;
99         }
100     };
101
102     @Test
103     public void testNetconfDeviceFailFirstSchemaFailSecondEmpty() throws Exception {
104         final ArrayList<String> capList = Lists.newArrayList(TEST_CAPABILITY);
105
106         final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
107         final NetconfDeviceCommunicator listener = getListener();
108
109         final SchemaContextFactory schemaFactory = getSchemaFactory();
110
111         // Make fallback attempt to fail due to empty resolved sources
112         final SchemaResolutionException schemaResolutionException
113                 = new SchemaResolutionException("fail first",
114                 Collections.<SourceIdentifier>emptyList(), HashMultimap.<SourceIdentifier, ModuleImport>create());
115         doReturn(Futures.immediateFailedCheckedFuture(
116                 schemaResolutionException))
117                 .when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
118
119         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
120                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
121         final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(),true);
122         // Monitoring not supported
123         final NetconfSessionPreferences sessionCaps = getSessionCaps(false, capList);
124         device.onRemoteSessionUp(sessionCaps, listener);
125
126         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceDisconnected();
127         Mockito.verify(listener, Mockito.timeout(5000)).close();
128         Mockito.verify(schemaFactory, times(1)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
129     }
130
131     @Test
132     public void testNetconfDeviceMissingSource() throws Exception {
133         final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
134         final NetconfDeviceCommunicator listener = getListener();
135
136         final SchemaContextFactory schemaFactory = getSchemaFactory();
137
138         // Make fallback attempt to fail due to empty resolved sources
139         final MissingSchemaSourceException schemaResolutionException = new MissingSchemaSourceException("fail first", TEST_SID);
140         doAnswer(new Answer<Object>() {
141             @Override
142             public Object answer(final InvocationOnMock invocation) throws Throwable {
143                 if(((Collection<?>) invocation.getArguments()[0]).size() == 2) {
144                     return Futures.immediateFailedCheckedFuture(schemaResolutionException);
145                 } else {
146                     return Futures.immediateCheckedFuture(getSchema());
147                 }
148             }
149         }).when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
150
151         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
152                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaFactory, stateSchemasResolver);
153         final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
154         // Monitoring supported
155         final NetconfSessionPreferences sessionCaps = getSessionCaps(true, Lists.newArrayList(TEST_CAPABILITY, TEST_CAPABILITY2));
156         device.onRemoteSessionUp(sessionCaps, listener);
157
158         Mockito.verify(facade, Mockito.timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
159         Mockito.verify(schemaFactory, times(2)).createSchemaContext(anyCollectionOf(SourceIdentifier.class));
160     }
161
162     private SchemaSourceRegistry getSchemaRegistry() {
163         final SchemaSourceRegistry mock = mock(SchemaSourceRegistry.class);
164         final SchemaSourceRegistration<?> mockReg = mock(SchemaSourceRegistration.class);
165         doNothing().when(mockReg).close();
166         doReturn(mockReg).when(mock).registerSchemaSource(any(org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider.class), any(PotentialSchemaSource.class));
167         return mock;
168     }
169
170     @Test
171     public void testNotificationBeforeSchema() throws Exception {
172         final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
173         final NetconfDeviceCommunicator listener = getListener();
174
175         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
176                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), getSchemaFactory(), stateSchemasResolver);
177         final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
178
179         device.onNotification(notification);
180         device.onNotification(notification);
181
182         verify(facade, times(0)).onNotification(any(ContainerNode.class));
183
184         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
185                 Lists.newArrayList(TEST_CAPABILITY));
186
187         final DOMRpcService deviceRpc = mock(DOMRpcService.class);
188
189         device.handleSalInitializationSuccess(NetconfToNotificationTest.getNotificationSchemaContext(getClass()), sessionCaps, deviceRpc);
190
191         verify(facade, timeout(10000).times(2)).onNotification(any(ContainerNode.class));
192
193         device.onNotification(notification);
194         verify(facade, timeout(10000).times(3)).onNotification(any(ContainerNode.class));
195     }
196
197     @Test
198     public void testNetconfDeviceReconnect() throws Exception {
199         final RemoteDeviceHandler<NetconfSessionPreferences> facade = getFacade();
200         final NetconfDeviceCommunicator listener = getListener();
201
202         final SchemaContextFactory schemaContextProviderFactory = getSchemaFactory();
203
204         final NetconfDevice.SchemaResourcesDTO schemaResourcesDTO
205                 = new NetconfDevice.SchemaResourcesDTO(getSchemaRegistry(), schemaContextProviderFactory, stateSchemasResolver);
206         final NetconfDevice device = new NetconfDevice(schemaResourcesDTO, getId(), facade, getExecutor(), true);
207         final NetconfSessionPreferences sessionCaps = getSessionCaps(true,
208                 Lists.newArrayList(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&amp;revision=" + TEST_REVISION));
209         device.onRemoteSessionUp(sessionCaps, listener);
210
211         verify(schemaContextProviderFactory, timeout(5000)).createSchemaContext(any(Collection.class));
212         verify(facade, timeout(5000)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
213
214         device.onRemoteSessionDown();
215         verify(facade, timeout(5000)).onDeviceDisconnected();
216
217         device.onRemoteSessionUp(sessionCaps, listener);
218
219         verify(schemaContextProviderFactory, timeout(5000).times(2)).createSchemaContext(any(Collection.class));
220         verify(facade, timeout(5000).times(2)).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(DOMRpcService.class));
221     }
222
223     private SchemaContextFactory getSchemaFactory() {
224         final SchemaContextFactory schemaFactory = mockClass(SchemaContextFactory.class);
225         doReturn(Futures.immediateCheckedFuture(getSchema())).when(schemaFactory).createSchemaContext(any(Collection.class));
226         return schemaFactory;
227     }
228
229     public static SchemaContext getSchema() {
230         final YangParserImpl parser = new YangParserImpl();
231         final List<InputStream> modelsToParse = Lists.newArrayList(
232                 NetconfDeviceTest.class.getResourceAsStream("/schemas/test-module.yang")
233         );
234         final Set<Module> models = parser.parseYangModelsFromStreams(modelsToParse);
235         return parser.resolveSchemaContext(models);
236     }
237
238     private RemoteDeviceHandler<NetconfSessionPreferences> getFacade() throws Exception {
239         final RemoteDeviceHandler<NetconfSessionPreferences> remoteDeviceHandler = mockCloseableClass(RemoteDeviceHandler.class);
240         doNothing().when(remoteDeviceHandler).onDeviceConnected(any(SchemaContext.class), any(NetconfSessionPreferences.class), any(NetconfDeviceRpc.class));
241         doNothing().when(remoteDeviceHandler).onDeviceDisconnected();
242         doNothing().when(remoteDeviceHandler).onNotification(any(ContainerNode.class));
243         return remoteDeviceHandler;
244     }
245
246     private <T extends AutoCloseable> T mockCloseableClass(final Class<T> remoteDeviceHandlerClass) throws Exception {
247         final T mock = mockClass(remoteDeviceHandlerClass);
248         doNothing().when(mock).close();
249         return mock;
250     }
251
252     private static <T> T mockClass(final Class<T> remoteDeviceHandlerClass) {
253         final T mock = mock(remoteDeviceHandlerClass);
254         Mockito.doReturn(remoteDeviceHandlerClass.getSimpleName()).when(mock).toString();
255         return mock;
256     }
257
258     public RemoteDeviceId getId() {
259         return new RemoteDeviceId("test-D");
260     }
261
262     public ExecutorService getExecutor() {
263         return Executors.newSingleThreadExecutor();
264     }
265
266     public MessageTransformer<NetconfMessage> getMessageTransformer() throws Exception {
267         final MessageTransformer<NetconfMessage> messageTransformer = mockClass(MessageTransformer.class);
268         doReturn(notification).when(messageTransformer).toRpcRequest(any(SchemaPath.class), any(NormalizedNode.class));
269         doReturn(rpcResultC).when(messageTransformer).toRpcResult(any(NetconfMessage.class), any(SchemaPath.class));
270         doReturn(compositeNode).when(messageTransformer).toNotification(any(NetconfMessage.class));
271         return messageTransformer;
272     }
273
274     public NetconfSessionPreferences getSessionCaps(final boolean addMonitor, final Collection<String> additionalCapabilities) {
275         final ArrayList<String> capabilities = Lists.newArrayList(
276                 XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_0,
277                 XmlNetconfConstants.URN_IETF_PARAMS_NETCONF_BASE_1_1);
278
279         if(addMonitor) {
280             capabilities.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString());
281         }
282
283         capabilities.addAll(additionalCapabilities);
284
285         return NetconfSessionPreferences.fromStrings(
286                 capabilities);
287     }
288
289     public NetconfDeviceCommunicator getListener() throws Exception {
290         final NetconfDeviceCommunicator remoteDeviceCommunicator = mockCloseableClass(NetconfDeviceCommunicator.class);
291 //        doReturn(Futures.immediateFuture(rpcResult)).when(remoteDeviceCommunicator).sendRequest(any(NetconfMessage.class), any(QName.class));
292         return remoteDeviceCommunicator;
293     }
294 }