Refresh IETF client/server models
[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.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;
52
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");
60
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>
66         </rpc>""";
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.
71             </status>
72         </rpc-reply>""";
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">
77                     <name>apache-1</name>
78                     <reset>
79                         <reset-at>2014-07-29T13:42:00Z</reset-at>
80                     </reset>
81                 </server>
82             </action>
83         </rpc>""";
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">
87                 2014-07-29T13:42:12Z
88             </reset-finished-at>
89         </rpc-reply>""";
90     private static final String GET_SCHEMAS_REQUEST = """
91         <rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="m-0">
92             <get>
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">
95                         <schemas/>
96                     </netconf-state>
97                 </filter>
98             </get>
99         </rpc>""";
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"
103     );
104
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;
111
112     @BeforeAll
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);
120     }
121
122     @AfterAll
123     public static void afterAll() throws Exception {
124         stopSimulator(tcpDeviceSimulator);
125         tcpDeviceSimulator = null;
126         stopSimulator(sshDeviceSimulator);
127         sshDeviceSimulator = null;
128         clientFactory.close();
129         timer.close();
130     }
131
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();
138     }
139
140     private static Stream<Arguments> customRpcArgs() {
141         return Stream.of(
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)
147         );
148     }
149
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());
160     }
161
162     private static List<NetconfClientProtocol> getSchemasArgs() {
163         return List.of(SSH, TCP);
164     }
165
166     private static Document sendRequest(final NetconfClientProtocol protocol, final String xml)
167         throws Exception {
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);
176         }
177         assertNotNull(response);
178         return response.getDocument();
179     }
180
181     /**
182      * Runs a simulator.
183      *
184      * @param simulator simulator instance
185      * @return The TCP port number to access the launched simulator.
186      */
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);
191         }
192         throw new IllegalStateException("Could not start device simulator");
193     }
194
195     private static void stopSimulator(final NetconfDeviceSimulator simulator) {
196         if (simulator != null) {
197             simulator.close();
198         }
199     }
200
201     @SuppressWarnings("deprecation")
202     private static Configuration getSimulatorConfig(final NetconfClientProtocol protocol) {
203         return new ConfigurationBuilder()
204             .setStartingPort(RANDOM_PORT)
205             .setDeviceCount(1)
206             .setRpcConfigFile(CUSTOM_RPC_CONFIG)
207             .setSsh(SSH == protocol)
208             .setAuthProvider(AUTH_PROVIDER)
209             .build();
210     }
211
212     private static NetconfClientConfiguration getClientConfig(final int port,
213         final NetconfClientProtocol protocol, final NetconfClientSessionListener sessionListener) {
214         return NetconfClientConfigurationBuilder.create()
215             .withTcpParameters(
216                 new TcpClientParametersBuilder()
217                     .setRemoteAddress(new Host(IetfInetUtil.ipAddressFor(InetAddress.getLoopbackAddress())))
218                     .setRemotePort(new PortNumber(Uint16.valueOf(port)))
219                     .build())
220             .withSshParameters(
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)
229             .build();
230     }
231 }