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
9 package org.opendaylight.netconf.test.tool;
11 import static org.junit.Assert.assertNotNull;
12 import static org.xmlunit.assertj.XmlAssert.assertThat;
14 import com.google.common.collect.ImmutableMap;
15 import io.netty.channel.nio.NioEventLoopGroup;
16 import io.netty.util.HashedWheelTimer;
17 import io.netty.util.concurrent.DefaultThreadFactory;
18 import io.netty.util.concurrent.GlobalEventExecutor;
20 import java.net.InetSocketAddress;
23 import java.util.concurrent.ConcurrentHashMap;
24 import java.util.concurrent.TimeUnit;
25 import java.util.regex.Pattern;
26 import org.junit.AfterClass;
27 import org.junit.BeforeClass;
28 import org.junit.Rule;
29 import org.junit.Test;
30 import org.opendaylight.netconf.api.NetconfMessage;
31 import org.opendaylight.netconf.api.xml.XmlUtil;
32 import org.opendaylight.netconf.auth.AuthProvider;
33 import org.opendaylight.netconf.client.NetconfClientDispatcher;
34 import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
35 import org.opendaylight.netconf.client.NetconfClientSession;
36 import org.opendaylight.netconf.client.NetconfClientSessionListener;
37 import org.opendaylight.netconf.client.SimpleNetconfClientSessionListener;
38 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
39 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
40 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
41 import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
42 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
43 import org.opendaylight.netconf.test.tool.config.Configuration;
44 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
45 import org.opendaylight.netconf.test.tool.config.YangResource;
46 import org.w3c.dom.Document;
48 @SuppressWarnings("SameParameterValue")
49 public class TestToolTest {
51 private static final long RECEIVE_TIMEOUT_MS = 5_000;
52 private static final int RANDOM_PORT = 0;
54 private static final User ADMIN_USER = new User("admin", "admin");
55 private static final File CUSTOM_RPC_CONFIG = new File("src/test/resources/customrpc.xml");
56 private static final Configuration SSH_SIMULATOR_CONFIG = getSimulatorConfig(NetconfClientProtocol.SSH,
58 private static final Configuration TCP_SIMULATOR_CONFIG = getSimulatorConfig(NetconfClientProtocol.SSH,
61 private static NioEventLoopGroup nettyGroup;
62 private static NetconfClientDispatcherImpl dispatcher;
66 public LogPropertyCatcher logPropertyCatcher =
67 new LogPropertyCatcher(Pattern.compile("(start\\(\\) listen on auto-allocated port="
68 + "|Simulated TCP device started on (/0:0:0:0:0:0:0:0|/0.0.0.0):)(\\d+)"));
70 private static final String XML_REQUEST_RFC7950_SECTION_4_2_9 = "<rpc message-id=\"101\"\n"
71 + " xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
72 + " <activate-software-image xmlns=\"http://example.com/system\">\n"
73 + " <image-name>example-fw-2.3</image-name>\n"
74 + " </activate-software-image>\n"
76 private static final String EXPECTED_XML_RESPONSE_RFC7950_SECTION_4_2_9 = "<rpc-reply message-id=\"101\"\n"
77 + " xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
78 + " <status xmlns=\"http://example.com/system\">\n"
79 + " The image example-fw-2.3 is being installed.\n"
82 private static final String XML_REQUEST_RFC7950_SECTION_7_15_3 = "<rpc message-id=\"101\"\n"
83 + " xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
84 + " <action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n"
85 + " <server xmlns=\"urn:example:server-farm\">\n"
86 + " <name>apache-1</name>\n"
88 + " <reset-at>2014-07-29T13:42:00Z</reset-at>\n"
93 private static final String EXPECTED_XML_RESPONSE_RFC7950_SECTION_7_15_3 = "<rpc-reply message-id=\"101\"\n"
94 + " xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
95 + " <reset-finished-at xmlns=\"urn:example:server-farm\">\n"
96 + " 2014-07-29T13:42:12Z\n"
97 + " </reset-finished-at>\n"
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"
105 public static void setUpClass() {
106 HashedWheelTimer hashedWheelTimer = new HashedWheelTimer();
107 nettyGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(NetconfClientDispatcher.class));
108 dispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer);
112 public static void cleanUpClass()
113 throws InterruptedException {
114 nettyGroup.shutdownGracefully().sync();
118 public void customRpcOverSsh()
120 Document docResponse = invokeRpc(SSH_SIMULATOR_CONFIG, XML_REQUEST_RFC7950_SECTION_7_15_3);
121 assertThat(docResponse)
122 .and(EXPECTED_XML_RESPONSE_RFC7950_SECTION_7_15_3)
128 public void customRpcOverTcp()
130 Document docResponse = invokeRpc(TCP_SIMULATOR_CONFIG, XML_REQUEST_RFC7950_SECTION_4_2_9);
131 assertThat(docResponse)
132 .and(EXPECTED_XML_RESPONSE_RFC7950_SECTION_4_2_9)
138 public void shouldSupportGetSchema()
140 String getSchema = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"m-0\">\n"
142 + " <filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
143 + " <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
145 + " </netconf-state>\n"
149 Document docResponse = invokeRpc(TCP_SIMULATOR_CONFIG, getSchema);
150 Set<YangResource> expectedYangResources = Configuration.DEFAULT_YANG_RESOURCES;
151 assert expectedYangResources.size() > 0;
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 Document invokeRpc(Configuration simulatorConfig, String xmlRequest)
161 int localPort = launchSimulator(simulatorConfig);
162 SimpleNetconfClientSessionListener sessionListener = new SimpleNetconfClientSessionListener();
163 NetconfClientConfiguration clientConfig = getClientConfig("localhost", localPort,
164 simulatorConfig, sessionListener);
165 Document docRequest = XmlUtil.readXmlToDocument(xmlRequest);
166 NetconfMessage request = new NetconfMessage(docRequest);
169 NetconfMessage response;
170 try (NetconfClientSession ignored = dispatcher.createClient(clientConfig).get()) {
171 response = sessionListener.sendRequest(request)
172 .get(RECEIVE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
176 assertNotNull(response);
177 return response.getDocument();
180 private static final ConcurrentHashMap<Configuration, Integer> CACHED_SIMULATORS = new ConcurrentHashMap<>();
183 * Retrieves a previously launched simulator or launches a new one using the given configuration.
185 * @param configuration The simulator configuration.
186 * @return The TCP port number to access the launched simulator.
188 private int launchSimulator(Configuration configuration) {
189 return CACHED_SIMULATORS.computeIfAbsent(configuration, cfg -> {
190 NetconfDeviceSimulator simulator = new NetconfDeviceSimulator(cfg);
192 return logPropertyCatcher.getLastValue()
193 .map(Integer::parseInt)
194 .orElseThrow(() -> new IllegalArgumentException("Unable to capture auto-allocated port from log"));
198 @SuppressWarnings("deprecation")
199 private static Configuration getSimulatorConfig(NetconfClientProtocol protocol, User user) {
200 return new ConfigurationBuilder()
201 .setStartingPort(RANDOM_PORT)
202 .setRpcConfigFile(CUSTOM_RPC_CONFIG)
203 .setSsh(protocol == NetconfClientProtocol.SSH)
204 .setAuthProvider(new InMemoryAuthenticationProvider(user))
208 @SuppressWarnings("deprecation")
209 private static NetconfClientConfiguration getClientConfig(String host, int port,
210 Configuration simulatorConfig,
211 NetconfClientSessionListener sessionListener) {
212 User user = ((InMemoryAuthenticationProvider) simulatorConfig.getAuthProvider()).user;
213 return NetconfClientConfigurationBuilder.create()
214 .withAddress(new InetSocketAddress(host, port))
215 .withSessionListener(sessionListener)
216 .withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
217 NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS))
218 .withProtocol(simulatorConfig.isSsh() ? NetconfClientProtocol.SSH : NetconfClientProtocol.TCP)
219 .withAuthHandler(new LoginPasswordHandler(user.username, user.password))
223 private static final class User {
224 private final String username;
225 private final String password;
227 private User(String username, String password) {
228 this.username = username;
229 this.password = password;
233 private static final class InMemoryAuthenticationProvider implements AuthProvider {
235 private final User user;
237 private InMemoryAuthenticationProvider(User user) {
242 public boolean authenticated(String username, String password) {
243 return user.username.equals(username) && user.password.equals(password);