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