2 * Copyright (C) 2019 Ericsson Software Technology AB. 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.test.tool;
10 import static org.junit.jupiter.api.Assertions.assertEquals;
11 import static org.junit.jupiter.api.Assertions.assertNotNull;
12 import static org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol.SSH;
13 import static org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol.TCP;
14 import static org.xmlunit.assertj.XmlAssert.assertThat;
16 import com.google.common.collect.ImmutableMap;
18 import java.net.InetAddress;
19 import java.util.List;
21 import java.util.concurrent.TimeUnit;
22 import java.util.stream.Stream;
23 import org.junit.jupiter.api.AfterAll;
24 import org.junit.jupiter.api.BeforeAll;
25 import org.junit.jupiter.params.ParameterizedTest;
26 import org.junit.jupiter.params.provider.Arguments;
27 import org.junit.jupiter.params.provider.MethodSource;
28 import org.opendaylight.netconf.api.messages.NetconfMessage;
29 import org.opendaylight.netconf.api.xml.XmlUtil;
30 import org.opendaylight.netconf.auth.AuthProvider;
31 import org.opendaylight.netconf.client.NetconfClientFactory;
32 import org.opendaylight.netconf.client.NetconfClientFactoryImpl;
33 import org.opendaylight.netconf.client.NetconfClientSession;
34 import org.opendaylight.netconf.client.NetconfClientSessionListener;
35 import org.opendaylight.netconf.client.SimpleNetconfClientSessionListener;
36 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
37 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
38 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
39 import org.opendaylight.netconf.test.tool.config.Configuration;
40 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev231228.password.grouping.password.type.CleartextPasswordBuilder;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev231228.netconf.client.initiate.stack.grouping.transport.ssh.ssh.TcpClientParametersBuilder;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev231228.netconf.client.listen.stack.grouping.transport.ssh.ssh.SshClientParametersBuilder;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.ClientIdentityBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev231228.ssh.client.grouping.client.identity.PasswordBuilder;
49 import org.opendaylight.yangtools.yang.common.Uint16;
50 import org.w3c.dom.Document;
52 public class TestToolTest {
53 private static final long RESPONSE_TIMEOUT_MS = 30_000;
54 private static final int RANDOM_PORT = 0;
55 private static final String USERNAME = "username";
56 private static final String PASSWORD = "pa$$W0rd";
57 private static final AuthProvider AUTH_PROVIDER = (user, passw) -> USERNAME.equals(user) && PASSWORD.equals(passw);
58 private static final File CUSTOM_RPC_CONFIG = new File("src/test/resources/customrpc.xml");
60 private static final String RFC7950_4_2_9_REQUEST = """
61 <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
62 <activate-software-image xmlns="http://example.com/system">
63 <image-name>example-fw-2.3</image-name>
64 </activate-software-image>
66 private static final String RFC7950_4_2_9_RESPONSE = """
67 <rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
68 <status xmlns="http://example.com/system">
69 The image example-fw-2.3 is being installed.
72 private static final String RFC7950_7_15_3_REQUEST = """
73 <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
74 <action xmlns="urn:ietf:params:xml:ns:yang:1">
75 <server xmlns="urn:example:server-farm">
78 <reset-at>2014-07-29T13:42:00Z</reset-at>
83 private static final String RFC7950_7_15_3_RESPONSE = """
84 <rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
85 <reset-finished-at xmlns="urn:example:server-farm">
89 private static final String GET_SCHEMAS_REQUEST = """
90 <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-0">
92 <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
93 <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
99 private static final Map<String, String> PREFIX_2_URI = ImmutableMap.of(
100 "base10", "urn:ietf:params:xml:ns:netconf:base:1.0",
101 "ncmon", "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
104 private static NetconfClientFactory clientFactory;
105 private static NetconfDeviceSimulator tcpDeviceSimulator;
106 private static NetconfDeviceSimulator sshDeviceSimulator;
107 private static int tcpDevicePort;
108 private static int sshDevicePort;
111 static void beforeAll() {
112 clientFactory = new NetconfClientFactoryImpl();
113 tcpDeviceSimulator = new NetconfDeviceSimulator(getSimulatorConfig(TCP));
114 tcpDevicePort = startSimulator(tcpDeviceSimulator);
115 sshDeviceSimulator = new NetconfDeviceSimulator(getSimulatorConfig(SSH));
116 sshDevicePort = startSimulator(sshDeviceSimulator);
120 public static void afterAll() throws Exception {
121 stopSimulator(tcpDeviceSimulator);
122 tcpDeviceSimulator = null;
123 stopSimulator(sshDeviceSimulator);
124 sshDeviceSimulator = null;
125 clientFactory.close();
128 @ParameterizedTest(name = "Custom RPC -- RFC7950 {0}")
129 @MethodSource("customRpcArgs")
130 void customRpc(final String ignoredTestDesc, final NetconfClientProtocol protocol, final String requestXml,
131 final String responseXml) throws Exception {
132 final var docResponse = sendRequest(protocol, requestXml);
133 assertThat(docResponse).and(responseXml).ignoreWhitespace().areIdentical();
136 private static Stream<Arguments> customRpcArgs() {
138 // # test descriptor, protocol, request, expected response
139 Arguments.of("#7.15.3 @TCP", TCP, RFC7950_7_15_3_REQUEST, RFC7950_7_15_3_RESPONSE),
140 Arguments.of("#7.15.3 @SSH", SSH, RFC7950_7_15_3_REQUEST, RFC7950_7_15_3_RESPONSE),
141 Arguments.of("#4.2.9 @TCP", TCP, RFC7950_4_2_9_REQUEST, RFC7950_4_2_9_RESPONSE),
142 Arguments.of("#4.2.9 @SSH", SSH, RFC7950_4_2_9_REQUEST, RFC7950_4_2_9_RESPONSE)
146 @ParameterizedTest(name = "Get Schemas @{0}")
147 @MethodSource("getSchemasArgs")
148 void getSchemas(final NetconfClientProtocol protocol) throws Exception {
149 final var docResponse = sendRequest(protocol, GET_SCHEMAS_REQUEST);
150 final var expectedYangResources = Configuration.DEFAULT_YANG_RESOURCES;
151 assertEquals(4, expectedYangResources.size());
152 assertThat(docResponse)
153 .withNamespaceContext(PREFIX_2_URI)
154 .valueByXPath("count(//base10:rpc-reply/base10:data/ncmon:netconf-state/ncmon:schemas/ncmon:schema)")
155 .isEqualTo(expectedYangResources.size());
158 private static List<NetconfClientProtocol> getSchemasArgs() {
159 return List.of(SSH, TCP);
162 private static Document sendRequest(final NetconfClientProtocol protocol, final String xml)
164 final var sessionListener = new SimpleNetconfClientSessionListener();
165 final int port = SSH == protocol ? sshDevicePort : tcpDevicePort;
166 final var clientConfig = getClientConfig(port, protocol, sessionListener);
167 final var request = new NetconfMessage(XmlUtil.readXmlToDocument(xml));
168 NetconfMessage response;
169 try (NetconfClientSession ignored = clientFactory.createClient(clientConfig)
170 .get(RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
171 response = sessionListener.sendRequest(request).get(RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
173 assertNotNull(response);
174 return response.getDocument();
180 * @param simulator simulator instance
181 * @return The TCP port number to access the launched simulator.
183 private static int startSimulator(final NetconfDeviceSimulator simulator) {
184 final var openDevices = simulator.start();
185 if (openDevices != null && !openDevices.isEmpty()) {
186 return openDevices.get(0);
188 throw new IllegalStateException("Could not start device simulator");
191 private static void stopSimulator(final NetconfDeviceSimulator simulator) {
192 if (simulator != null) {
197 @SuppressWarnings("deprecation")
198 private static Configuration getSimulatorConfig(final NetconfClientProtocol protocol) {
199 return new ConfigurationBuilder()
200 .setStartingPort(RANDOM_PORT)
202 .setRpcConfigFile(CUSTOM_RPC_CONFIG)
203 .setSsh(SSH == protocol)
204 .setAuthProvider(AUTH_PROVIDER)
208 private static NetconfClientConfiguration getClientConfig(final int port,
209 final NetconfClientProtocol protocol, final NetconfClientSessionListener sessionListener) {
210 return NetconfClientConfigurationBuilder.create()
212 new TcpClientParametersBuilder()
213 .setRemoteAddress(new Host(IetfInetUtil.ipAddressFor(InetAddress.getLoopbackAddress())))
214 .setRemotePort(new PortNumber(Uint16.valueOf(port)))
217 new SshClientParametersBuilder()
218 .setClientIdentity(new ClientIdentityBuilder()
219 .setUsername(USERNAME)
220 .setPassword(new PasswordBuilder().setPasswordType(
221 new CleartextPasswordBuilder().setCleartextPassword(PASSWORD).build()
222 ).build()).build()).build())
223 .withSessionListener(sessionListener)
224 .withProtocol(protocol)