2 * Copyright (c) 2015 Cisco Systems, Inc. and others. 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.controller.netconf.test.tool.client.stress;
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;
49 public final class StressClient {
51 private static final Logger LOG = LoggerFactory.getLogger(StressClient.class);
53 static final QName COMMIT_QNAME = QName.create(CommitInput.QNAME, "commit");
54 public static final NetconfMessage COMMIT_MSG;
58 COMMIT_MSG = new NetconfMessage(XmlUtil.readXmlToDocument("<rpc message-id=\"commit-batch\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n" +
61 } catch (SAXException | IOException e) {
62 throw new ExceptionInInitializerError(e);
66 static final QName EDIT_QNAME = QName.create(EditConfigInput.QNAME, "edit-config");
67 static final org.w3c.dom.Document editBlueprint;
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" +
80 } catch (SAXException | IOException e) {
81 throw new ExceptionInInitializerError(e);
85 private static final String MSG_ID_PLACEHOLDER = "{MSG_ID}";
86 private static final String MSG_ID_PLACEHOLDER_REGEX = "\\{MSG_ID\\}";
88 public static void main(final String[] args) {
89 final Parameters params = parseArgs(args, Parameters.getParser());
95 } catch (final InterruptedException e) {
96 // e.printStackTrace();
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);
102 LOG.info("Preparing messages");
103 // Prepare all msgs up front
104 final List<NetconfMessage> preparedMessages = Lists.newArrayListWithCapacity(params.editCount);
106 final String editContentString;
107 boolean needsModification = false;
109 editContentString = Files.toString(params.editContent, Charsets.UTF_8);
110 if(editContentString.contains(MSG_ID_PLACEHOLDER)) {
111 needsModification = true;
113 } catch (IOException e) {
114 throw new IllegalArgumentException("Cannot read content of " + params.editContent);
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);
122 final Element editContentElement;
124 // Insert message id where needed
125 final String specificEditContent = needsModification ?
126 editContentString.replaceAll(MSG_ID_PLACEHOLDER_REGEX, Integer.toString(i)) :
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);
137 preparedMessages.add(netconfMessage);
142 final NioEventLoopGroup nioGroup = new NioEventLoopGroup();
143 final Timer timer = new HashedWheelTimer();
145 final NetconfClientDispatcherImpl netconfClientDispatcher = configureClientDispatcher(params, nioGroup, timer);
147 final NetconfDeviceCommunicator sessionListener = getSessionListener(params.getInetAddress());
149 final NetconfClientConfiguration cfg = getNetconfClientConfiguration(params, sessionListener);
151 LOG.info("Connecting to netconf server {}:{}", params.ip, params.port);
152 final NetconfClientSession netconfClientSession;
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);
161 LOG.info("Starting stress test");
162 final Stopwatch started = Stopwatch.createStarted();
163 getExecutionStrategy(params, preparedMessages, sessionListener).invoke();
166 LOG.info("FINISHED. Execution time: {}", started);
167 LOG.info("Requests per second: {}", (params.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS)));
170 netconfClientSession.close();
173 nioGroup.shutdownGracefully().get(20L, TimeUnit.SECONDS);
174 } catch (InterruptedException | ExecutionException | TimeoutException e) {
175 LOG.warn("Unable to close executor properly", e);
179 private static ExecutionStrategy getExecutionStrategy(final Parameters params, final List<NetconfMessage> preparedMessages, final NetconfDeviceCommunicator sessionListener) {
181 return new AsyncExecutionStrategy(params, preparedMessages, sessionListener);
183 return new SyncExecutionStrategy(params, preparedMessages, sessionListener);
187 private static NetconfClientDispatcherImpl configureClientDispatcher(final Parameters params, final NioEventLoopGroup nioGroup, final Timer timer) {
188 final NetconfClientDispatcherImpl netconfClientDispatcher;
190 if(params.legacyFraming) {
191 netconfClientDispatcher= ConfigurableClientDispatcher.createLegacyExi(nioGroup, nioGroup, timer);
193 netconfClientDispatcher = ConfigurableClientDispatcher.createChunkedExi(nioGroup, nioGroup, timer);
196 if(params.legacyFraming) {
197 netconfClientDispatcher = ConfigurableClientDispatcher.createLegacy(nioGroup, nioGroup, timer);
199 netconfClientDispatcher = ConfigurableClientDispatcher.createChunked(nioGroup, nioGroup, timer);
202 return netconfClientDispatcher;
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();
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);
220 private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
221 final Parameters opt = new Parameters();
223 parser.parseArgs(args, opt);
225 } catch (final ArgumentParserException e) {
226 parser.handleError(e);
234 private static class LoggingRemoteDevice implements RemoteDevice<NetconfSessionPreferences, NetconfMessage, NetconfDeviceCommunicator> {
236 public void onRemoteSessionUp(final NetconfSessionPreferences remoteSessionCapabilities, final NetconfDeviceCommunicator netconfDeviceCommunicator) {
237 LOG.info("Session established");
241 public void onRemoteSessionDown() {
242 LOG.info("Session down");
246 public void onRemoteSessionFailed(final Throwable throwable) {
247 LOG.info("Session failed");
251 public void onNotification(final NetconfMessage notification) {
252 LOG.info("Notification received: {}", notification.toString());