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.sal.connect.netconf.util;
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.Collections;
24 import java.util.List;
25 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.mdsal.dom.api.DOMRpcService;
36 import org.opendaylight.netconf.api.ModifyAction;
37 import org.opendaylight.netconf.api.NetconfMessage;
38 import org.opendaylight.netconf.api.xml.XmlUtil;
39 import org.opendaylight.netconf.sal.connect.api.MessageTransformer;
40 import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator;
41 import org.opendaylight.netconf.sal.connect.netconf.AbstractTestModelTest;
42 import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc;
43 import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer;
44 import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId;
45 import org.opendaylight.netconf.util.NetconfUtil;
46 import org.opendaylight.yangtools.rfc8528.data.util.EmptyMountPointContext;
47 import org.opendaylight.yangtools.yang.common.QName;
48 import org.opendaylight.yangtools.yang.common.QNameModule;
49 import org.opendaylight.yangtools.yang.common.Revision;
50 import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
51 import org.opendaylight.yangtools.yang.common.XMLNamespace;
52 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
53 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
54 import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
55 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
56 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
57 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
58 import org.w3c.dom.Document;
59 import org.w3c.dom.Element;
60 import org.w3c.dom.NamedNodeMap;
61 import org.xml.sax.SAXException;
63 @RunWith(MockitoJUnitRunner.StrictStubs.class)
64 public class NetconfBaseOpsTest extends AbstractTestModelTest {
65 private static final QNameModule TEST_MODULE = QNameModule.create(
66 XMLNamespace.of("test:namespace"), Revision.of("2013-07-22"));
68 private static final QName CONTAINER_C_QNAME = QName.create(TEST_MODULE, "c");
69 private static final NodeIdentifier CONTAINER_C_NID = NodeIdentifier.create(CONTAINER_C_QNAME);
70 private static final QName LEAF_A_QNAME = QName.create(TEST_MODULE, "a");
71 private static final NodeIdentifier LEAF_A_NID = NodeIdentifier.create(LEAF_A_QNAME);
72 private static final QName LEAF_B_QNAME = QName.create(TEST_MODULE, "b");
73 private static final NodeIdentifier LEAF_B_NID = NodeIdentifier.create(LEAF_B_QNAME);
74 private static final QName CONTAINER_D_QNAME = QName.create(TEST_MODULE, "d");
75 private static final NodeIdentifier CONTAINER_D_NID = NodeIdentifier.create(CONTAINER_D_QNAME);
76 private static final QName LEAF_X_QNAME = QName.create(TEST_MODULE, "x");
77 private static final NodeIdentifier LEAF_X_NID = NodeIdentifier.create(LEAF_X_QNAME);
79 private static final QName CONTAINER_E_QNAME = QName.create(TEST_MODULE, "e");
80 private static final NodeIdentifier CONTAINER_E_NID = NodeIdentifier.create(CONTAINER_E_QNAME);
81 private static final QName LEAF_Z_QNAME = QName.create(TEST_MODULE, "z");
82 private static final NodeIdentifier LEAF_Z_NID = NodeIdentifier.create(LEAF_Z_QNAME);
85 XMLUnit.setIgnoreWhitespace(true);
86 XMLUnit.setIgnoreComments(true);
90 private RemoteDeviceCommunicator listener;
91 private NetconfRpcFutureCallback callback;
92 private NetconfBaseOps baseOps;
95 public void setUp() throws Exception {
96 final InputStream okStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_ok.xml");
97 final InputStream dataStream = getClass().getResourceAsStream("/netconfMessages/rpc-reply_get.xml");
98 final NetconfMessage ok = new NetconfMessage(XmlUtil.readXmlToDocument(okStream));
99 final NetconfMessage data = new NetconfMessage(XmlUtil.readXmlToDocument(dataStream));
100 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME)))
101 .thenReturn(RpcResultBuilder.success(data).buildFuture());
102 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME)))
103 .thenReturn(RpcResultBuilder.success(data).buildFuture());
104 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME)))
105 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
106 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME)))
107 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
108 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME)))
109 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
110 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME)))
111 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
112 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_LOCK_QNAME)))
113 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
114 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME)))
115 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
116 when(listener.sendRequest(any(), eq(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME)))
117 .thenReturn(RpcResultBuilder.success(ok).buildFuture());
118 final MessageTransformer transformer = new NetconfMessageTransformer(new EmptyMountPointContext(SCHEMA_CONTEXT),
119 true, BASE_SCHEMAS.getBaseSchema());
120 final DOMRpcService rpc = new NetconfDeviceRpc(SCHEMA_CONTEXT, listener, transformer);
121 final RemoteDeviceId id =
122 new RemoteDeviceId("device-1", InetSocketAddress.createUnresolved("localhost", 17830));
123 callback = new NetconfRpcFutureCallback("prefix", id);
124 baseOps = new NetconfBaseOps(rpc, new EmptyMountPointContext(SCHEMA_CONTEXT));
128 public void testLock() throws Exception {
129 baseOps.lock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
130 verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
134 public void testLockCandidate() throws Exception {
135 baseOps.lockCandidate(callback);
136 verifyMessageSent("lock", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
140 public void testUnlock() throws Exception {
141 baseOps.unlock(callback, NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
142 verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
146 public void testUnlockCandidate() throws Exception {
147 baseOps.unlockCandidate(callback);
148 verifyMessageSent("unlock", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
152 public void testLockRunning() throws Exception {
153 baseOps.lockRunning(callback);
154 verifyMessageSent("lock-running", NetconfMessageTransformUtil.NETCONF_LOCK_QNAME);
158 public void testUnlockRunning() throws Exception {
159 baseOps.unlockRunning(callback);
160 verifyMessageSent("unlock-running", NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME);
164 public void testDiscardChanges() throws Exception {
165 baseOps.discardChanges(callback);
166 verifyMessageSent("discardChanges", NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME);
170 public void testCommit() throws Exception {
171 baseOps.commit(callback);
172 verifyMessageSent("commit", NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME);
176 public void testValidateCandidate() throws Exception {
177 baseOps.validateCandidate(callback);
178 verifyMessageSent("validate", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
182 public void testValidateRunning() throws Exception {
183 baseOps.validateRunning(callback);
184 verifyMessageSent("validate-running", NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME);
189 public void testCopyConfig() throws Exception {
190 baseOps.copyConfig(callback, NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME,
191 NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME);
192 verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
196 public void testCopyRunningToCandidate() throws Exception {
197 baseOps.copyRunningToCandidate(callback);
198 verifyMessageSent("copy-config", NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME);
202 public void testGetConfigRunningData() throws Exception {
203 final Optional<NormalizedNode> dataOpt =
204 baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.empty())).get();
205 assertTrue(dataOpt.isPresent());
206 assertEquals(NetconfUtil.NETCONF_DATA_QNAME, dataOpt.get().getIdentifier().getNodeType());
210 public void testGetData() throws Exception {
211 final Optional<NormalizedNode> dataOpt =
212 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty())).get();
213 assertTrue(dataOpt.isPresent());
214 assertEquals(NetconfUtil.NETCONF_DATA_QNAME, dataOpt.get().getIdentifier().getNodeType());
218 public void testGetConfigRunning() throws Exception {
219 baseOps.getConfigRunning(callback, Optional.empty());
220 verifyMessageSent("getConfig", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
224 public void testGetConfigCandidate() throws Exception {
225 baseOps.getConfigCandidate(callback, Optional.empty());
226 verifyMessageSent("getConfig_candidate", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
230 public void testGetConfigCandidateWithFilter() throws Exception {
231 final YangInstanceIdentifier id = YangInstanceIdentifier.builder()
232 .node(CONTAINER_C_QNAME)
234 baseOps.getConfigCandidate(callback, Optional.of(id));
235 verifyMessageSent("getConfig_candidate-filter", NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME);
239 public void testGet() throws Exception {
240 baseOps.get(callback, Optional.empty());
241 verifyMessageSent("get", NetconfMessageTransformUtil.NETCONF_GET_QNAME);
245 public void testEditConfigCandidate() throws Exception {
246 final LeafNode<Object> leaf = Builders.leafBuilder()
247 .withNodeIdentifier(LEAF_A_NID)
248 .withValue("leaf-value")
250 final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
251 .node(CONTAINER_C_QNAME)
254 final DataContainerChild structure = baseOps.createEditConfigStructure(Optional.of(leaf),
255 Optional.of(ModifyAction.REPLACE), leafId);
256 baseOps.editConfigCandidate(callback, structure, true);
257 verifyMessageSent("edit-config-test-module", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
261 public void testDeleteContainerNodeCandidate() throws Exception {
262 final YangInstanceIdentifier containerId = YangInstanceIdentifier.builder()
263 .node(CONTAINER_C_QNAME)
265 final DataContainerChild structure = baseOps.createEditConfigStructure(Optional.empty(),
266 Optional.of(ModifyAction.DELETE), containerId);
267 baseOps.editConfigCandidate(callback, structure, true);
268 verifyMessageSent("edit-config-delete-container-node-candidate",
269 NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
273 public void testDeleteLeafNodeCandidate() throws Exception {
274 final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
275 .node(CONTAINER_C_QNAME)
278 final DataContainerChild structure = baseOps.createEditConfigStructure(Optional.empty(),
279 Optional.of(ModifyAction.DELETE), leafId);
280 baseOps.editConfigCandidate(callback, structure, true);
281 verifyMessageSent("edit-config-delete-leaf-node-candidate",
282 NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
286 public void testEditConfigRunning() throws Exception {
287 final LeafNode<Object> leaf = Builders.leafBuilder()
288 .withNodeIdentifier(LEAF_A_NID)
289 .withValue("leaf-value")
291 final YangInstanceIdentifier leafId = YangInstanceIdentifier.builder()
292 .node(CONTAINER_C_NID)
295 final DataContainerChild structure = baseOps.createEditConfigStructure(Optional.of(leaf),
296 Optional.of(ModifyAction.REPLACE), leafId);
297 baseOps.editConfigRunning(callback, structure, ModifyAction.MERGE, true);
298 verifyMessageSent("edit-config-test-module-running", NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME);
302 public void testGetWithFields() throws ExecutionException, InterruptedException {
303 final YangInstanceIdentifier path = YangInstanceIdentifier.create(CONTAINER_C_NID);
304 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(LEAF_A_NID);
305 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.create(LEAF_B_NID);
307 baseOps.getData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
308 verify(listener).sendRequest(msg("/netconfMessages/get-fields-request.xml"),
309 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
313 public void testGetConfigWithFields() throws ExecutionException, InterruptedException {
314 final YangInstanceIdentifier path = YangInstanceIdentifier.create(CONTAINER_C_NID);
315 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(LEAF_A_NID);
316 final YangInstanceIdentifier leafBField = YangInstanceIdentifier.create(LEAF_B_NID);
318 baseOps.getConfigRunningData(callback, Optional.of(path), List.of(leafAField, leafBField)).get();
319 verify(listener).sendRequest(msg("/netconfMessages/get-config-fields-request.xml"),
320 eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME));
324 public void testGetDataWithoutFields() {
325 assertThrows(ExecutionException.class, () -> baseOps.getData(callback,
326 Optional.of(YangInstanceIdentifier.empty()), Collections.emptyList()).get());
330 public void getConfigRunningDataWithoutFields() {
331 assertThrows(ExecutionException.class, () -> baseOps.getConfigRunningData(callback,
332 Optional.of(YangInstanceIdentifier.empty()), Collections.emptyList()).get());
336 public void testGetWithFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
337 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(CONTAINER_C_NID, LEAF_A_NID);
338 final YangInstanceIdentifier leafXField = YangInstanceIdentifier.create(
339 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
340 final YangInstanceIdentifier leafZField = YangInstanceIdentifier.create(CONTAINER_E_NID, LEAF_Z_NID);
342 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty()),
343 List.of(leafAField, leafXField, leafZField)).get();
344 verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-subtrees.xml"),
345 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
349 public void testGetConfigWithFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
350 final YangInstanceIdentifier leafAField = YangInstanceIdentifier.create(CONTAINER_C_NID, LEAF_A_NID);
351 final YangInstanceIdentifier leafXField = YangInstanceIdentifier.create(
352 CONTAINER_C_NID, CONTAINER_D_NID, LEAF_X_NID);
353 final YangInstanceIdentifier leafZField = YangInstanceIdentifier.create(CONTAINER_E_NID, LEAF_Z_NID);
355 baseOps.getConfigRunningData(callback, Optional.of(YangInstanceIdentifier.empty()),
356 List.of(leafAField, leafXField, leafZField)).get();
357 verify(listener).sendRequest(msg("/netconfMessages/get-config-with-multiple-subtrees.xml"),
358 eq(NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME));
362 public void testGetWithRootFieldsAndEmptyParentPath() throws ExecutionException, InterruptedException {
363 final YangInstanceIdentifier contCField = YangInstanceIdentifier.create(CONTAINER_C_NID);
364 final YangInstanceIdentifier contDField = YangInstanceIdentifier.create(CONTAINER_E_NID);
366 baseOps.getData(callback, Optional.of(YangInstanceIdentifier.empty()), List.of(contCField, contDField)).get();
367 verify(listener).sendRequest(msg("/netconfMessages/get-with-multiple-root-subtrees.xml"),
368 eq(NetconfMessageTransformUtil.NETCONF_GET_QNAME));
371 private void verifyMessageSent(final String fileName, final QName name) {
372 final String path = "/netconfMessages/" + fileName + ".xml";
373 verify(listener).sendRequest(msg(path), eq(name));
376 private static NetconfMessage msg(final String name) {
377 final InputStream stream = NetconfBaseOpsTest.class.getResourceAsStream(name);
379 return argThat(new NetconfMessageMatcher(XmlUtil.readXmlToDocument(stream)));
380 } catch (SAXException | IOException e) {
381 throw new IllegalStateException("Failed to read xml file " + name, e);
385 private static class NetconfMessageMatcher implements ArgumentMatcher<NetconfMessage> {
387 private final Document expected;
389 NetconfMessageMatcher(final Document expected) {
390 this.expected = removeAttrs(expected);
394 public boolean matches(final NetconfMessage message) {
395 final Document actualDoc = removeAttrs(message.getDocument());
396 actualDoc.normalizeDocument();
397 expected.normalizeDocument();
398 final Diff diff = XMLUnit.compareXML(expected, actualDoc);
399 return diff.similar();
402 private static Document removeAttrs(final Document input) {
403 final Document copy = XmlUtil.newDocument();
404 copy.appendChild(copy.importNode(input.getDocumentElement(), true));
405 final Element element = copy.getDocumentElement();
406 final List<String> attrNames = new ArrayList<>();
407 final NamedNodeMap attributes = element.getAttributes();
408 for (int i = 0; i < attributes.getLength(); i++) {
409 final String nodeName = attributes.item(i).getNodeName();
410 if ("xmlns".equals(nodeName)) {
413 attrNames.add(nodeName);
415 attrNames.forEach(element::removeAttribute);