2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.client.mdsal.impl;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertThrows;
12 import static org.junit.jupiter.api.Assertions.assertTrue;
13 import static org.mockito.ArgumentMatchers.any;
14 import static org.mockito.ArgumentMatchers.argThat;
15 import static org.mockito.Mockito.verify;
16 import static org.mockito.Mockito.when;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.net.InetSocketAddress;
21 import java.util.ArrayList;
22 import java.util.List;
23 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.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.junit.jupiter.api.extension.ExtendWith;
31 import org.mockito.ArgumentMatcher;
32 import org.mockito.Mock;
33 import org.mockito.junit.jupiter.MockitoExtension;
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.NetconfSessionPreferences;
39 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator;
40 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
41 import org.opendaylight.netconf.client.mdsal.spi.NetconfDeviceRpc;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.output.Data;
43 import org.opendaylight.yangtools.yang.common.QName;
44 import org.opendaylight.yangtools.yang.common.QNameModule;
45 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
46 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
47 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
48 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
49 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
50 import org.w3c.dom.Document;
51 import org.w3c.dom.Element;
52 import org.w3c.dom.NamedNodeMap;
53 import org.xml.sax.SAXException;
55 @ExtendWith(MockitoExtension.class)
56 class NetconfBaseOpsTest extends AbstractTestModelTest {
57 private static final QNameModule TEST_MODULE = QNameModule.ofRevision("test:namespace", "2013-07-22");
59 private static final QName CONTAINER_C_QNAME = QName.create(TEST_MODULE, "c");
60 private static final NodeIdentifier CONTAINER_C_NID = NodeIdentifier.create(CONTAINER_C_QNAME);
61 private static final QName LEAF_A_QNAME = QName.create(TEST_MODULE, "a");
62 private static final NodeIdentifier LEAF_A_NID = NodeIdentifier.create(LEAF_A_QNAME);
63 private static final QName LEAF_B_QNAME = QName.create(TEST_MODULE, "b");
64 private static final NodeIdentifier LEAF_B_NID = NodeIdentifier.create(LEAF_B_QNAME);
65 private static final QName CONTAINER_D_QNAME = QName.create(TEST_MODULE, "d");
66 private static final NodeIdentifier CONTAINER_D_NID = NodeIdentifier.create(CONTAINER_D_QNAME);
67 private static final QName LEAF_X_QNAME = QName.create(TEST_MODULE, "x");
68 private static final NodeIdentifier LEAF_X_NID = NodeIdentifier.create(LEAF_X_QNAME);
70 private static final QName CONTAINER_E_QNAME = QName.create(TEST_MODULE, "e");
71 private static final NodeIdentifier CONTAINER_E_NID = NodeIdentifier.create(CONTAINER_E_QNAME);
72 private static final QName LEAF_Z_QNAME = QName.create(TEST_MODULE, "z");
73 private static final NodeIdentifier LEAF_Z_NID = NodeIdentifier.create(LEAF_Z_QNAME);
74 private static final NetconfMessage NETCONF_DATA_MESSAGE;
77 final var dataStream = NetconfBaseOpsTest.class.getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
79 NETCONF_DATA_MESSAGE = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
80 } catch (SAXException | IOException e) {
81 throw new ExceptionInInitializerError(e);
83 XMLUnit.setIgnoreWhitespace(true);
84 XMLUnit.setIgnoreComments(true);
88 private RemoteDeviceCommunicator listener;
89 private NetconfRpcFutureCallback callback;
90 private NetconfBaseOps baseOps;
94 final var rpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, listener, new NetconfMessageTransformer(
95 MountPointContext.of(SCHEMA_CONTEXT), true,
96 BASE_SCHEMAS.baseSchemaForCapabilities(NetconfSessionPreferences.fromStrings(Set.of()))));
97 callback = new NetconfRpcFutureCallback("prefix",
98 new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830)));
99 baseOps = new NetconfBaseOps(rpc, MountPointContext.of(SCHEMA_CONTEXT));
104 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
105 baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
106 verifyMessageSent("lock");
110 void testLockCandidate() {
111 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
112 baseOps.lockCandidate(callback);
113 verifyMessageSent("lock");
118 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
119 baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
120 verifyMessageSent("unlock");
124 void testUnlockCandidate() {
125 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
126 baseOps.unlockCandidate(callback);
127 verifyMessageSent("unlock");
131 void testLockRunning() {
132 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
133 baseOps.lockRunning(callback);
134 verifyMessageSent("lock-running");
138 void testUnlockRunning() {
139 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
140 baseOps.unlockRunning(callback);
141 verifyMessageSent("unlock-running");
145 void testDiscardChanges() {
146 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
147 baseOps.discardChanges(callback);
148 verifyMessageSent("discardChanges");
153 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
154 baseOps.commit(callback);
155 verifyMessageSent("commit");
159 void testValidateCandidate() {
160 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
161 baseOps.validateCandidate(callback);
162 verifyMessageSent("validate");
166 void testValidateRunning() {
167 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
168 baseOps.validateRunning(callback);
169 verifyMessageSent("validate-running");
174 void testCopyConfig() {
175 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
176 baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_NODEID,
177 NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
178 verifyMessageSent("copy-config");
182 void testCopyRunningToCandidate() {
183 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
184 baseOps.copyRunningToCandidate(callback);
185 verifyMessageSent("copy-config");
189 void testGetConfigRunningData() throws Exception {
190 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
191 final var dataOpt = baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of())).get();
192 assertTrue(dataOpt.isPresent());
193 assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
197 void testGetData() throws Exception {
198 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
199 final var dataOpt = baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of())).get();
200 assertTrue(dataOpt.isPresent());
201 assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
205 void testGetConfigRunning() {
206 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
207 baseOps.getConfigRunning(callback, Optional.empty());
208 verifyMessageSent("getConfig");
212 void testGetConfigCandidate() {
213 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
214 baseOps.getConfigCandidate(callback, Optional.empty());
215 verifyMessageSent("getConfig_candidate");
219 void testGetConfigCandidateWithFilter() {
220 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
221 baseOps.getConfigCandidate(callback, Optional.of(YangInstanceIdentifier.of(CONTAINER_C_QNAME)));
222 verifyMessageSent("getConfig_candidate-filter");
227 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
228 baseOps.get(callback, Optional.empty());
229 verifyMessageSent("get");
233 void testEditConfigCandidate() {
234 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
235 baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(
236 Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
237 Optional.of(EffectiveOperation.REPLACE), YangInstanceIdentifier.builder()
238 .node(CONTAINER_C_QNAME)
241 verifyMessageSent("edit-config-test-module");
245 void testDeleteContainerNodeCandidate() {
246 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
247 baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
248 Optional.of(EffectiveOperation.DELETE), YangInstanceIdentifier.of(CONTAINER_C_QNAME)), true);
249 verifyMessageSent("edit-config-delete-container-node-candidate");
253 void testDeleteLeafNodeCandidate() {
254 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
255 baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
256 Optional.of(EffectiveOperation.DELETE),
257 YangInstanceIdentifier.builder().node(CONTAINER_C_QNAME).node(LEAF_A_NID).build()), true);
258 verifyMessageSent("edit-config-delete-leaf-node-candidate");
262 void testEditConfigRunning() {
263 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
264 baseOps.editConfigRunning(callback, baseOps.createEditConfigStructure(
265 Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
266 Optional.of(EffectiveOperation.REPLACE),
267 YangInstanceIdentifier.builder().node(CONTAINER_C_NID).node(LEAF_A_NID).build()),
268 EffectiveOperation.MERGE, true);
269 verifyMessageSent("edit-config-test-module-running");
273 void testGetWithFields() throws Exception {
274 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
275 final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
276 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
277 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
279 baseOps.getData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
280 verify(listener).sendRequest(msg("/netconfMessages/get-fields-request.xml"));
284 void testGetConfigWithFields() throws Exception {
285 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
286 final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
287 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
288 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
290 baseOps.getConfigRunningData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
291 verify(listener).sendRequest(msg("/netconfMessages/get-config-fields-request.xml"));
295 void testGetDataWithoutFields() {
296 assertThrows(ExecutionException.class, () -> baseOps.getData(callback,
297 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
301 void getConfigRunningDataWithoutFields() {
302 assertThrows(ExecutionException.class, () -> baseOps.getConfigRunningData(callback,
303 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
307 void testGetWithFieldsAndEmptyParentPath() throws Exception {
308 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
309 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(CONTAINER_C_NID, LEAF_A_NID);
310 final YangInstanceIdentifier leafXField = YangInstanceIdentifier.of(
311 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
312 final YangInstanceIdentifier leafZField = YangInstanceIdentifier.of(CONTAINER_E_NID, LEAF_Z_NID);
314 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of()),
315 List.of(leafAField, leafXField, leafZField)).get();
316 verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-subtrees.xml"));
320 void testGetConfigWithFieldsAndEmptyParentPath() throws Exception {
321 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
322 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(CONTAINER_C_NID, LEAF_A_NID);
323 final YangInstanceIdentifier leafXField = YangInstanceIdentifier.of(
324 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
325 final YangInstanceIdentifier leafZField = YangInstanceIdentifier.of(CONTAINER_E_NID, LEAF_Z_NID);
327 baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of()),
328 List.of(leafAField, leafXField, leafZField)).get();
329 verify(listener).sendRequest(msg("/netconfMessages/get-config-with-multiple-subtrees.xml"));
333 void testGetWithRootFieldsAndEmptyParentPath() throws Exception {
334 when(listener.sendRequest(any())).thenReturn(RpcResultBuilder.success(NETCONF_DATA_MESSAGE).buildFuture());
335 final YangInstanceIdentifier contCField = YangInstanceIdentifier.of(CONTAINER_C_NID);
336 final YangInstanceIdentifier contDField = YangInstanceIdentifier.of(CONTAINER_E_NID);
338 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of()), List.of(contCField, contDField)).get();
339 verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-root-subtrees.xml"));
342 private void verifyMessageSent(final String fileName) {
343 final String path = "/netconfMessages/" + fileName + ".xml";
344 verify(listener).sendRequest(msg(path));
347 private static NetconfMessage msg(final String name) {
348 final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
350 return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
351 } catch (SAXException | IOException e) {
352 throw new IllegalStateException("Failed to read xml file " + name, e);
356 private static class NetconfMessageMatcher implements ArgumentMatcher<NetconfMessage> {
358 private final Document expected;
360 NetconfMessageMatcher(final Document expected) {
361 this.expected = removeAttrs(expected);
365 public boolean matches(final NetconfMessage message) {
366 final Document actualDoc = removeAttrs(message.getDocument());
367 actualDoc.normalizeDocument();
368 expected.normalizeDocument();
369 final Diff diff = XMLUnit.compareXML(expected, actualDoc);
370 return diff.similar();
373 private static Document removeAttrs(final Document input) {
374 final Document copy = XmlUtil.newDocument();
375 copy.appendChild(copy.importNode(input.getDocumentElement(), true));
376 final Element element = copy.getDocumentElement();
377 final List<String> attrNames = new ArrayList<>();
378 final NamedNodeMap attributes = element.getAttributes();
379 for (int i = 0; i < attributes.getLength(); i++) {
380 final String nodeName = attributes.item(i).getNodeName();
381 if ("xmlns".equals(nodeName)) {
384 attrNames.add(nodeName);
386 attrNames.forEach(element::removeAttribute);