Update netconf device simulator to use new transport
[netconf.git] / netconf / tools / netconf-testtool / src / test / java / org / opendaylight / netconf / test / tool / TestToolTest.java
1 /*
2  * Copyright (C) 2019 Ericsson Software Technology AB. All rights reserved.
3  *
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
7  */
8 package org.opendaylight.netconf.test.tool;
9
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;
15
16 import com.google.common.collect.ImmutableMap;
17 import java.io.File;
18 import java.net.InetAddress;
19 import java.util.List;
20 import java.util.Map;
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.rev230417.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.rev230417.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.rev230417.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.rev230417.ssh.client.grouping.ClientIdentityBuilder;
48 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.client.rev230417.ssh.client.grouping.client.identity.PasswordBuilder;
49 import org.opendaylight.yangtools.yang.common.Uint16;
50 import org.w3c.dom.Document;
51
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");
59
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>
65         </rpc>""";
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.
70             </status>
71         </rpc-reply>""";
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">
76                     <name>apache-1</name>
77                     <reset>
78                         <reset-at>2014-07-29T13:42:00Z</reset-at>
79                     </reset>
80                 </server>
81             </action>
82         </rpc>""";
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">
86                 2014-07-29T13:42:12Z
87             </reset-finished-at>
88         </rpc-reply>""";
89     private static final String GET_SCHEMAS_REQUEST = """
90         <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-0">
91             <get>
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">
94                         <schemas/>
95                     </netconf-state>
96                 </filter>
97             </get>
98         </rpc>""";
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"
102     );
103
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;
109
110     @BeforeAll
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);
117     }
118
119     @AfterAll
120     public static void afterAll() throws Exception {
121         stopSimulator(tcpDeviceSimulator);
122         tcpDeviceSimulator = null;
123         stopSimulator(sshDeviceSimulator);
124         sshDeviceSimulator = null;
125         clientFactory.close();
126     }
127
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();
134     }
135
136     private static Stream<Arguments> customRpcArgs() {
137         return Stream.of(
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)
143         );
144     }
145
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());
156     }
157
158     private static List<NetconfClientProtocol> getSchemasArgs() {
159         return List.of(SSH, TCP);
160     }
161
162     private static Document sendRequest(final NetconfClientProtocol protocol, final String xml)
163         throws Exception {
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);
172         }
173         assertNotNull(response);
174         return response.getDocument();
175     }
176
177     /**
178      * Runs a simulator.
179      *
180      * @param simulator simulator instance
181      * @return The TCP port number to access the launched simulator.
182      */
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);
187         }
188         throw new IllegalStateException("Could not start device simulator");
189     }
190
191     private static void stopSimulator(final NetconfDeviceSimulator simulator) {
192         if (simulator != null) {
193             simulator.close();
194         }
195     }
196
197     @SuppressWarnings("deprecation")
198     private static Configuration getSimulatorConfig(final NetconfClientProtocol protocol) {
199         return new ConfigurationBuilder()
200             .setStartingPort(RANDOM_PORT)
201             .setDeviceCount(1)
202             .setRpcConfigFile(CUSTOM_RPC_CONFIG)
203             .setSsh(SSH == protocol)
204             .setAuthProvider(AUTH_PROVIDER)
205             .build();
206     }
207
208     private static NetconfClientConfiguration getClientConfig(final int port,
209         final NetconfClientProtocol protocol, final NetconfClientSessionListener sessionListener) {
210         return NetconfClientConfigurationBuilder.create()
211             .withTcpParameters(
212                 new TcpClientParametersBuilder()
213                     .setRemoteAddress(new Host(IetfInetUtil.ipAddressFor(InetAddress.getLoopbackAddress())))
214                     .setRemotePort(new PortNumber(Uint16.valueOf(port)))
215                     .build())
216             .withSshParameters(
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)
225             .build();
226     }
227 }