Convert NetconfSalKeystoreService into a component
[netconf.git] / plugins / sal-netconf-connector / src / test / java / org / opendaylight / netconf / sal / connect / netconf / util / NetconfBaseOpsTest.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.sal.connect.netconf.util;
9
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertThrows;
12 import static org.junit.Assert.assertTrue;
13 import static org.mockito.ArgumentMatchers.any;
14 import static org.mockito.ArgumentMatchers.argThat;
15 import static org.mockito.ArgumentMatchers.eq;
16 import static org.mockito.Mockito.verify;
17 import static org.mockito.Mockito.when;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.InetSocketAddress;
22 import java.util.ArrayList;
23 import java.util.List;
24 import java.util.Optional;
25 import java.util.concurrent.ExecutionException;
26 import org.custommonkey.xmlunit.Diff;
27 import org.custommonkey.xmlunit.XMLUnit;
28 import org.junit.Before;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.mockito.ArgumentMatcher;
32 import org.mockito.Mock;
33 import org.mockito.junit.MockitoJUnitRunner;
34 import org.opendaylight.netconf.api.EffectiveOperation;
35 import org.opendaylight.netconf.api.NetconfMessage;
36 import org.opendaylight.netconf.api.xml.XmlUtil;
37 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator;
38 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceId;
39 import org.opendaylight.netconf.sal.connect.netconf.AbstractTestModelTest;
40 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
41 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
42 import org.opendaylight.netconf.util.NetconfUtil;
43 import org.opendaylight.yangtools.rfc8528.data.util.EmptyMountPointContext;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.common.QNameModule;
46 import org.opendaylight.yangtools.yang.common.Revision;
47 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
48 import org.opendaylight.yangtools.yang.common.XMLNamespace;
49 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
50 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
51 import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
52 import org.w3c.dom.Document;
53 import org.w3c.dom.Element;
54 import org.w3c.dom.NamedNodeMap;
55 import org.xml.sax.SAXException;
56
57 @RunWith(MockitoJUnitRunner.StrictStubs.class)
58 public class NetconfBaseOpsTest extends AbstractTestModelTest {
59     private static final QNameModule TEST_MODULE = QNameModule.create(
60             XMLNamespace.of("test:namespace"), Revision.of("2013-07-22"));
61
62     private static final QName CONTAINER_C_QNAME = QName.create(TEST_MODULE, "c");
63     private static final NodeIdentifier CONTAINER_C_NID = NodeIdentifier.create(CONTAINER_C_QNAME);
64     private static final QName LEAF_A_QNAME = QName.create(TEST_MODULE, "a");
65     private static final NodeIdentifier LEAF_A_NID = NodeIdentifier.create(LEAF_A_QNAME);
66     private static final QName LEAF_B_QNAME = QName.create(TEST_MODULE, "b");
67     private static final NodeIdentifier LEAF_B_NID = NodeIdentifier.create(LEAF_B_QNAME);
68     private static final QName CONTAINER_D_QNAME = QName.create(TEST_MODULE, "d");
69     private static final NodeIdentifier CONTAINER_D_NID = NodeIdentifier.create(CONTAINER_D_QNAME);
70     private static final QName LEAF_X_QNAME = QName.create(TEST_MODULE, "x");
71     private static final NodeIdentifier LEAF_X_NID = NodeIdentifier.create(LEAF_X_QNAME);
72
73     private static final QName CONTAINER_E_QNAME = QName.create(TEST_MODULE, "e");
74     private static final NodeIdentifier CONTAINER_E_NID = NodeIdentifier.create(CONTAINER_E_QNAME);
75     private static final QName LEAF_Z_QNAME = QName.create(TEST_MODULE, "z");
76     private static final NodeIdentifier LEAF_Z_NID = NodeIdentifier.create(LEAF_Z_QNAME);
77
78     static {
79         XMLUnit.setIgnoreWhitespace(true);
80         XMLUnit.setIgnoreComments(true);
81     }
82
83     @Mock
84     private RemoteDeviceCommunicator listener;
85     private NetconfRpcFutureCallback callback;
86     private NetconfBaseOps baseOps;
87
88     @Before
89     public void setUp() throws Exception {
90         final InputStream okStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_ok.xml");
91         final InputStream dataStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
92         final NetconfMessage ok = new NetconfMessage(XmlUtil.readXmlToDocument(okStream));
93         final NetconfMessage data = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
94         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)))
95                 .thenReturn(RpcResultBuilder.success(data).buildFuture());
96         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME)))
97                 .thenReturn(RpcResultBuilder.success(data).buildFuture());
98         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)))
99                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
100         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME)))
101                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
102         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME)))
103                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
104         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME)))
105                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
106         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME)))
107                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
108         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME)))
109                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
110         when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME)))
111                 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
112         final var rpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, listener, new NetconfMessageTransformer(
113             new EmptyMountPointContext(SCHEMA_CONTEXT), true, BASE_SCHEMAS.getBaseSchema()));
114         callback = new NetconfRpcFutureCallback("prefix",
115             new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830)));
116         baseOps = new NetconfBaseOps(rpc, new EmptyMountPointContext(SCHEMA_CONTEXT));
117     }
118
119     @Test
120     public void testLock() throws Exception {
121         baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
122         verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
123     }
124
125     @Test
126     public void testLockCandidate() throws Exception {
127         baseOps.lockCandidate(callback);
128         verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
129     }
130
131     @Test
132     public void testUnlock() throws Exception {
133         baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
134         verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
135     }
136
137     @Test
138     public void testUnlockCandidate() throws Exception {
139         baseOps.unlockCandidate(callback);
140         verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
141     }
142
143     @Test
144     public void testLockRunning() throws Exception {
145         baseOps.lockRunning(callback);
146         verifyMessageSent("lock-running", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
147     }
148
149     @Test
150     public void testUnlockRunning() throws Exception {
151         baseOps.unlockRunning(callback);
152         verifyMessageSent("unlock-running", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
153     }
154
155     @Test
156     public void testDiscardChanges() throws Exception {
157         baseOps.discardChanges(callback);
158         verifyMessageSent("discardChanges", NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME);
159     }
160
161     @Test
162     public void testCommit() throws Exception {
163         baseOps.commit(callback);
164         verifyMessageSent("commit", NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME);
165     }
166
167     @Test
168     public void testValidateCandidate() throws Exception {
169         baseOps.validateCandidate(callback);
170         verifyMessageSent("validate", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
171     }
172
173     @Test
174     public void testValidateRunning() throws Exception {
175         baseOps.validateRunning(callback);
176         verifyMessageSent("validate-running", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
177     }
178
179
180     @Test
181     public void testCopyConfig() throws Exception {
182         baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_NODEID,
183                 NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
184         verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
185     }
186
187     @Test
188     public void testCopyRunningToCandidate() throws Exception {
189         baseOps.copyRunningToCandidate(callback);
190         verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
191     }
192
193     @Test
194     public void testGetConfigRunningData() throws Exception {
195         final var dataOpt = baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.empty())).get();
196         assertTrue(dataOpt.isPresent());
197         assertEquals(NetconfUtil.NETCONF_DATA_QNAME, dataOpt.orElseThrow().getIdentifier().getNodeType());
198     }
199
200     @Test
201     public void testGetData() throws Exception {
202         final var dataOpt = baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty())).get();
203         assertTrue(dataOpt.isPresent());
204         assertEquals(NetconfUtil.NETCONF_DATA_QNAME, dataOpt.orElseThrow().getIdentifier().getNodeType());
205     }
206
207     @Test
208     public void testGetConfigRunning() throws Exception {
209         baseOps.getConfigRunning(callback, Optional.empty());
210         verifyMessageSent("getConfig", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
211     }
212
213     @Test
214     public void testGetConfigCandidate() throws Exception {
215         baseOps.getConfigCandidate(callback, Optional.empty());
216         verifyMessageSent("getConfig_candidate", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
217     }
218
219     @Test
220     public void testGetConfigCandidateWithFilter() throws Exception {
221         baseOps.getConfigCandidate(callback, Optional.of(YangInstanceIdentifier.of(CONTAINER_C_QNAME)));
222         verifyMessageSent("getConfig_candidate-filter", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
223     }
224
225     @Test
226     public void testGet() throws Exception {
227         baseOps.get(callback, Optional.empty());
228         verifyMessageSent("get", NetconfMessageTransformUtil.NETCONF_GET_QNAME);
229     }
230
231     @Test
232     public void testEditConfigCandidate() throws Exception {
233         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(
234             Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
235             Optional.of(EffectiveOperation.REPLACE), YangInstanceIdentifier.builder()
236             .node(CONTAINER_C_QNAME)
237             .node(LEAF_A_NID)
238             .build()), true);
239         verifyMessageSent("edit-config-test-module", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
240     }
241
242     @Test
243     public void testDeleteContainerNodeCandidate() throws Exception {
244         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
245             Optional.of(EffectiveOperation.DELETE), YangInstanceIdentifier.of(CONTAINER_C_QNAME)), true);
246         verifyMessageSent("edit-config-delete-container-node-candidate",
247                 NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
248     }
249
250     @Test
251     public void testDeleteLeafNodeCandidate() throws Exception {
252         baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
253             Optional.of(EffectiveOperation.DELETE),
254             YangInstanceIdentifier.builder().node(CONTAINER_C_QNAME).node(LEAF_A_NID).build()), true);
255         verifyMessageSent("edit-config-delete-leaf-node-candidate",
256                 NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
257     }
258
259     @Test
260     public void testEditConfigRunning() throws Exception {
261         baseOps.editConfigRunning(callback, baseOps.createEditConfigStructure(
262             Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
263             Optional.of(EffectiveOperation.REPLACE),
264             YangInstanceIdentifier.builder().node(CONTAINER_C_NID).node(LEAF_A_NID).build()),
265             EffectiveOperation.MERGE, true);
266         verifyMessageSent("edit-config-test-module-running", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
267     }
268
269     @Test
270     public void testGetWithFields() throws ExecutionException, InterruptedException {
271         final YangInstanceIdentifier path = YangInstanceIdentifier.create(CONTAINER_C_NID);
272         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(LEAF_A_NID);
273         final YangInstanceIdentifier leafBField = YangInstanceIdentifier.create(LEAF_B_NID);
274
275         baseOps.getData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
276         verify(listener).sendRequest(msg("/netconfMessages/get-fields-request.xml"),
277                 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
278     }
279
280     @Test
281     public void testGetConfigWithFields() throws ExecutionException, InterruptedException {
282         final YangInstanceIdentifier path = YangInstanceIdentifier.create(CONTAINER_C_NID);
283         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(LEAF_A_NID);
284         final YangInstanceIdentifier leafBField = YangInstanceIdentifier.create(LEAF_B_NID);
285
286         baseOps.getConfigRunningData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
287         verify(listener).sendRequest(msg("/netconfMessages/get-config-fields-request.xml"),
288                 eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME));
289     }
290
291     @Test
292     public void testGetDataWithoutFields() {
293         assertThrows(ExecutionException.class, () -> baseOps.getData(callback,
294                 Optional.of(YangInstanceIdentifier.empty()), List.of()).get());
295     }
296
297     @Test
298     public void getConfigRunningDataWithoutFields() {
299         assertThrows(ExecutionException.class, () -> baseOps.getConfigRunningData(callback,
300                 Optional.of(YangInstanceIdentifier.empty()), List.of()).get());
301     }
302
303     @Test
304     public void testGetWithFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
305         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(CONTAINER_C_NID, LEAF_A_NID);
306         final YangInstanceIdentifier leafXField = YangInstanceIdentifier.create(
307                 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
308         final YangInstanceIdentifier leafZField = YangInstanceIdentifier.create(CONTAINER_E_NID, LEAF_Z_NID);
309
310         baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty()),
311                 List.of(leafAField, leafXField, leafZField)).get();
312         verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-subtrees.xml"),
313                 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
314     }
315
316     @Test
317     public void testGetConfigWithFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
318         final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(CONTAINER_C_NID, LEAF_A_NID);
319         final YangInstanceIdentifier leafXField = YangInstanceIdentifier.create(
320                 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
321         final YangInstanceIdentifier leafZField = YangInstanceIdentifier.create(CONTAINER_E_NID, LEAF_Z_NID);
322
323         baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.empty()),
324                 List.of(leafAField, leafXField, leafZField)).get();
325         verify(listener).sendRequest(msg("/netconfMessages/get-config-with-multiple-subtrees.xml"),
326                 eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME));
327     }
328
329     @Test
330     public void testGetWithRootFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
331         final YangInstanceIdentifier contCField = YangInstanceIdentifier.create(CONTAINER_C_NID);
332         final YangInstanceIdentifier contDField = YangInstanceIdentifier.create(CONTAINER_E_NID);
333
334         baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty()), List.of(contCField, contDField)).get();
335         verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-root-subtrees.xml"),
336                 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
337     }
338
339     private void verifyMessageSent(final String fileName, final QName name) {
340         final String path = "/netconfMessages/" + fileName + ".xml";
341         verify(listener).sendRequest(msg(path), eq(name));
342     }
343
344     private static NetconfMessage msg(final String name) {
345         final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
346         try {
347             return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
348         } catch (SAXException | IOException e) {
349             throw new IllegalStateException("Failed to read xml file " + name, e);
350         }
351     }
352
353     private static class NetconfMessageMatcher implements ArgumentMatcher<NetconfMessage> {
354
355         private final Document expected;
356
357         NetconfMessageMatcher(final Document expected) {
358             this.expected = removeAttrs(expected);
359         }
360
361         @Override
362         public boolean matches(final NetconfMessage message) {
363             final Document actualDoc = removeAttrs(message.getDocument());
364             actualDoc.normalizeDocument();
365             expected.normalizeDocument();
366             final Diff diff = XMLUnit.compareXML(expected, actualDoc);
367             return diff.similar();
368         }
369
370         private static Document removeAttrs(final Document input) {
371             final Document copy = XmlUtil.newDocument();
372             copy.appendChild(copy.importNode(input.getDocumentElement(), true));
373             final Element element = copy.getDocumentElement();
374             final List<String> attrNames = new ArrayList<>();
375             final NamedNodeMap attributes = element.getAttributes();
376             for (int i = 0; i < attributes.getLength(); i++) {
377                 final String nodeName = attributes.item(i).getNodeName();
378                 if ("xmlns".equals(nodeName)) {
379                     continue;
380                 }
381                 attrNames.add(nodeName);
382             }
383             attrNames.forEach(element::removeAttribute);
384             return copy;
385         }
386     }
387
388 }