Update MRI projects for Aluminium
[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
9 package org.opendaylight.netconf.test.tool;
10
11 import static org.junit.Assert.assertEquals;
12 import static org.junit.Assert.assertNotNull;
13 import static org.xmlunit.assertj.XmlAssert.assertThat;
14
15 import com.google.common.collect.ImmutableMap;
16 import io.netty.channel.nio.NioEventLoopGroup;
17 import io.netty.util.HashedWheelTimer;
18 import io.netty.util.concurrent.DefaultThreadFactory;
19 import io.netty.util.concurrent.GlobalEventExecutor;
20 import java.io.File;
21 import java.net.InetSocketAddress;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.TimeUnit;
26 import java.util.regex.Pattern;
27 import org.junit.AfterClass;
28 import org.junit.BeforeClass;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.opendaylight.netconf.api.NetconfMessage;
32 import org.opendaylight.netconf.api.xml.XmlUtil;
33 import org.opendaylight.netconf.auth.AuthProvider;
34 import org.opendaylight.netconf.client.NetconfClientDispatcher;
35 import org.opendaylight.netconf.client.NetconfClientDispatcherImpl;
36 import org.opendaylight.netconf.client.NetconfClientSession;
37 import org.opendaylight.netconf.client.NetconfClientSessionListener;
38 import org.opendaylight.netconf.client.SimpleNetconfClientSessionListener;
39 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration;
40 import org.opendaylight.netconf.client.conf.NetconfClientConfiguration.NetconfClientProtocol;
41 import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder;
42 import org.opendaylight.netconf.nettyutil.NeverReconnectStrategy;
43 import org.opendaylight.netconf.nettyutil.handler.ssh.authentication.LoginPasswordHandler;
44 import org.opendaylight.netconf.test.tool.config.Configuration;
45 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
46 import org.opendaylight.netconf.test.tool.config.YangResource;
47 import org.w3c.dom.Document;
48
49 @SuppressWarnings("SameParameterValue")
50 public class TestToolTest {
51
52     private static final long RECEIVE_TIMEOUT_MS = 5_000;
53     private static final int RANDOM_PORT = 0;
54
55     private static final User ADMIN_USER = new User("admin", "admin");
56     private static final File CUSTOM_RPC_CONFIG = new File("src/test/resources/customrpc.xml");
57     private static final Configuration SSH_SIMULATOR_CONFIG = getSimulatorConfig(NetconfClientProtocol.SSH,
58         ADMIN_USER);
59     private static final Configuration TCP_SIMULATOR_CONFIG = getSimulatorConfig(NetconfClientProtocol.SSH,
60         ADMIN_USER);
61
62     private static NioEventLoopGroup nettyGroup;
63     private static NetconfClientDispatcherImpl dispatcher;
64
65
66     @Rule
67     public LogPropertyCatcher logPropertyCatcher =
68         new LogPropertyCatcher(Pattern.compile("(start\\(\\) listen on auto-allocated port="
69             + "|Simulated TCP device started on (/0:0:0:0:0:0:0:0|/0.0.0.0):)(\\d+)"));
70
71     private static final String XML_REQUEST_RFC7950_SECTION_4_2_9 = "<rpc message-id=\"101\"\n"
72         + "          xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
73         + "       <activate-software-image xmlns=\"http://example.com/system\">\n"
74         + "         <image-name>example-fw-2.3</image-name>\n"
75         + "       </activate-software-image>\n"
76         + "     </rpc>";
77     private static final String EXPECTED_XML_RESPONSE_RFC7950_SECTION_4_2_9 = "<rpc-reply message-id=\"101\"\n"
78         + "                xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
79         + "       <status xmlns=\"http://example.com/system\">\n"
80         + "         The image example-fw-2.3 is being installed.\n"
81         + "       </status>\n"
82         + "     </rpc-reply>";
83     private static final String XML_REQUEST_RFC7950_SECTION_7_15_3 = "<rpc message-id=\"101\"\n"
84         + "          xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
85         + "       <action xmlns=\"urn:ietf:params:xml:ns:yang:1\">\n"
86         + "         <server xmlns=\"urn:example:server-farm\">\n"
87         + "           <name>apache-1</name>\n"
88         + "           <reset>\n"
89         + "             <reset-at>2014-07-29T13:42:00Z</reset-at>\n"
90         + "           </reset>\n"
91         + "         </server>\n"
92         + "       </action>\n"
93         + "     </rpc>";
94     private static final String EXPECTED_XML_RESPONSE_RFC7950_SECTION_7_15_3 = "<rpc-reply message-id=\"101\"\n"
95         + "           xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"
96         + "  <reset-finished-at xmlns=\"urn:example:server-farm\">\n"
97         + "    2014-07-29T13:42:12Z\n"
98         + "  </reset-finished-at>\n"
99         + "</rpc-reply>";
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     @BeforeClass
106     public static void setUpClass() {
107         HashedWheelTimer hashedWheelTimer = new HashedWheelTimer();
108         nettyGroup = new NioEventLoopGroup(1, new DefaultThreadFactory(NetconfClientDispatcher.class));
109         dispatcher = new NetconfClientDispatcherImpl(nettyGroup, nettyGroup, hashedWheelTimer);
110     }
111
112     @AfterClass
113     public static void cleanUpClass()
114         throws InterruptedException {
115         nettyGroup.shutdownGracefully().sync();
116     }
117
118     @Test
119     public void customRpcOverSsh()
120         throws Exception {
121         Document docResponse = invokeRpc(SSH_SIMULATOR_CONFIG, XML_REQUEST_RFC7950_SECTION_7_15_3);
122         assertThat(docResponse)
123             .and(EXPECTED_XML_RESPONSE_RFC7950_SECTION_7_15_3)
124             .ignoreWhitespace()
125             .areIdentical();
126     }
127
128     @Test
129     public void customRpcOverTcp()
130         throws Exception {
131         Document docResponse = invokeRpc(TCP_SIMULATOR_CONFIG, XML_REQUEST_RFC7950_SECTION_4_2_9);
132         assertThat(docResponse)
133             .and(EXPECTED_XML_RESPONSE_RFC7950_SECTION_4_2_9)
134             .ignoreWhitespace()
135             .areIdentical();
136     }
137
138     @Test
139     public void shouldSupportGetSchema()
140         throws Exception {
141         String getSchema = "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"m-0\">\n"
142             + "  <get>\n"
143             + "    <filter xmlns:ns0=\"urn:ietf:params:xml:ns:netconf:base:1.0\" ns0:type=\"subtree\">\n"
144             + "      <netconf-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring\">\n"
145             + "        <schemas/>\n"
146             + "      </netconf-state>\n"
147             + "    </filter>\n"
148             + "  </get>\n"
149             + "</rpc>";
150         Document docResponse = invokeRpc(TCP_SIMULATOR_CONFIG, getSchema);
151         Set<YangResource> expectedYangResources = Configuration.DEFAULT_YANG_RESOURCES;
152         assertEquals(4, expectedYangResources.size());
153         assertThat(docResponse)
154             .withNamespaceContext(PREFIX_2_URI)
155             .valueByXPath("count(//base10:rpc-reply/base10:data/ncmon:netconf-state/ncmon:schemas/ncmon:schema)")
156             .isEqualTo(expectedYangResources.size());
157     }
158
159     private Document invokeRpc(final Configuration simulatorConfig, final String xmlRequest)
160         throws Exception {
161         // GIVEN
162         int localPort = launchSimulator(simulatorConfig);
163         SimpleNetconfClientSessionListener sessionListener = new SimpleNetconfClientSessionListener();
164         NetconfClientConfiguration clientConfig = getClientConfig("localhost", localPort,
165             simulatorConfig, sessionListener);
166         Document docRequest = XmlUtil.readXmlToDocument(xmlRequest);
167         NetconfMessage request = new NetconfMessage(docRequest);
168
169         // WHEN
170         NetconfMessage response;
171         try (NetconfClientSession ignored = dispatcher.createClient(clientConfig).get()) {
172             response = sessionListener.sendRequest(request)
173                 .get(RECEIVE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
174         }
175
176         // THEN
177         assertNotNull(response);
178         return response.getDocument();
179     }
180
181     private static final ConcurrentHashMap<Configuration, Integer> CACHED_SIMULATORS = new ConcurrentHashMap<>();
182
183     /**
184      * Retrieves a previously launched simulator or launches a new one using the given configuration.
185      *
186      * @param configuration The simulator configuration.
187      * @return The TCP port number to access the launched simulator.
188      */
189     private int launchSimulator(final Configuration configuration) {
190         return CACHED_SIMULATORS.computeIfAbsent(configuration, cfg -> {
191             NetconfDeviceSimulator simulator = new NetconfDeviceSimulator(cfg);
192             simulator.start();
193             return logPropertyCatcher.getLastValue()
194                 .map(Integer::parseInt)
195                 .orElseThrow(() -> new IllegalArgumentException("Unable to capture auto-allocated port from log"));
196         });
197     }
198
199     @SuppressWarnings("deprecation")
200     private static Configuration getSimulatorConfig(final NetconfClientProtocol protocol, final User user) {
201         return new ConfigurationBuilder()
202             .setStartingPort(RANDOM_PORT)
203             .setRpcConfigFile(CUSTOM_RPC_CONFIG)
204             .setSsh(protocol == NetconfClientProtocol.SSH)
205             .setAuthProvider(new InMemoryAuthenticationProvider(user))
206             .build();
207     }
208
209     @SuppressWarnings("deprecation")
210     private static NetconfClientConfiguration getClientConfig(final String host, final int port,
211                                                               final Configuration simulatorConfig,
212                                                               final NetconfClientSessionListener sessionListener) {
213         User user = ((InMemoryAuthenticationProvider) simulatorConfig.getAuthProvider()).user;
214         return NetconfClientConfigurationBuilder.create()
215             .withAddress(new InetSocketAddress(host, port))
216             .withSessionListener(sessionListener)
217             .withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE,
218                 NetconfClientConfigurationBuilder.DEFAULT_CONNECTION_TIMEOUT_MILLIS))
219             .withProtocol(simulatorConfig.isSsh() ? NetconfClientProtocol.SSH : NetconfClientProtocol.TCP)
220             .withAuthHandler(new LoginPasswordHandler(user.username, user.password))
221             .build();
222     }
223
224     private static final class User {
225         private final String username;
226         private final String password;
227
228         private User(final String username, final String password) {
229             this.username = username;
230             this.password = password;
231         }
232     }
233
234     private static final class InMemoryAuthenticationProvider implements AuthProvider {
235
236         private final User user;
237
238         private InMemoryAuthenticationProvider(final User user) {
239             this.user = user;
240         }
241
242         @Override
243         public boolean authenticated(final String username, final String password) {
244             return user.username.equals(username) && user.password.equals(password);
245         }
246     }
247 }