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.netconf.test.tool.client.http.perf;
11 import com.google.common.base.Stopwatch;
12 import com.google.common.io.Files;
13 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
14 import java.io.IOException;
15 import java.nio.charset.StandardCharsets;
16 import java.util.ArrayList;
17 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.ExecutorService;
20 import java.util.concurrent.Executors;
21 import java.util.concurrent.Future;
22 import java.util.concurrent.TimeUnit;
23 import net.sourceforge.argparse4j.inf.ArgumentParser;
24 import net.sourceforge.argparse4j.inf.ArgumentParserException;
25 import org.opendaylight.netconf.test.tool.TestToolUtils;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 @SuppressFBWarnings("DM_EXIT")
30 public final class RestPerfClient {
32 private static final Logger LOG = LoggerFactory.getLogger(RestPerfClient.class);
34 private static final String HOST_KEY = "{HOST}";
35 private static final String PORT_KEY = "{PORT}";
36 private static final String DEVICE_PORT_KEY = "{DEVICE_PORT}";
38 private static final String PEER_KEY = "{PEERID}";
39 private static final String INT_LEAF_KEY = "{INTLEAF}";
41 private static final String PHYS_ADDR_PLACEHOLDER = "{PHYS_ADDR}";
43 private static final String DEST = "http://{HOST}:{PORT}";
45 private static long macStart = 0xAABBCCDD0000L;
49 static final class DestToPayload {
51 private final String destination;
52 private final String payload;
54 DestToPayload(final String destination, final String payload) {
55 this.destination = destination;
56 this.payload = payload;
59 public String getDestination() {
63 public String getPayload() {
68 private RestPerfClient() {
72 public static void main(final String[] args) {
74 Parameters parameters = parseArgs(args, Parameters.getParser());
75 parameters.validate();
76 throttle = parameters.throttle / parameters.threadAmount;
78 if (parameters.async && parameters.threadAmount > 1) {
79 LOG.info("Throttling per thread: {}", throttle);
82 final String editContentString;
84 editContentString = Files.asCharSource(parameters.editContent, StandardCharsets.UTF_8).read();
85 } catch (final IOException e) {
86 throw new IllegalArgumentException("Cannot read content of " + parameters.editContent, e);
89 final int threadAmount = parameters.threadAmount;
90 LOG.info("thread amount: {}", threadAmount);
91 final int requestsPerThread = parameters.editCount / parameters.threadAmount;
92 LOG.info("requestsPerThread: {}", requestsPerThread);
93 final int leftoverRequests = parameters.editCount % parameters.threadAmount;
94 LOG.info("leftoverRequests: {}", leftoverRequests);
96 final ArrayList<ArrayList<DestToPayload>> allThreadsPayloads = new ArrayList<>();
97 for (int i = 0; i < threadAmount; i++) {
98 final ArrayList<DestToPayload> payloads = new ArrayList<>();
99 for (int j = 0; j < requestsPerThread; j++) {
100 final int devicePort = parameters.sameDevice
101 ? parameters.devicePortRangeStart : parameters.devicePortRangeStart + i;
102 final StringBuilder destBuilder = new StringBuilder(DEST);
104 destBuilder.indexOf(HOST_KEY),
105 destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(),
108 destBuilder.indexOf(PORT_KEY),
109 destBuilder.indexOf(PORT_KEY) + PORT_KEY.length(),
110 parameters.port + "");
111 final StringBuilder suffixBuilder = new StringBuilder(parameters.destination);
112 if (suffixBuilder.indexOf(DEVICE_PORT_KEY) != -1) {
113 suffixBuilder.replace(
114 suffixBuilder.indexOf(DEVICE_PORT_KEY),
115 suffixBuilder.indexOf(DEVICE_PORT_KEY) + DEVICE_PORT_KEY.length(),
118 destBuilder.append(suffixBuilder);
121 new DestToPayload(destBuilder.toString(), prepareMessage(i, j, editContentString, devicePort)));
123 allThreadsPayloads.add(payloads);
126 for (int i = 0; i < leftoverRequests; i++) {
127 final int devicePort = parameters.sameDevice
128 ? parameters.devicePortRangeStart : parameters.devicePortRangeStart + threadAmount - 1;
129 final StringBuilder destBuilder = new StringBuilder(DEST);
131 destBuilder.indexOf(HOST_KEY),
132 destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(),
135 destBuilder.indexOf(PORT_KEY),
136 destBuilder.indexOf(PORT_KEY) + PORT_KEY.length(),
137 parameters.port + "");
138 final StringBuilder suffixBuilder = new StringBuilder(parameters.destination);
139 if (suffixBuilder.indexOf(DEVICE_PORT_KEY) != -1) {
140 suffixBuilder.replace(
141 suffixBuilder.indexOf(DEVICE_PORT_KEY),
142 suffixBuilder.indexOf(DEVICE_PORT_KEY) + DEVICE_PORT_KEY.length(),
145 destBuilder.append(suffixBuilder);
147 final ArrayList<DestToPayload> payloads = allThreadsPayloads.get(allThreadsPayloads.size() - 1);
150 destBuilder.toString(),
151 prepareMessage(threadAmount - 1, requestsPerThread + i, editContentString, devicePort)));
154 final ArrayList<PerfClientCallable> callables = new ArrayList<>();
155 for (ArrayList<DestToPayload> payloads : allThreadsPayloads) {
156 callables.add(new PerfClientCallable(parameters, payloads));
159 final ExecutorService executorService = Executors.newFixedThreadPool(threadAmount);
161 LOG.info("Starting performance test");
162 boolean allThreadsCompleted = true;
163 final Stopwatch started = Stopwatch.createStarted();
165 final List<Future<Void>> futures = executorService.invokeAll(
166 callables, parameters.timeout, TimeUnit.MINUTES);
167 for (int i = 0; i < futures.size(); i++) {
168 Future<Void> future = futures.get(i);
169 if (future.isCancelled()) {
170 allThreadsCompleted = false;
171 LOG.info("{}. thread timed out.", i + 1);
175 } catch (final ExecutionException e) {
176 allThreadsCompleted = false;
177 LOG.info("{}. thread failed.", i + 1, e);
181 } catch (final InterruptedException e) {
182 allThreadsCompleted = false;
183 LOG.warn("Unable to execute requests", e);
185 executorService.shutdownNow();
188 LOG.info("FINISHED. Execution time: {}", started);
189 // If some threads failed or timed out, skip calculation of requests per second value
191 if (allThreadsCompleted) {
193 "Requests per second: {}", parameters.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS));
198 private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
199 final Parameters opt = new Parameters();
201 parser.parseArgs(args, opt);
203 } catch (final ArgumentParserException e) {
204 parser.handleError(e);
211 private static String prepareMessage(final int idi, final int idj, final String editContentString,
212 final int devicePort) {
213 StringBuilder messageBuilder = new StringBuilder(editContentString);
214 if (editContentString.contains(PEER_KEY)) {
215 messageBuilder.replace(
216 messageBuilder.indexOf(PEER_KEY),
217 messageBuilder.indexOf(PEER_KEY) + PEER_KEY.length(),
218 Integer.toString(idi))
220 messageBuilder.indexOf(INT_LEAF_KEY),
221 messageBuilder.indexOf(INT_LEAF_KEY) + INT_LEAF_KEY.length(),
222 Integer.toString(idj));
225 if (messageBuilder.indexOf(DEVICE_PORT_KEY) != -1) {
226 messageBuilder.replace(
227 messageBuilder.indexOf(DEVICE_PORT_KEY),
228 messageBuilder.indexOf(DEVICE_PORT_KEY) + DEVICE_PORT_KEY.length(),
229 Integer.toString(devicePort));
232 int idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
235 messageBuilder.replace(idx, idx + PHYS_ADDR_PLACEHOLDER.length(), TestToolUtils.getMac(macStart++));
236 idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
239 return messageBuilder.toString();