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.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;
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;
26 import java.util.concurrent.ExecutionException;
27 import org.custommonkey.xmlunit.Diff;
28 import org.custommonkey.xmlunit.XMLUnit;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.junit.runner.RunWith;
32 import org.mockito.ArgumentMatcher;
33 import org.mockito.Mock;
34 import org.mockito.junit.MockitoJUnitRunner;
35 import org.opendaylight.netconf.api.EffectiveOperation;
36 import org.opendaylight.netconf.api.messages.NetconfMessage;
37 import org.opendaylight.netconf.api.xml.XmlUtil;
38 import org.opendaylight.netconf.client.mdsal.AbstractTestModelTest;
39 import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
40 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceCommunicator;
41 import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
42 import org.opendaylight.netconf.client.mdsal.spi.NetconfDeviceRpc;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Commit;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.CopyConfig;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.DiscardChanges;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfig;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Get;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.GetConfig;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Lock;
50 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Unlock;
51 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Validate;
52 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.output.Data;
53 import org.opendaylight.yangtools.yang.common.QName;
54 import org.opendaylight.yangtools.yang.common.QNameModule;
55 import org.opendaylight.yangtools.yang.common.Revision;
56 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
57 import org.opendaylight.yangtools.yang.common.XMLNamespace;
58 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
59 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
60 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
61 import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
62 import org.w3c.dom.Document;
63 import org.w3c.dom.Element;
64 import org.w3c.dom.NamedNodeMap;
65 import org.xml.sax.SAXException;
67 @RunWith(MockitoJUnitRunner.StrictStubs.class)
68 public class NetconfBaseOpsTest extends AbstractTestModelTest {
69 private static final QNameModule TEST_MODULE = QNameModule.create(
70 XMLNamespace.of("test:namespace"), Revision.of("2013-07-22"));
72 private static final QName CONTAINER_C_QNAME = QName.create(TEST_MODULE, "c");
73 private static final NodeIdentifier CONTAINER_C_NID = NodeIdentifier.create(CONTAINER_C_QNAME);
74 private static final QName LEAF_A_QNAME = QName.create(TEST_MODULE, "a");
75 private static final NodeIdentifier LEAF_A_NID = NodeIdentifier.create(LEAF_A_QNAME);
76 private static final QName LEAF_B_QNAME = QName.create(TEST_MODULE, "b");
77 private static final NodeIdentifier LEAF_B_NID = NodeIdentifier.create(LEAF_B_QNAME);
78 private static final QName CONTAINER_D_QNAME = QName.create(TEST_MODULE, "d");
79 private static final NodeIdentifier CONTAINER_D_NID = NodeIdentifier.create(CONTAINER_D_QNAME);
80 private static final QName LEAF_X_QNAME = QName.create(TEST_MODULE, "x");
81 private static final NodeIdentifier LEAF_X_NID = NodeIdentifier.create(LEAF_X_QNAME);
83 private static final QName CONTAINER_E_QNAME = QName.create(TEST_MODULE, "e");
84 private static final NodeIdentifier CONTAINER_E_NID = NodeIdentifier.create(CONTAINER_E_QNAME);
85 private static final QName LEAF_Z_QNAME = QName.create(TEST_MODULE, "z");
86 private static final NodeIdentifier LEAF_Z_NID = NodeIdentifier.create(LEAF_Z_QNAME);
89 XMLUnit.setIgnoreWhitespace(true);
90 XMLUnit.setIgnoreComments(true);
94 private RemoteDeviceCommunicator listener;
95 private NetconfRpcFutureCallback callback;
96 private NetconfBaseOps baseOps;
99 public void setUp() throws Exception {
100 final var okStream = NetconfBaseOpsTest.class.getResourceAsStream("/netconfMessages/rpc-reply_ok.xml");
101 final var dataStream = NetconfBaseOpsTest.class.getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
102 final var ok = new NetconfMessage(XmlUtil.readXmlToDocument(okStream));
103 final var data = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
104 when(listener.sendRequest(any(), eq(GetConfig.QNAME))).thenReturn(RpcResultBuilder.success(data).buildFuture());
105 when(listener.sendRequest(any(), eq(Get.QNAME))).thenReturn(RpcResultBuilder.success(data).buildFuture());
106 when(listener.sendRequest(any(), eq(EditConfig.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
107 when(listener.sendRequest(any(), eq(CopyConfig.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
108 when(listener.sendRequest(any(), eq(DiscardChanges.QNAME)))
109 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
110 when(listener.sendRequest(any(), eq(Validate.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
111 when(listener.sendRequest(any(), eq(Lock.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
112 when(listener.sendRequest(any(), eq(Unlock.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
113 when(listener.sendRequest(any(), eq(Commit.QNAME))).thenReturn(RpcResultBuilder.success(ok).buildFuture());
114 final var rpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, listener, new NetconfMessageTransformer(
115 MountPointContext.of(SCHEMA_CONTEXT), true,
116 BASE_SCHEMAS.baseSchemaForCapabilities(NetconfSessionPreferences.fromStrings(Set.of()))));
117 callback = new NetconfRpcFutureCallback("prefix",
118 new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830)));
119 baseOps = new NetconfBaseOps(rpc, MountPointContext.of(SCHEMA_CONTEXT));
123 public void testLock() throws Exception {
124 baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
125 verifyMessageSent("lock", Lock.QNAME);
129 public void testLockCandidate() throws Exception {
130 baseOps.lockCandidate(callback);
131 verifyMessageSent("lock", Lock.QNAME);
135 public void testUnlock() throws Exception {
136 baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
137 verifyMessageSent("unlock", Unlock.QNAME);
141 public void testUnlockCandidate() throws Exception {
142 baseOps.unlockCandidate(callback);
143 verifyMessageSent("unlock", Unlock.QNAME);
147 public void testLockRunning() throws Exception {
148 baseOps.lockRunning(callback);
149 verifyMessageSent("lock-running", Lock.QNAME);
153 public void testUnlockRunning() throws Exception {
154 baseOps.unlockRunning(callback);
155 verifyMessageSent("unlock-running", Unlock.QNAME);
159 public void testDiscardChanges() throws Exception {
160 baseOps.discardChanges(callback);
161 verifyMessageSent("discardChanges", DiscardChanges.QNAME);
165 public void testCommit() throws Exception {
166 baseOps.commit(callback);
167 verifyMessageSent("commit", Commit.QNAME);
171 public void testValidateCandidate() throws Exception {
172 baseOps.validateCandidate(callback);
173 verifyMessageSent("validate", Validate.QNAME);
177 public void testValidateRunning() throws Exception {
178 baseOps.validateRunning(callback);
179 verifyMessageSent("validate-running", Validate.QNAME);
184 public void testCopyConfig() throws Exception {
185 baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_NODEID,
186 NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID);
187 verifyMessageSent("copy-config", CopyConfig.QNAME);
191 public void testCopyRunningToCandidate() throws Exception {
192 baseOps.copyRunningToCandidate(callback);
193 verifyMessageSent("copy-config", CopyConfig.QNAME);
197 public void testGetConfigRunningData() throws Exception {
198 final var dataOpt = baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of())).get();
199 assertTrue(dataOpt.isPresent());
200 assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
204 public void testGetData() throws Exception {
205 final var dataOpt = baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of())).get();
206 assertTrue(dataOpt.isPresent());
207 assertEquals(Data.QNAME, dataOpt.orElseThrow().name().getNodeType());
211 public void testGetConfigRunning() throws Exception {
212 baseOps.getConfigRunning(callback, Optional.empty());
213 verifyMessageSent("getConfig", GetConfig.QNAME);
217 public void testGetConfigCandidate() throws Exception {
218 baseOps.getConfigCandidate(callback, Optional.empty());
219 verifyMessageSent("getConfig_candidate", GetConfig.QNAME);
223 public void testGetConfigCandidateWithFilter() throws Exception {
224 baseOps.getConfigCandidate(callback, Optional.of(YangInstanceIdentifier.of(CONTAINER_C_QNAME)));
225 verifyMessageSent("getConfig_candidate-filter", GetConfig.QNAME);
229 public void testGet() throws Exception {
230 baseOps.get(callback, Optional.empty());
231 verifyMessageSent("get", Get.QNAME);
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(EffectiveOperation.REPLACE), YangInstanceIdentifier.builder()
239 .node(CONTAINER_C_QNAME)
242 verifyMessageSent("edit-config-test-module", EditConfig.QNAME);
246 public void testDeleteContainerNodeCandidate() throws Exception {
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", EditConfig.QNAME);
253 public void testDeleteLeafNodeCandidate() throws Exception {
254 baseOps.editConfigCandidate(callback, baseOps.createEditConfigStructure(Optional.empty(),
255 Optional.of(EffectiveOperation.DELETE),
256 YangInstanceIdentifier.builder().node(CONTAINER_C_QNAME).node(LEAF_A_NID).build()), true);
257 verifyMessageSent("edit-config-delete-leaf-node-candidate", EditConfig.QNAME);
261 public void testEditConfigRunning() throws Exception {
262 baseOps.editConfigRunning(callback, baseOps.createEditConfigStructure(
263 Optional.of(ImmutableNodes.leafNode(LEAF_A_NID, "leaf-value")),
264 Optional.of(EffectiveOperation.REPLACE),
265 YangInstanceIdentifier.builder().node(CONTAINER_C_NID).node(LEAF_A_NID).build()),
266 EffectiveOperation.MERGE, true);
267 verifyMessageSent("edit-config-test-module-running", EditConfig.QNAME);
271 public void testGetWithFields() throws ExecutionException, InterruptedException {
272 final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
273 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
274 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
276 baseOps.getData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
277 verify(listener).sendRequest(msg("/netconfMessages/get-fields-request.xml"), eq(Get.QNAME));
281 public void testGetConfigWithFields() throws ExecutionException, InterruptedException {
282 final YangInstanceIdentifier path = YangInstanceIdentifier.of(CONTAINER_C_NID);
283 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(LEAF_A_NID);
284 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.of(LEAF_B_NID);
286 baseOps.getConfigRunningData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
287 verify(listener).sendRequest(msg("/netconfMessages/get-config-fields-request.xml"), eq(GetConfig.QNAME));
291 public void testGetDataWithoutFields() {
292 assertThrows(ExecutionException.class, () -> baseOps.getData(callback,
293 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
297 public void getConfigRunningDataWithoutFields() {
298 assertThrows(ExecutionException.class, () -> baseOps.getConfigRunningData(callback,
299 Optional.of(YangInstanceIdentifier.of()), List.of()).get());
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);
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"), eq(Get.QNAME));
315 public void testGetConfigWithFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
316 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.of(CONTAINER_C_NID, LEAF_A_NID);
317 final YangInstanceIdentifier leafXField = YangInstanceIdentifier.of(
318 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
319 final YangInstanceIdentifier leafZField = YangInstanceIdentifier.of(CONTAINER_E_NID, LEAF_Z_NID);
321 baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.of()),
322 List.of(leafAField, leafXField, leafZField)).get();
323 verify(listener).sendRequest(msg("/netconfMessages/get-config-with-multiple-subtrees.xml"),
324 eq(GetConfig.QNAME));
328 public void testGetWithRootFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
329 final YangInstanceIdentifier contCField = YangInstanceIdentifier.of(CONTAINER_C_NID);
330 final YangInstanceIdentifier contDField = YangInstanceIdentifier.of(CONTAINER_E_NID);
332 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.of()), List.of(contCField, contDField)).get();
333 verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-root-subtrees.xml"), eq(Get.QNAME));
336 private void verifyMessageSent(final String fileName, final QName name) {
337 final String path = "/netconfMessages/" + fileName + ".xml";
338 verify(listener).sendRequest(msg(path), eq(name));
341 private static NetconfMessage msg(final String name) {
342 final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
344 return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
345 } catch (SAXException | IOException e) {
346 throw new IllegalStateException("Failed to read xml file " + name, e);
350 private static class NetconfMessageMatcher implements ArgumentMatcher<NetconfMessage> {
352 private final Document expected;
354 NetconfMessageMatcher(final Document expected) {
355 this.expected = removeAttrs(expected);
359 public boolean matches(final NetconfMessage message) {
360 final Document actualDoc = removeAttrs(message.getDocument());
361 actualDoc.normalizeDocument();
362 expected.normalizeDocument();
363 final Diff diff = XMLUnit.compareXML(expected, actualDoc);
364 return diff.similar();
367 private static Document removeAttrs(final Document input) {
368 final Document copy = XmlUtil.newDocument();
369 copy.appendChild(copy.importNode(input.getDocumentElement(), true));
370 final Element element = copy.getDocumentElement();
371 final List<String> attrNames = new ArrayList<>();
372 final NamedNodeMap attributes = element.getAttributes();
373 for (int i = 0; i < attributes.getLength(); i++) {
374 final String nodeName = attributes.item(i).getNodeName();
375 if ("xmlns".equals(nodeName)) {
378 attrNames.add(nodeName);
380 attrNames.forEach(element::removeAttribute);