2 * Copyright (c) 2016 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;
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.io.CharStreams;
15 import com.ning.http.client.AsyncHttpClient;
16 import com.ning.http.client.AsyncHttpClientConfig.Builder;
17 import com.ning.http.client.Request;
18 import com.ning.http.client.Response;
19 import java.io.BufferedInputStream;
20 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.net.ConnectException;
25 import java.util.List;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ExecutionException;
28 import java.util.concurrent.ScheduledExecutorService;
29 import java.util.concurrent.ScheduledFuture;
30 import java.util.concurrent.ScheduledThreadPoolExecutor;
31 import java.util.concurrent.Semaphore;
32 import java.util.concurrent.TimeUnit;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35 import net.sourceforge.argparse4j.inf.ArgumentParser;
36 import net.sourceforge.argparse4j.inf.ArgumentParserException;
37 import org.opendaylight.netconf.test.tool.Main.Params;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 public class ScaleUtil {
43 private static final Logger RESULTS_LOG = LoggerFactory.getLogger("results");
44 private static final ScheduledExecutorService executor = new LoggingWrapperExecutor(4);
46 private static final int deviceStep = 1000;
47 private static final long retryDelay = 10l;
48 private static final long timeout = 20l;
50 private static final Stopwatch stopwatch = Stopwatch.createUnstarted();
52 private static ScheduledFuture timeoutGuardFuture;
53 private static ch.qos.logback.classic.Logger root;
54 private static final Semaphore semaphore = new Semaphore(0);
56 public static void main(final String[] args) {
57 final Params params = parseArgs(args, Params.getParser());
59 root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
60 root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
62 // cleanup at the start in case controller was already running
63 final Runtime runtime = Runtime.getRuntime();
64 cleanup(runtime, params);
67 root.warn("Starting scale test with {} devices", params.deviceCount);
68 timeoutGuardFuture = executor.schedule(new TimeoutGuard(), timeout, TimeUnit.MINUTES);
69 final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator();
71 final List<Integer> openDevices = netconfDeviceSimulator.start(params);
72 if (openDevices.size() == 0) {
73 root.error("Failed to start any simulated devices, exiting...");
76 if (params.distroFolder != null) {
77 final Main.ConfigGenerator configGenerator = new Main.ConfigGenerator(params.distroFolder, openDevices);
78 final List<File> generated = configGenerator.generate(
79 params.ssh, params.generateConfigBatchSize,
80 params.generateConfigsTimeout, params.generateConfigsAddress,
81 params.devicesPerPort);
82 configGenerator.updateFeatureFile(generated);
83 configGenerator.changeLoadOrder();
85 } catch (final Exception e) {
86 root.error("Unhandled exception", e);
87 netconfDeviceSimulator.close();
91 root.warn(params.distroFolder.getAbsolutePath());
93 runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/start");
96 final Process exec = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/status");
99 } catch (InterruptedException e) {
100 root.warn("Failed to sleep", e);
102 status = CharStreams.toString(new BufferedReader(new InputStreamReader(exec.getInputStream())));
103 root.warn("Current status: {}", status);
104 } while (!status.startsWith("Running ..."));
105 root.warn("Doing feature install {}", params.distroFolder.getAbsolutePath() + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
106 final Process featureInstall = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
107 root.warn(CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getInputStream()))));
108 root.warn(CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getErrorStream()))));
110 } catch (IOException e) {
111 root.warn("Failed to start karaf", e);
115 root.warn("Karaf started, starting stopwatch");
119 executor.schedule(new ScaleVerifyCallable(netconfDeviceSimulator, params.deviceCount), retryDelay, TimeUnit.SECONDS);
120 root.warn("First callable scheduled");
122 root.warn("semaphore released");
123 } catch (InterruptedException e) {
124 throw new RuntimeException(e);
127 timeoutGuardFuture.cancel(false);
128 params.deviceCount += deviceStep;
129 netconfDeviceSimulator.close();
132 cleanup(runtime, params);
136 private static void cleanup(final Runtime runtime, final Params params) {
138 stopKaraf(runtime, params);
139 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
141 } catch (IOException | InterruptedException e) {
142 root.warn("Failed to stop karaf", e);
147 private static void stopKaraf(final Runtime runtime, final Params params) throws IOException, InterruptedException {
148 root.info("Stopping karaf and sleeping for 10 sec..");
149 String controllerPid = "";
152 final Process pgrep = runtime.exec("pgrep -f org.apache.karaf.main.Main");
154 controllerPid = CharStreams.toString(new BufferedReader(new InputStreamReader(pgrep.getInputStream())));
155 root.warn(controllerPid);
156 runtime.exec("kill -9 " + controllerPid);
158 Thread.sleep(10000l);
159 } while (!controllerPid.isEmpty());
160 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
163 private static void deleteFolder(File folder) {
164 File[] files = folder.listFiles();
165 if(files!=null) { //some JVMs return null for empty dirs
167 if(f.isDirectory()) {
177 private static Params parseArgs(final String[] args, final ArgumentParser parser) {
178 final Params parameters = new Params();
180 parser.parseArgs(args, parameters);
182 } catch (ArgumentParserException e) {
183 parser.handleError(e);
190 private static class ScaleVerifyCallable implements Callable {
192 private static final Logger LOG = LoggerFactory.getLogger(ScaleVerifyCallable.class);
194 private static final String RESTCONF_URL = "http://127.0.0.1:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/";
195 private static final Pattern PATTERN = Pattern.compile("connected");
197 private final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(new Builder()
198 .setConnectTimeout(Integer.MAX_VALUE)
199 .setRequestTimeout(Integer.MAX_VALUE)
200 .setAllowPoolingConnections(true)
202 private final NetconfDeviceSimulator simulator;
203 private final int deviceCount;
204 private final Request request;
206 public ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) {
207 LOG.info("New callable created");
208 this.simulator = simulator;
209 this.deviceCount = deviceCount;
210 AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(RESTCONF_URL)
211 .addHeader("content-type", "application/xml")
212 .addHeader("Accept", "application/xml")
213 .setRequestTimeout(Integer.MAX_VALUE);
214 request = requestBuilder.build();
218 public Object call() throws Exception {
220 final Response response = asyncHttpClient.executeRequest(request).get();
222 if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
223 LOG.warn("Request failed, status code: {}", response.getStatusCode() + response.getStatusText());
224 executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
226 final String body = response.getResponseBody();
227 final Matcher matcher = PATTERN.matcher(body);
229 while (matcher.find()) {
232 RESULTS_LOG.info("Currently connected devices : {} out of {}, time elapsed: {}", count, deviceCount + 1, stopwatch);
233 if (count != deviceCount + 1) {
234 executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
237 RESULTS_LOG.info("All devices connected in {}", stopwatch);
241 } catch (ConnectException | ExecutionException e) {
242 LOG.warn("Failed to connect to Restconf, is the controller running?", e);
243 executor.schedule(new ScaleVerifyCallable(simulator, deviceCount), retryDelay, TimeUnit.SECONDS);
249 private static class TimeoutGuard implements Callable {
252 public Object call() throws Exception {
253 RESULTS_LOG.warn("Timeout for scale test reached after: {} ..aborting", stopwatch);
254 root.warn("Timeout for scale test reached after: {} ..aborting", stopwatch);
260 public static class LoggingWrapperExecutor extends ScheduledThreadPoolExecutor {
262 public LoggingWrapperExecutor(int corePoolSize) {
267 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
268 return super.schedule(wrapCallable(callable), delay, unit);
271 private Callable wrapCallable(Callable callable) {
272 return new LogOnExceptionCallable(callable);
275 private class LogOnExceptionCallable implements Callable {
276 private Callable theCallable;
278 public LogOnExceptionCallable(Callable theCallable) {
280 this.theCallable = theCallable;
284 public Object call() throws Exception {
288 } catch (Exception e) {
290 root.warn("error in executing: " + theCallable + ". It will no longer be run!", e);
292 // rethrow so that the executor can do it's thing
293 throw new RuntimeException(e);