Http client for testing restconf 43/21143/5
authorTomas Cere <tcere@cisco.com>
Thu, 14 May 2015 15:31:09 +0000 (17:31 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Mon, 8 Jun 2015 08:36:23 +0000 (08:36 +0000)
use --help to find out all suported args.

Change-Id: Ib9cae0479d672d4432bbc0fe87dc98a32f524784
Signed-off-by: Tomas Cere <tcere@cisco.com>
opendaylight/netconf/netconf-testtool/pom.xml
opendaylight/netconf/netconf-testtool/src/main/assembly/stress-client.xml [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/TestToolUtils.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/Parameters.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/PerfClientCallable.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/RestPerfClient.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java [new file with mode: 0644]
opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/stress/StressClient.java
opendaylight/netconf/netconf-testtool/src/main/resources/logback.xml [new file with mode: 0644]

index 2a2a2f065e463423f87f2a78bf92fc7feb040058..7c5f80d3c6ddba2ea35268dba68e272578ce17aa 100644 (file)
             <artifactId>logback-classic</artifactId>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>com.ning</groupId>
+            <artifactId>async-http-client</artifactId>
+            <version>1.9.24</version>
+        </dependency>
         <dependency>
             <groupId>org.bouncycastle</groupId>
             <artifactId>bcpkix-jdk15on</artifactId>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>
-        <dependency>
-            <groupId>xmlunit</groupId>
-            <artifactId>xmlunit</artifactId>
-        </dependency>
         <dependency>
             <groupId>com.google.guava</groupId>
             <artifactId>guava</artifactId>
                                         <exclude>META-INF/*.SF</exclude>
                                         <exclude>META-INF/*.DSA</exclude>
                                         <exclude>META-INF/*.RSA</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.client</exclude>
                                     </excludes>
                                 </filter>
                             </filters>
+                            <artifactSet>
+                                <excludes>
+                                    <exclude>com.ning</exclude>
+                                </excludes>
+                            </artifactSet>
                             <transformers>
                                 <transformer
                                         implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                         <exclude>META-INF/*.SF</exclude>
                                         <exclude>META-INF/*.DSA</exclude>
                                         <exclude>META-INF/*.RSA</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.client.http</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.rpc</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.AcceptingAuthProvider</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.DummyMonitoringService</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.FakeModuleBuilderCapability</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.Main</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.ModuleBuilderCapability</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.NetconfDeviceSimulator</exclude>
                                     </excludes>
                                 </filter>
                             </filters>
                             <shadedClassifierName>stress-client</shadedClassifierName>
                         </configuration>
                     </execution>
+
+                    <execution>
+                        <id>restconf-perf-client</id>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <phase>package</phase>
+                        <configuration>
+                            <shadedArtifactId>rest-perf-client</shadedArtifactId>
+                            <filters>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>META-INF/*.SF</exclude>
+                                        <exclude>META-INF/*.DSA</exclude>
+                                        <exclude>META-INF/*.RSA</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.rpc</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.AcceptingAuthProvider</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.DummyMonitoringService</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.FakeModuleBuilderCapability</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.Main</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.ModuleBuilderCapability</exclude>
+                                        <exclude>org.opendaylight.controller.netconf.test.tool.NetconfDeviceSimulator</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                            <artifactSet>
+                                <excludes>
+                                    <exclude>org.bouncycastle:*</exclude>
+                                    <exclude>com.google:*</exclude>
+                                    <exclude>org.opendaylight.yangtools</exclude>
+                                    <exclude>org.opendaylight.yang</exclude>
+                                </excludes>
+                            </artifactSet>
+                            <transformers>
+                                <transformer
+                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.opendaylight.controller.netconf.test.tool.client.http.perf.RestPerfClient</mainClass>
+                                </transformer>
+                            </transformers>
+                            <shadedArtifactAttached>true</shadedArtifactAttached>
+                            <shadedClassifierName>rest-perf-client</shadedClassifierName>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <artifactId>maven-assembly-plugin</artifactId>
+                <configuration>
+                    <descriptors>
+                        <descriptor>src/main/assembly/stress-client.xml</descriptor>
+                    </descriptors>
+                    <finalName>stress-client-${project.version}-package</finalName>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>make-assembly</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>single</goal>
+                        </goals>
+                    </execution>
                 </executions>
             </plugin>
         </plugins>
diff --git a/opendaylight/netconf/netconf-testtool/src/main/assembly/stress-client.xml b/opendaylight/netconf/netconf-testtool/src/main/assembly/stress-client.xml
new file mode 100644 (file)
index 0000000..5b640da
--- /dev/null
@@ -0,0 +1,30 @@
+<!--
+  ~ Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+  ~
+  ~ This program and the accompanying materials are made available under the
+  ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+  ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+  -->
+
+<assembly>
+  <id>stress-client</id>
+  <formats>
+    <format>tar.gz</format>
+  </formats>
+  <fileSets>
+    <fileSet>
+      <directory>target/lib</directory>
+      <outputDirectory>/lib</outputDirectory>
+      <includes>
+        <include>*</include>
+      </includes>
+    </fileSet>
+    <fileSet>
+      <directory>target</directory>
+      <outputDirectory></outputDirectory>
+      <includes>
+        <include>stress-client*.jar</include>
+      </includes>
+    </fileSet>
+  </fileSets>
+</assembly>
\ No newline at end of file
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/TestToolUtils.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/TestToolUtils.java
new file mode 100644 (file)
index 0000000..0bc4408
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool;
+
+public class TestToolUtils {
+
+    public static String getMac(long mac) {
+        StringBuilder m = new StringBuilder(Long.toString(mac, 16));
+
+        for (int i = m.length(); i < 12; i++) {
+            m.insert(0, "0");
+        }
+
+        for (int j = m.length() - 2; j >= 2; j -= 2) {
+            m.insert(j, ":");
+        }
+
+        return m.toString();
+    }
+}
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/AsyncExecutionStrategy.java
new file mode 100644 (file)
index 0000000..f1d76be
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool.client.http.perf;
+
+import com.ning.http.client.AsyncCompletionHandler;
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.HttpResponseStatus;
+import com.ning.http.client.ListenableFuture;
+import com.ning.http.client.Request;
+import com.ning.http.client.Response;
+import java.util.ArrayList;
+import java.util.concurrent.Semaphore;
+import org.opendaylight.controller.netconf.test.tool.client.stress.ExecutionStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AsyncExecutionStrategy implements ExecutionStrategy{
+
+    private static final Logger LOG = LoggerFactory.getLogger(AsyncExecutionStrategy.class);
+
+    private final Parameters params;
+    private final ArrayList<Request> payloads;
+    private final AsyncHttpClient asyncHttpClient;
+    private final Semaphore semaphore;
+
+    AsyncExecutionStrategy(final Parameters params, final AsyncHttpClient asyncHttpClient, final ArrayList<Request> payloads) {
+        this.params = params;
+        this.asyncHttpClient = asyncHttpClient;
+        this.payloads = payloads;
+        this.semaphore = new Semaphore(RestPerfClient.throttle);
+    }
+
+    @Override
+    public void invoke() {
+        final ArrayList<ListenableFuture<Response>> futures = new ArrayList<>();
+        LOG.info("Begin sending async requests");
+
+        for (final Request request : payloads) {
+            try {
+                semaphore.acquire();
+            } catch (InterruptedException e) {
+                LOG.warn("Semaphore acquire interrupted");
+            }
+            futures.add(asyncHttpClient.executeRequest(request, new AsyncCompletionHandler<Response>() {
+                @Override
+                public STATE onStatusReceived(HttpResponseStatus status) throws Exception {
+                    super.onStatusReceived(status);
+                    if (status.getStatusCode() != 200 && status.getStatusCode() != 204) {
+                        LOG.warn("Request failed, status code: {}", status.getStatusCode() + status.getStatusText());
+                        LOG.warn("request: {}", request.toString());
+                    }
+                    return STATE.CONTINUE;
+                }
+
+                @Override
+                public Response onCompleted(Response response) throws Exception {
+                    semaphore.release();
+                    return response;
+                }
+            }));
+        }
+        LOG.info("Requests sent, waiting for responses");
+
+        try {
+            semaphore.acquire(RestPerfClient.throttle);
+        } catch (InterruptedException e) {
+            LOG.warn("Semaphore acquire interrupted");
+        }
+
+        LOG.info("Responses received, ending...");
+    }
+}
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/Parameters.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/Parameters.java
new file mode 100644 (file)
index 0000000..20f0fca
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool.client.http.perf;
+
+import com.google.common.base.Preconditions;
+import java.io.File;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+
+public class Parameters {
+
+    @Arg(dest = "ip")
+    public String ip;
+
+    @Arg(dest = "port")
+    public int port;
+
+    @Arg(dest = "destination")
+    public String destination;
+
+    @Arg(dest = "edit-count")
+    public int editCount;
+
+    @Arg(dest = "edit-content")
+    public File editContent;
+
+    @Arg(dest = "async")
+    public boolean async;
+
+    @Arg(dest = "thread-amount")
+    public int threadAmount;
+
+    @Arg(dest = "same-device")
+    public boolean sameDevice;
+
+    @Arg(dest = "device-port-range-start")
+    public int devicePortRangeStart;
+
+    @Arg(dest = "throttle")
+    public int throttle;
+
+    static ArgumentParser getParser() {
+        final ArgumentParser parser = ArgumentParsers.newArgumentParser("netconf stress client");
+
+        parser.description("Netconf stress client");
+
+        parser.addArgument("--ip")
+                .type(String.class)
+                .setDefault("127.0.0.1")
+                .help("Restconf server IP")
+                .dest("ip");
+
+        parser.addArgument("--port")
+                .type(Integer.class)
+                .setDefault(8181)
+                .help("Restconf server port")
+                .dest("port");
+
+        parser.addArgument("--destination")
+                .type(String.class)
+                .setDefault("/restconf/config/network-topology:network-topology/topology/topology-netconf/node/" +
+                        "{DEVICE_PORT}-sim-device/yang-ext:mount/cisco-vpp:vpp/bridge-domains/bridge-domain/a")
+                .help("Destination to send the requests to after the ip:port part of the uri. " +
+                        "Use {DEVICE_PORT} tag to use the device-port-range-start argument")
+                .dest("destination");
+
+        parser.addArgument("--edits")
+                .type(Integer.class)
+                .setDefault(50000)
+                .help("Amount requests to be sent")
+                .dest("edit-count");
+
+        parser.addArgument("--edit-content")
+                .type(File.class)
+                .setDefault(new File("edit.txt"))
+                .dest("edit-content");
+
+        parser.addArgument("--async-requests")
+                .type(Boolean.class)
+                .setDefault(true)
+                .dest("async");
+
+        parser.addArgument("--thread-amount")
+                .type(Integer.class)
+                .setDefault(1)
+                .dest("thread-amount");
+
+        parser.addArgument("--same-device")
+                .type(Boolean.class)
+                .setDefault(true)
+                .help("If true, every thread edits the device at the first port. If false, n-th thread edits device at n-th port.")
+                .dest("same-device");
+
+        parser.addArgument("--device-port-range-start")
+                .type(Integer.class)
+                .setDefault(17830)
+                .dest("device-port-range-start");
+
+        parser.addArgument("--throttle")
+                .type(Integer.class)
+                .setDefault(5000)
+                .help("Maximum amount of async requests that can be open at a time, " +
+                        "with mutltiple threads this gets divided among all threads")
+                .dest("throttle");
+
+        return parser;
+    }
+
+    void validate() {
+        Preconditions.checkArgument(port > 0, "Port =< 0");
+        Preconditions.checkArgument(editCount > 0, "Edit count =< 0");
+
+        Preconditions.checkArgument(editContent.exists(), "Edit content file missing");
+        Preconditions.checkArgument(editContent.isDirectory() == false, "Edit content file is a dir");
+        Preconditions.checkArgument(editContent.canRead(), "Edit content file is unreadable");
+        // TODO validate
+    }
+
+    public InetSocketAddress getInetAddress() {
+        try {
+            return new InetSocketAddress(InetAddress.getByName(ip), port);
+        } catch (final UnknownHostException e) {
+            throw new IllegalArgumentException("Unknown ip", e);
+        }
+    }
+}
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/PerfClientCallable.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/PerfClientCallable.java
new file mode 100644 (file)
index 0000000..fa444d9
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool.client.http.perf;
+
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.AsyncHttpClientConfig;
+import com.ning.http.client.Request;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import org.opendaylight.controller.netconf.test.tool.client.http.perf.RestPerfClient.DestToPayload;
+import org.opendaylight.controller.netconf.test.tool.client.stress.ExecutionStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class PerfClientCallable implements Callable<Void>{
+
+    private static final Logger LOG = LoggerFactory.getLogger(PerfClientCallable.class);
+
+    private final Parameters params;
+    private final ArrayList<Request> payloads;
+    private final AsyncHttpClient asyncHttpClient;
+    private ExecutionStrategy executionStrategy;
+
+    public PerfClientCallable(Parameters params, ArrayList<DestToPayload> payloads) {
+        this.params = params;
+        this.asyncHttpClient = new AsyncHttpClient(new AsyncHttpClientConfig.Builder()
+                .setConnectTimeout(Integer.MAX_VALUE)
+                .setRequestTimeout(Integer.MAX_VALUE)
+                .setAllowPoolingConnections(true)
+                .build());
+        this.payloads = new ArrayList<>();
+        for (DestToPayload payload : payloads) {
+            this.payloads.add(asyncHttpClient.preparePost(payload.getDestination())
+                    .addHeader("content-type", "application/json")
+                    .addHeader("Accept", "application/xml")
+                    .setBody(payload.getPayload())
+                    .setRequestTimeout(Integer.MAX_VALUE)
+                    .build());
+        }
+        executionStrategy = getExecutionStrategy();
+    }
+
+    private ExecutionStrategy getExecutionStrategy() {
+        return params.async
+                ? new AsyncExecutionStrategy(params, asyncHttpClient, payloads)
+                : new SyncExecutionStrategy(params, asyncHttpClient, payloads);
+    }
+
+    @Override
+    public Void call() throws Exception{
+
+        executionStrategy.invoke();
+        asyncHttpClient.closeAsynchronously();
+        return null;
+    }
+}
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/RestPerfClient.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/RestPerfClient.java
new file mode 100644 (file)
index 0000000..81113b2
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool.client.http.perf;
+
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Stopwatch;
+import com.google.common.io.Files;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+import org.opendaylight.controller.netconf.test.tool.TestToolUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RestPerfClient {
+
+    private static final Logger LOG = LoggerFactory.getLogger(RestPerfClient.class);
+
+    private static final String HOST_KEY = "{HOST}";
+    private static final String PORT_KEY = "{PORT}";
+    private static final String DEVICE_PORT_KEY = "{DEVICE_PORT}";
+
+    private static final String PEER_KEY = "{PEERID}";
+    private static final String INT_LEAF_KEY = "{INTLEAF}";
+
+    private static final String PHYS_ADDR_PLACEHOLDER = "{PHYS_ADDR}";
+
+    private static final String dest = "http://{HOST}:{PORT}";
+
+    private static long macStart = 0xAABBCCDD0000L;
+
+    static int throttle;
+
+    static final class DestToPayload {
+
+        private final String destination;
+        private final String payload;
+
+        public DestToPayload(String destination, String payload) {
+            this.destination = destination;
+            this.payload = payload;
+        }
+
+        public String getDestination() {
+            return destination;
+        }
+
+        public String getPayload() {
+            return payload;
+        }
+    }
+
+    public static void main(String[] args) throws IOException {
+
+        Parameters parameters = parseArgs(args, Parameters.getParser());
+        parameters.validate();
+        throttle = parameters.throttle / parameters.threadAmount;
+
+        if (parameters.async && parameters.threadAmount > 1) {
+            LOG.info("Throttling per thread: {}", throttle);
+        }
+
+        final String editContentString;
+        try {
+            editContentString = Files.toString(parameters.editContent, Charsets.UTF_8);
+        } catch (final IOException e) {
+            throw new IllegalArgumentException("Cannot read content of " + parameters.editContent);
+        }
+
+        final int threadAmount = parameters.threadAmount;
+        LOG.info("thread amount: {}", threadAmount);
+        final int requestsPerThread = parameters.editCount / parameters.threadAmount;
+        LOG.info("requestsPerThread: {}", requestsPerThread);
+        final int leftoverRequests = parameters.editCount % parameters.threadAmount;
+        LOG.info("leftoverRequests: {}", leftoverRequests);
+
+        final ArrayList<ArrayList<DestToPayload>> allThreadsPayloads = new ArrayList<>();
+        for (int i = 0; i < threadAmount; i++) {
+            final ArrayList<DestToPayload> payloads = new ArrayList<>();
+            for (int j = 0; j < requestsPerThread; j++) {
+                final int devicePort = parameters.sameDevice ? parameters.devicePortRangeStart : parameters.devicePortRangeStart + i;
+                final StringBuilder destBuilder = new StringBuilder(dest);
+                destBuilder.replace(destBuilder.indexOf(HOST_KEY), destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(), parameters.ip)
+                        .replace(destBuilder.indexOf(PORT_KEY), destBuilder.indexOf(PORT_KEY) + PORT_KEY.length(), parameters.port + "");
+                final StringBuilder suffixBuilder = new StringBuilder(parameters.destination);
+                if (suffixBuilder.indexOf(DEVICE_PORT_KEY) != -1) {
+                    suffixBuilder.replace(suffixBuilder.indexOf(DEVICE_PORT_KEY), suffixBuilder.indexOf(DEVICE_PORT_KEY) + DEVICE_PORT_KEY.length(), devicePort + "");
+                }
+                destBuilder.append(suffixBuilder);
+
+                payloads.add(new DestToPayload(destBuilder.toString(), prepareMessage(i, j, editContentString)));
+            }
+            allThreadsPayloads.add(payloads);
+        }
+
+        for (int i = 0; i < leftoverRequests; i++) {
+            ArrayList<DestToPayload> payloads = allThreadsPayloads.get(allThreadsPayloads.size() - 1);
+
+            final int devicePort = parameters.sameDevice ? parameters.devicePortRangeStart : parameters.devicePortRangeStart + threadAmount - 1;
+            final StringBuilder destBuilder = new StringBuilder(dest);
+            destBuilder.replace(destBuilder.indexOf(HOST_KEY), destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(), parameters.ip)
+                    .replace(destBuilder.indexOf(PORT_KEY), destBuilder.indexOf(PORT_KEY) + PORT_KEY.length(), parameters.port + "");
+            final StringBuilder suffixBuilder = new StringBuilder(parameters.destination);
+            if (suffixBuilder.indexOf(DEVICE_PORT_KEY) != -1) {
+                suffixBuilder.replace(suffixBuilder.indexOf(DEVICE_PORT_KEY), suffixBuilder.indexOf(DEVICE_PORT_KEY) + DEVICE_PORT_KEY.length(), devicePort + "");
+            }
+            destBuilder.append(suffixBuilder);
+            payloads.add(new DestToPayload(destBuilder.toString(), prepareMessage(threadAmount - 1, requestsPerThread + i, editContentString)));
+        }
+
+        final ArrayList<PerfClientCallable> callables = new ArrayList<>();
+        for (ArrayList<DestToPayload> payloads : allThreadsPayloads) {
+            callables.add(new PerfClientCallable(parameters, payloads));
+        }
+
+        final ExecutorService executorService = Executors.newFixedThreadPool(threadAmount);
+
+        LOG.info("Starting performance test");
+        final Stopwatch started = Stopwatch.createStarted();
+        try {
+            final List<Future<Void>> futures = executorService.invokeAll(callables, 5, TimeUnit.MINUTES);
+            for (final Future<Void> future : futures) {
+                try {
+                    future.get(4L, TimeUnit.MINUTES);
+                } catch (ExecutionException | TimeoutException e) {
+                    throw new RuntimeException(e);
+                }
+            }
+            executorService.shutdownNow();
+        } catch (final InterruptedException e) {
+            throw new RuntimeException("Unable to execute requests", e);
+        }
+        started.stop();
+
+        LOG.info("FINISHED. Execution time: {}", started);
+        LOG.info("Requests per second: {}", (parameters.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS)));
+
+        System.exit(0);
+    }
+
+    private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
+        final Parameters opt = new Parameters();
+        try {
+            parser.parseArgs(args, opt);
+            return opt;
+        } catch (final ArgumentParserException e) {
+            parser.handleError(e);
+        }
+
+        System.exit(1);
+        return null;
+    }
+
+    private static String prepareMessage(final int idi, final int idj, final String editContentString) {
+        StringBuilder messageBuilder = new StringBuilder(editContentString);
+        if (editContentString.contains(PEER_KEY)) {
+            messageBuilder.replace(messageBuilder.indexOf(PEER_KEY), messageBuilder.indexOf(PEER_KEY) + PEER_KEY.length(), Integer.toString(idi))
+                    .replace(messageBuilder.indexOf(INT_LEAF_KEY), messageBuilder.indexOf(INT_LEAF_KEY) + INT_LEAF_KEY.length(), Integer.toString(idj));
+        }
+
+        int idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
+
+        while (idx != -1) {
+            messageBuilder.replace(idx, idx + PHYS_ADDR_PLACEHOLDER.length(), TestToolUtils.getMac(macStart++));
+            idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
+        }
+
+        return messageBuilder.toString();
+    }
+}
diff --git a/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java b/opendaylight/netconf/netconf-testtool/src/main/java/org/opendaylight/controller/netconf/test/tool/client/http/perf/SyncExecutionStrategy.java
new file mode 100644 (file)
index 0000000..c3d9646
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.netconf.test.tool.client.http.perf;
+
+import com.ning.http.client.AsyncHttpClient;
+import com.ning.http.client.Request;
+import com.ning.http.client.Response;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
+import org.opendaylight.controller.netconf.test.tool.client.stress.ExecutionStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class SyncExecutionStrategy implements ExecutionStrategy{
+
+    private static final Logger LOG = LoggerFactory.getLogger(SyncExecutionStrategy.class);
+
+    private final Parameters params;
+    private final ArrayList<Request> payloads;
+    private final AsyncHttpClient asyncHttpClient;
+
+    SyncExecutionStrategy(final Parameters params, final AsyncHttpClient asyncHttpClient, final ArrayList<Request> payloads) {
+        this.params = params;
+        this.asyncHttpClient = asyncHttpClient;
+        this.payloads = payloads;
+    }
+
+    @Override
+    public void invoke() {
+
+        LOG.info("Begin sending sync requests");
+        for (Request request : payloads) {
+            try {
+                Response response = asyncHttpClient.executeRequest(request).get();
+                if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
+                    LOG.warn("Status code: {}", response.getStatusCode());
+                    LOG.warn("url: {}", request.getUrl());
+                    LOG.warn(response.getResponseBody());
+                }
+            } catch (InterruptedException | ExecutionException | IOException e) {
+                LOG.warn(e.toString());
+            }
+        }
+        LOG.info("End sending sync requests");
+
+    }
+}
index e03de59f2c71ea6694683b17c70ad27aa6e42900..36b947aac4e9a2e97caeea6fcf76ecefd0efda71 100644 (file)
@@ -32,6 +32,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.opendaylight.controller.netconf.api.NetconfMessage;
 import org.opendaylight.controller.netconf.client.NetconfClientDispatcherImpl;
 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.client.AsyncSshHandler;
+import org.opendaylight.controller.netconf.test.tool.TestToolUtils;
 import org.opendaylight.controller.netconf.util.xml.XmlUtil;
 import org.opendaylight.controller.sal.connect.api.RemoteDevice;
 import org.opendaylight.controller.sal.connect.netconf.listener.NetconfDeviceCommunicator;
@@ -193,7 +194,7 @@ public final class StressClient {
             final StringBuilder stringBuilder = new StringBuilder(specificEditContent);
             int idx = stringBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
             while (idx!= -1) {
-                stringBuilder.replace(idx, idx + PHYS_ADDR_PLACEHOLDER.length(), getMac(macStart++));
+                stringBuilder.replace(idx, idx + PHYS_ADDR_PLACEHOLDER.length(), TestToolUtils.getMac(macStart++));
                 idx = stringBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
             }
             specificEditContent = stringBuilder.toString();
@@ -227,20 +228,6 @@ public final class StressClient {
         return netconfClientDispatcher;
     }
 
-    public static String getMac(long mac) {
-        StringBuilder m = new StringBuilder(Long.toString(mac, 16));
-
-        for (int i = m.length(); i < 12; i++) {
-            m.insert(0, "0");
-        }
-
-        for (int j = m.length() - 2; j >= 2; j-=2) {
-            m.insert(j, ":");
-        }
-
-        return m.toString();
-    }
-
     private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
         final Parameters opt = new Parameters();
         try {
diff --git a/opendaylight/netconf/netconf-testtool/src/main/resources/logback.xml b/opendaylight/netconf/netconf-testtool/src/main/resources/logback.xml
new file mode 100644 (file)
index 0000000..df3ab10
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+  ~
+  ~ This program and the accompanying materials are made available under the
+  ~ terms of the Eclipse Public License v1.0 which accompanies this distribution,
+  ~ and is available at http://www.eclipse.org/legal/epl-v10.html
+  -->
+
+<configuration>
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT"/>
+    </root>
+
+    <logger name="com.ning.http.client" level="WARN"/>
+</configuration>
\ No newline at end of file