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.common.impl.DefaultNetconfTimer;
40 import org.opendaylight.netconf.test.tool.config.Configuration;
41 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
42 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.crypto.types.rev240208.password.grouping.password.type.CleartextPasswordBuilder;
43 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Host;
44 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IetfInetUtil;
45 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
46 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev240208.netconf.client.initiate.stack.grouping.transport.ssh.ssh.TcpClientParametersBuilder;
47 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.client.rev240208.netconf.client.listen.stack.grouping.transport.ssh.ssh.SshClientParametersBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev240208.ssh.client.grouping.ClientIdentityBuilder;
49 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev240208.ssh.client.grouping.client.identity.PasswordBuilder;
50 import org.opendaylight.yangtools.yang.common.Uint16;
51 import org.w3c.dom.Document;
53 public class TestToolTest {
54 private static final long RESPONSE_TIMEOUT_MS = 30_000;
55 private static final int RANDOM_PORT = 0;
56 private static final String USERNAME = "username";
57 private static final String PASSWORD = "pa$$W0rd";
58 private static final AuthProvider AUTH_PROVIDER = (user, passw) -> USERNAME.equals(user) && PASSWORD.equals(passw);
59 private static final File CUSTOM_RPC_CONFIG = new File("src/test/resources/customrpc.xml");
61 private static final String RFC7950_4_2_9_REQUEST = """
62 <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
63 <activate-software-image xmlns="http://example.com/system">
64 <image-name>example-fw-2.3</image-name>
65 </activate-software-image>
67 private static final String RFC7950_4_2_9_RESPONSE = """
68 <rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
69 <status xmlns="http://example.com/system">
70 The image example-fw-2.3 is being installed.
73 private static final String RFC7950_7_15_3_REQUEST = """
74 <rpc message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
75 <action xmlns="urn:ietf:params:xml:ns:yang:1">
76 <server xmlns="urn:example:server-farm">
79 <reset-at>2014-07-29T13:42:00Z</reset-at>
84 private static final String RFC7950_7_15_3_RESPONSE = """
85 <rpc-reply message-id="101" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
86 <reset-finished-at xmlns="urn:example:server-farm">
90 private static final String GET_SCHEMAS_REQUEST = """
91 <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-0">
93 <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
94 <netconf-state xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
100 private static final Map<String, String> PREFIX_2_URI = ImmutableMap.of(
101 "base10", "urn:ietf:params:xml:ns:netconf:base:1.0",
102 "ncmon", "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"
105 private static DefaultNetconfTimer timer;
106 private static NetconfClientFactory clientFactory;
107 private static NetconfDeviceSimulator tcpDeviceSimulator;
108 private static NetconfDeviceSimulator sshDeviceSimulator;
109 private static int tcpDevicePort;
110 private static int sshDevicePort;
113 static void beforeAll() {
114 timer = new DefaultNetconfTimer();
115 clientFactory = new NetconfClientFactoryImpl(timer);
116 tcpDeviceSimulator = new NetconfDeviceSimulator(getSimulatorConfig(TCP));
117 tcpDevicePort = startSimulator(tcpDeviceSimulator);
118 sshDeviceSimulator = new NetconfDeviceSimulator(getSimulatorConfig(SSH));
119 sshDevicePort = startSimulator(sshDeviceSimulator);
123 public static void afterAll() throws Exception {
124 stopSimulator(tcpDeviceSimulator);
125 tcpDeviceSimulator = null;
126 stopSimulator(sshDeviceSimulator);
127 sshDeviceSimulator = null;
128 clientFactory.close();
132 @ParameterizedTest(name = "Custom RPC -- RFC7950 {0}")
133 @MethodSource("customRpcArgs")
134 void customRpc(final String ignoredTestDesc, final NetconfClientProtocol protocol, final String requestXml,
135 final String responseXml) throws Exception {
136 final var docResponse = sendRequest(protocol, requestXml);
137 assertThat(docResponse).and(responseXml).ignoreWhitespace().areIdentical();
140 private static Stream<Arguments> customRpcArgs() {
142 // # test descriptor, protocol, request, expected response
143 Arguments.of("#7.15.3 @TCP", TCP, RFC7950_7_15_3_REQUEST, RFC7950_7_15_3_RESPONSE),
144 Arguments.of("#7.15.3 @SSH", SSH, RFC7950_7_15_3_REQUEST, RFC7950_7_15_3_RESPONSE),
145 Arguments.of("#4.2.9 @TCP", TCP, RFC7950_4_2_9_REQUEST, RFC7950_4_2_9_RESPONSE),
146 Arguments.of("#4.2.9 @SSH", SSH, RFC7950_4_2_9_REQUEST, RFC7950_4_2_9_RESPONSE)
150 @ParameterizedTest(name = "Get Schemas @{0}")
151 @MethodSource("getSchemasArgs")
152 void getSchemas(final NetconfClientProtocol protocol) throws Exception {
153 final var docResponse = sendRequest(protocol, GET_SCHEMAS_REQUEST);
154 final var expectedYangResources = Configuration.DEFAULT_YANG_RESOURCES;
155 assertEquals(4, expectedYangResources.size());
156 assertThat(docResponse)
157 .withNamespaceContext(PREFIX_2_URI)
158 .valueByXPath("count(//base10:rpc-reply/base10:data/ncmon:netconf-state/ncmon:schemas/ncmon:schema)")
159 .isEqualTo(expectedYangResources.size());
162 private static List<NetconfClientProtocol> getSchemasArgs() {
163 return List.of(SSH, TCP);
166 private static Document sendRequest(final NetconfClientProtocol protocol, final String xml)
168 final var sessionListener = new SimpleNetconfClientSessionListener();
169 final int port = SSH == protocol ? sshDevicePort : tcpDevicePort;
170 final var clientConfig = getClientConfig(port, protocol, sessionListener);
171 final var request = new NetconfMessage(XmlUtil.readXmlToDocument(xml));
172 NetconfMessage response;
173 try (NetconfClientSession ignored = clientFactory.createClient(clientConfig)
174 .get(RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
175 response = sessionListener.sendRequest(request).get(RESPONSE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
177 assertNotNull(response);
178 return response.getDocument();
184 * @param simulator simulator instance
185 * @return The TCP port number to access the launched simulator.
187 private static int startSimulator(final NetconfDeviceSimulator simulator) {
188 final var openDevices = simulator.start();
189 if (openDevices != null && !openDevices.isEmpty()) {
190 return openDevices.get(0);
192 throw new IllegalStateException("Could not start device simulator");
195 private static void stopSimulator(final NetconfDeviceSimulator simulator) {
196 if (simulator != null) {
201 @SuppressWarnings("deprecation")
202 private static Configuration getSimulatorConfig(final NetconfClientProtocol protocol) {
203 return new ConfigurationBuilder()
204 .setStartingPort(RANDOM_PORT)
206 .setRpcConfigFile(CUSTOM_RPC_CONFIG)
207 .setSsh(SSH == protocol)
208 .setAuthProvider(AUTH_PROVIDER)
212 private static NetconfClientConfiguration getClientConfig(final int port,
213 final NetconfClientProtocol protocol, final NetconfClientSessionListener sessionListener) {
214 return NetconfClientConfigurationBuilder.create()
216 new TcpClientParametersBuilder()
217 .setRemoteAddress(new Host(IetfInetUtil.ipAddressFor(InetAddress.getLoopbackAddress())))
218 .setRemotePort(new PortNumber(Uint16.valueOf(port)))
221 new SshClientParametersBuilder()
222 .setClientIdentity(new ClientIdentityBuilder()
223 .setUsername(USERNAME)
224 .setPassword(new PasswordBuilder().setPasswordType(
225 new CleartextPasswordBuilder().setCleartextPassword(PASSWORD).build()
226 ).build()).build()).build())
227 .withSessionListener(sessionListener)
228 .withProtocol(protocol)