94eff3d25d8e64de4c06bf335651d7622df11ddd
[netconf.git] / netconf / tools / netconf-testtool / src / main / java / org / opendaylight / netconf / test / tool / client / http / perf / RestPerfClient.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.netconf.test.tool.client.http.perf;
10
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;
28
29 @SuppressFBWarnings("DM_EXIT")
30 public final class RestPerfClient {
31
32     private static final Logger LOG = LoggerFactory.getLogger(RestPerfClient.class);
33
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}";
37
38     private static final String PEER_KEY = "{PEERID}";
39     private static final String INT_LEAF_KEY = "{INTLEAF}";
40
41     private static final String PHYS_ADDR_PLACEHOLDER = "{PHYS_ADDR}";
42
43     private static final String DEST = "http://{HOST}:{PORT}";
44
45     private static long macStart = 0xAABBCCDD0000L;
46
47     static int throttle;
48
49     static final class DestToPayload {
50
51         private final String destination;
52         private final String payload;
53
54         DestToPayload(String destination, String payload) {
55             this.destination = destination;
56             this.payload = payload;
57         }
58
59         public String getDestination() {
60             return destination;
61         }
62
63         public String getPayload() {
64             return payload;
65         }
66     }
67
68     private RestPerfClient() {
69
70     }
71
72     public static void main(String[] args) throws IOException {
73
74         Parameters parameters = parseArgs(args, Parameters.getParser());
75         parameters.validate();
76         throttle = parameters.throttle / parameters.threadAmount;
77
78         if (parameters.async && parameters.threadAmount > 1) {
79             LOG.info("Throttling per thread: {}", throttle);
80         }
81
82         final String editContentString;
83         try {
84             editContentString = Files.toString(parameters.editContent, StandardCharsets.UTF_8);
85         } catch (final IOException e) {
86             throw new IllegalArgumentException("Cannot read content of " + parameters.editContent, e);
87         }
88
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);
95
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);
103                 destBuilder.replace(
104                         destBuilder.indexOf(HOST_KEY),
105                         destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(),
106                         parameters.ip)
107                     .replace(
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(),
116                         devicePort + "");
117                 }
118                 destBuilder.append(suffixBuilder);
119
120                 payloads.add(
121                     new DestToPayload(destBuilder.toString(), prepareMessage(i, j, editContentString, devicePort)));
122             }
123             allThreadsPayloads.add(payloads);
124         }
125
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);
130             destBuilder.replace(
131                     destBuilder.indexOf(HOST_KEY),
132                     destBuilder.indexOf(HOST_KEY) + HOST_KEY.length(),
133                     parameters.ip)
134                 .replace(
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(),
143                     devicePort + "");
144             }
145             destBuilder.append(suffixBuilder);
146
147             final ArrayList<DestToPayload> payloads = allThreadsPayloads.get(allThreadsPayloads.size() - 1);
148             payloads.add(
149                 new DestToPayload(
150                     destBuilder.toString(),
151                     prepareMessage(threadAmount - 1, requestsPerThread + i, editContentString, devicePort)));
152         }
153
154         final ArrayList<PerfClientCallable> callables = new ArrayList<>();
155         for (ArrayList<DestToPayload> payloads : allThreadsPayloads) {
156             callables.add(new PerfClientCallable(parameters, payloads));
157         }
158
159         final ExecutorService executorService = Executors.newFixedThreadPool(threadAmount);
160
161         LOG.info("Starting performance test");
162         boolean allThreadsCompleted = true;
163         final Stopwatch started = Stopwatch.createStarted();
164         try {
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);
172                 } else {
173                     try {
174                         future.get();
175                     } catch (final ExecutionException e) {
176                         allThreadsCompleted = false;
177                         LOG.info("{}. thread failed.", i + 1, e);
178                     }
179                 }
180             }
181         } catch (final InterruptedException e) {
182             allThreadsCompleted = false;
183             LOG.warn("Unable to execute requests", e);
184         }
185         executorService.shutdownNow();
186         started.stop();
187
188         LOG.info("FINISHED. Execution time: {}", started);
189         // If some threads failed or timed out, skip calculation of requests per second value
190         // and do not log it
191         if (allThreadsCompleted) {
192             LOG.info(
193                 "Requests per second: {}", parameters.editCount * 1000.0 / started.elapsed(TimeUnit.MILLISECONDS));
194         }
195         System.exit(0);
196     }
197
198     private static Parameters parseArgs(final String[] args, final ArgumentParser parser) {
199         final Parameters opt = new Parameters();
200         try {
201             parser.parseArgs(args, opt);
202             return opt;
203         } catch (final ArgumentParserException e) {
204             parser.handleError(e);
205         }
206
207         System.exit(1);
208         return null;
209     }
210
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))
219                 .replace(
220                     messageBuilder.indexOf(INT_LEAF_KEY),
221                     messageBuilder.indexOf(INT_LEAF_KEY) + INT_LEAF_KEY.length(),
222                     Integer.toString(idj));
223         }
224
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));
230         }
231
232         int idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
233
234         while (idx != -1) {
235             messageBuilder.replace(idx, idx + PHYS_ADDR_PLACEHOLDER.length(), TestToolUtils.getMac(macStart++));
236             idx = messageBuilder.indexOf(PHYS_ADDR_PLACEHOLDER);
237         }
238
239         return messageBuilder.toString();
240     }
241 }