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