Merge "Add assertNotNull to prevent null analysis warnings"
[controller.git] / opendaylight / netconf / netconf-testtool / src / main / java / org / opendaylight / controller / netconf / test / tool / client / stress / StressClient.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  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.controller.netconf.test.tool.client.stress;
10
11 import ch.qos.logback.classic.Level;
12 import com.google.common.base.Charsets;
13 import com.google.common.base.Stopwatch;
14 import com.google.common.collect.Lists;
15 import com.google.common.io.Files;
16 import io.netty.channel.nio.NioEventLoopGroup;
17 import io.netty.util.HashedWheelTimer;
18 import io.netty.util.Timer;
19 import io.netty.util.concurrent.GlobalEventExecutor;
20 import java.io.IOException;
21 import java.net.InetSocketAddress;
22 import java.util.List;
23 import java.util.concurrent.ExecutionException;
24 import java.util.concurrent.TimeUnit;
25 import java.util.concurrent.TimeoutException;
26 import net.sourceforge.argparse4j.inf.ArgumentParser;
27 import net.sourceforge.argparse4j.inf.ArgumentParserException;
28 import org.opendaylight.controller.netconf.api.NetconfMessage;
29 import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
30 import org.opendaylight.controller.netconf.client.NetconfClientSession;
31 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfiguration;
32 import org.opendaylight.controller.netconf.client.conf.NetconfClientConfigurationBuilder;
33 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
34 import org.opendaylight.controller.sal.connect.api.RemoteDevice;
35 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
36 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfSessionPreferences;
37 import org.opendaylight.controller.sal.connect.util.RemoteDeviceId;
38 import org.opendaylight.protocol.framework.NeverReconnectStrategy;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.CommitInput;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfigInput;
41 import org.opendaylight.yangtools.yang.common.QName;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.w3c.dom.Document;
45 import org.w3c.dom.Element;
46 import org.w3c.dom.Node;
47 import org.xml.sax.SAXException;
48
49 public final class StressClient {
50
51     private static final Logger LOG = LoggerFactory.getLogger(StressClient.class);
52
53     static final QName COMMIT_QNAME = QName.create(CommitInput.QNAME, "commit");
54     public static final NetconfMessage COMMIT_MSG;
55
56     static {
57         try {
58             COMMIT_MSG = new NetconfMessage(XmlUtil.readXmlToDocument("<rpc message-id=\"commit-batch\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
59                     "    <commit/>\n" +
60                     "</rpc>"));
61         } catch (SAXException | IOException e) {
62             throw new ExceptionInInitializerError(e);
63         }
64     }
65
66     static final QName EDIT_QNAME = QName.create(EditConfigInput.QNAME, "edit-config");
67     static final org.w3c.dom.Document editBlueprint;
68
69     static {
70         try {
71             editBlueprint = XmlUtil.readXmlToDocument(
72                     "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
73                             "    <edit-config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
74                             "        <target>\n" +
75                             "            <candidate/>\n" +
76                             "        </target>\n" +
77                             "        <config/>\n" +
78                             "    </edit-config>\n" +
79                             "</rpc>");
80         } catch (SAXException | IOException e) {
81             throw new ExceptionInInitializerError(e);
82         }
83     }
84
85     private static final String MSG_ID_PLACEHOLDER = "{MSG_ID}";
86     private static final String MSG_ID_PLACEHOLDER_REGEX = "\\{MSG_ID\\}";
87
88     public static void main(final String[] args) {
89         final Parameters params = parseArgs(args, Parameters.getParser());
90         params.validate();
91
92         // TODO remove
93         try {
94             Thread.sleep(10000);
95         } catch (final InterruptedException e) {
96 //            e.printStackTrace();
97         }
98
99         final ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
100         root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
101
102         LOG.info("Preparing messages");
103         // Prepare all msgs up front
104         final List<NetconfMessage> preparedMessages = Lists.newArrayListWithCapacity(params.editCount);
105
106         final String editContentString;
107         boolean needsModification = false;
108         try {
109             editContentString = Files.toString(params.editContent, Charsets.UTF_8);
110             if(editContentString.contains(MSG_ID_PLACEHOLDER)) {
111                 needsModification = true;
112             };
113         } catch (IOException e) {
114             throw new IllegalArgumentException("Cannot read content of " + params.editContent);
115         }
116
117         for (int i = 0; i < params.editCount; i++) {
118             final Document msg = XmlUtil.createDocumentCopy(editBlueprint);
119             msg.getDocumentElement().setAttribute("message-id", Integer.toString(i));
120             final NetconfMessage netconfMessage = new NetconfMessage(msg);
121
122             final Element editContentElement;
123             try {
124                 // Insert message id where needed
125                 final String specificEditContent = needsModification ?
126                         editContentString.replaceAll(MSG_ID_PLACEHOLDER_REGEX, Integer.toString(i)) :
127                         editContentString;
128
129                 editContentElement = XmlUtil.readXmlToElement(specificEditContent);
130                 final Node config = ((Element) msg.getDocumentElement().getElementsByTagName("edit-config").item(0)).
131                         getElementsByTagName("config").item(0);
132                 config.appendChild(msg.importNode(editContentElement, true));
133             } catch (final IOException | SAXException e) {
134                 throw new IllegalArgumentException("Edit content file is unreadable", e);
135             }
136
137             preparedMessages.add(netconfMessage);
138
139         }
140
141
142         final NioEventLoopGroup nioGroup = new NioEventLoopGroup();
143         final Timer timer = new HashedWheelTimer();
144
145         final NetconfClientDispatcherImpl netconfClientDispatcher = configureClientDispatcher(params, nioGroup, timer);
146
147         final NetconfDeviceCommunicator sessionListener = getSessionListener(params.getInetAddress());
148
149         final NetconfClientConfiguration cfg = getNetconfClientConfiguration(params, sessionListener);
150
151         LOG.info("Connecting to netconf server {}:{}", params.ip, params.port);
152         final NetconfClientSession netconfClientSession;
153         try {
154             netconfClientSession = netconfClientDispatcher.createClient(cfg).get();
155         } catch (final InterruptedException e) {
156             throw new RuntimeException(e);
157         } catch (final ExecutionException e) {
158             throw new RuntimeException("Unable to connect", e);
159         }
160
161         LOG.info("Starting stress test");
162         final Stopwatch started = Stopwatch.createStarted();
163         getExecutionStrategy(params, preparedMessages, sessionListener).invoke();
164         started.stop();
165
166         LOG.info("FINISHED. Execution time: {}", started);
167         LOG.info("Requests per second: {}", (params.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS)));
168
169         // Cleanup
170         netconfClientSession.close();
171         timer.stop();
172         try {
173             nioGroup.shutdownGracefully().get(20L, TimeUnit.SECONDS);
174         } catch (InterruptedException | ExecutionException | TimeoutException e) {
175             LOG.warn("Unable to close executor properly", e);
176         }
177     }
178
179     private static ExecutionStrategy getExecutionStrategy(final Parameters params, final List<NetconfMessage> preparedMessages, final NetconfDeviceCommunicator sessionListener) {
180         if(params.async) {
181             return new AsyncExecutionStrategy(params, preparedMessages, sessionListener);
182         } else {
183             return new SyncExecutionStrategy(params, preparedMessages, sessionListener);
184         }
185     }
186
187     private static NetconfClientDispatcherImpl configureClientDispatcher(final Parameters params, final NioEventLoopGroup nioGroup, final Timer timer) {
188         final NetconfClientDispatcherImpl netconfClientDispatcher;
189         if(params.exi) {
190             if(params.legacyFraming) {
191                 netconfClientDispatcher= ConfigurableClientDispatcher.createLegacyExi(nioGroup, nioGroup, timer);
192             } else {
193                 netconfClientDispatcher = ConfigurableClientDispatcher.createChunkedExi(nioGroup, nioGroup, timer);
194             }
195         } else {
196             if(params.legacyFraming) {
197                 netconfClientDispatcher = ConfigurableClientDispatcher.createLegacy(nioGroup, nioGroup, timer);
198             } else {
199                 netconfClientDispatcher = ConfigurableClientDispatcher.createChunked(nioGroup, nioGroup, timer);
200             }
201         }
202         return netconfClientDispatcher;
203     }
204
205     private static NetconfClientConfiguration getNetconfClientConfiguration(final Parameters params, final NetconfDeviceCommunicator sessionListener) {
206         final NetconfClientConfigurationBuilder netconfClientConfigurationBuilder = NetconfClientConfigurationBuilder.create();
207         netconfClientConfigurationBuilder.withSessionListener(sessionListener);
208         netconfClientConfigurationBuilder.withAddress(params.getInetAddress());
209         netconfClientConfigurationBuilder.withProtocol(params.ssh ? NetconfClientConfiguration.NetconfClientProtocol.SSH : NetconfClientConfiguration.NetconfClientProtocol.TCP);
210         netconfClientConfigurationBuilder.withConnectionTimeoutMillis(20000L);
211         netconfClientConfigurationBuilder.withReconnectStrategy(new NeverReconnectStrategy(GlobalEventExecutor.INSTANCE, 5000));
212         return netconfClientConfigurationBuilder.build();
213     }
214
215     static NetconfDeviceCommunicator getSessionListener(final InetSocketAddress inetAddress) {
216         final RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> loggingRemoteDevice = new LoggingRemoteDevice();
217         return new NetconfDeviceCommunicator(new RemoteDeviceId("secure-test", inetAddress), loggingRemoteDevice);
218     }
219
220     private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
221         final Parameters opt = new Parameters();
222         try {
223             parser.parseArgs(args, opt);
224             return opt;
225         } catch (final ArgumentParserException e) {
226             parser.handleError(e);
227         }
228
229         System.exit(1);
230         return null;
231     }
232
233
234     private static class LoggingRemoteDevice implements RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> {
235         @Override
236         public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceCommunicator netconfDeviceCommunicator) {
237             LOG.info("Session established");
238         }
239
240         @Override
241         public void onRemoteSessionDown() {
242             LOG.info("Session down");
243         }
244
245         @Override
246         public void onRemoteSessionFailed(final Throwable throwable) {
247             LOG.info("Session failed");
248         }
249
250         @Override
251         public void onNotification(final NetconfMessage notification) {
252             LOG.info("Notification received: {}", notification.toString());
253         }
254     }
255
256 }