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.Stopwatch;
13 import com.google.common.io.CharStreams;
14 import com.ning.http.client.AsyncHttpClient;
15 import com.ning.http.client.AsyncHttpClientConfig.Builder;
16 import com.ning.http.client.Request;
17 import com.ning.http.client.Response;
18 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.InputStreamReader;
22 import java.net.ConnectException;
23 import java.util.List;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.ExecutionException;
26 import java.util.concurrent.ScheduledExecutorService;
27 import java.util.concurrent.ScheduledFuture;
28 import java.util.concurrent.ScheduledThreadPoolExecutor;
29 import java.util.concurrent.Semaphore;
30 import java.util.concurrent.TimeUnit;
31 import java.util.regex.Matcher;
32 import java.util.regex.Pattern;
33 import net.sourceforge.argparse4j.inf.ArgumentParser;
34 import net.sourceforge.argparse4j.inf.ArgumentParserException;
35 import org.opendaylight.netconf.test.tool.config.Configuration;
36 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 public final class ScaleUtil {
41 private static final ScheduledExecutorService EXECUTOR = new LoggingWrapperExecutor(4);
42 private static final Semaphore SEMAPHORE = new Semaphore(0);
43 private static final Stopwatch STOPWATCH = Stopwatch.createUnstarted();
45 private static final long TIMEOUT = 20L;
46 private static final long RETRY_DELAY = 10L;
47 private static final int DEVICE_STEP = 1000;
49 private static ch.qos.logback.classic.Logger root;
50 private static Logger resultsLog;
55 @SuppressWarnings("checkstyle:illegalCatch")
56 public static void main(final String[] args) {
57 final TesttoolParameters params = TesttoolParameters.parseArgs(args, TesttoolParameters.getParser());
61 // cleanup at the start in case controller was already running
62 final Runtime runtime = Runtime.getRuntime();
63 cleanup(runtime, params);
66 root.warn("Starting scale test with {} devices", params.deviceCount);
67 final ScheduledFuture timeoutGuardFuture = EXECUTOR.schedule(new TimeoutGuard(), TIMEOUT, TimeUnit.MINUTES);
68 final Configuration configuration = new ConfigurationBuilder().from(params).build();
69 final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(configuration);
71 final List<Integer> openDevices = netconfDeviceSimulator.start();
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(
78 params.distroFolder, openDevices);
79 final List<File> generated = configGenerator.generate(
80 params.ssh, params.generateConfigBatchSize,
81 params.generateConfigsTimeout, params.generateConfigsAddress,
82 params.devicesPerPort);
83 configGenerator.updateFeatureFile(generated);
84 configGenerator.changeLoadOrder();
86 } catch (final Exception e) {
87 root.error("Unhandled exception", e);
88 netconfDeviceSimulator.close();
92 root.warn(params.distroFolder.getAbsolutePath());
94 runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/start");
97 final Process exec = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/status");
100 } catch (InterruptedException e) {
101 root.warn("Failed to sleep", e);
103 status = CharStreams.toString(new BufferedReader(new InputStreamReader(exec.getInputStream())));
104 root.warn("Current status: {}", status);
105 } while (!status.startsWith("Running ..."));
106 root.warn("Doing feature install {}", params.distroFolder.getAbsolutePath()
107 + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
108 final Process featureInstall = runtime.exec(params.distroFolder.getAbsolutePath()
109 + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
111 CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getInputStream()))));
113 CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getErrorStream()))));
115 } catch (IOException e) {
116 root.warn("Failed to start karaf", e);
120 root.warn("Karaf started, starting stopwatch");
125 new ScaleVerifyCallable(netconfDeviceSimulator, params.deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
126 root.warn("First callable scheduled");
128 root.warn("semaphore released");
129 } catch (InterruptedException e) {
130 throw new RuntimeException(e);
133 timeoutGuardFuture.cancel(false);
134 params.deviceCount += DEVICE_STEP;
135 netconfDeviceSimulator.close();
138 cleanup(runtime, params);
142 private static void setUpLoggers(final TesttoolParameters params) {
143 System.setProperty("log_file_name", "scale-util.log");
145 root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
146 root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
147 resultsLog = LoggerFactory.getLogger("results");
150 private static void cleanup(final Runtime runtime, final TesttoolParameters params) {
152 stopKaraf(runtime, params);
153 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
155 } catch (IOException | InterruptedException e) {
156 root.warn("Failed to stop karaf", e);
161 private static void stopKaraf(final Runtime runtime, final TesttoolParameters params)
162 throws IOException, InterruptedException {
163 root.info("Stopping karaf and sleeping for 10 sec..");
164 String controllerPid = "";
166 final Process pgrep = runtime.exec("pgrep -f org.apache.karaf.main.Main");
168 controllerPid = CharStreams.toString(new BufferedReader(new InputStreamReader(pgrep.getInputStream())));
169 root.warn(controllerPid);
170 runtime.exec("kill -9 " + controllerPid);
172 Thread.sleep(10000L);
173 } while (!controllerPid.isEmpty());
174 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
177 private static void deleteFolder(File folder) {
178 File[] files = folder.listFiles();
179 if (files != null) { //some JVMs return null for empty dirs
180 for (File f : files) {
181 if (f.isDirectory()) {
191 private static TesttoolParameters parseArgs(final String[] args, final ArgumentParser parser) {
192 final TesttoolParameters parameters = new TesttoolParameters();
194 parser.parseArgs(args, parameters);
196 } catch (ArgumentParserException e) {
197 parser.handleError(e);
204 private static class ScaleVerifyCallable implements Callable {
205 private static final Logger LOG = LoggerFactory.getLogger(ScaleVerifyCallable.class);
207 private static final String RESTCONF_URL
208 = "http://127.0.0.1:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/";
209 private static final Pattern PATTERN = Pattern.compile("connected");
211 private final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(new Builder()
212 .setConnectTimeout(Integer.MAX_VALUE)
213 .setRequestTimeout(Integer.MAX_VALUE)
214 .setAllowPoolingConnections(true)
216 private final NetconfDeviceSimulator simulator;
217 private final int deviceCount;
218 private final Request request;
220 ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) {
221 LOG.info("New callable created");
222 this.simulator = simulator;
223 this.deviceCount = deviceCount;
224 AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(RESTCONF_URL)
225 .addHeader("content-type", "application/xml")
226 .addHeader("Accept", "application/xml")
227 .setRequestTimeout(Integer.MAX_VALUE);
228 request = requestBuilder.build();
232 public Object call() throws Exception {
234 final Response response = asyncHttpClient.executeRequest(request).get();
236 if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
237 LOG.warn("Request failed, status code: {}", response.getStatusCode() + response.getStatusText());
238 EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
240 final String body = response.getResponseBody();
241 final Matcher matcher = PATTERN.matcher(body);
243 while (matcher.find()) {
246 resultsLog.info("Currently connected devices : {} out of {}, time elapsed: {}",
247 count, deviceCount + 1, STOPWATCH);
248 if (count != deviceCount + 1) {
250 new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
253 resultsLog.info("All devices connected in {}", STOPWATCH);
257 } catch (ConnectException | ExecutionException e) {
258 LOG.warn("Failed to connect to Restconf, is the controller running?", e);
259 EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
265 private static class TimeoutGuard implements Callable {
267 public Object call() throws Exception {
268 resultsLog.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
269 root.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
275 @SuppressWarnings("checkstyle:illegalCatch")
276 public static class LoggingWrapperExecutor extends ScheduledThreadPoolExecutor {
277 public LoggingWrapperExecutor(int corePoolSize) {
282 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
283 return super.schedule(wrapCallable(callable), delay, unit);
286 private Callable wrapCallable(Callable callable) {
287 return new LogOnExceptionCallable(callable);
290 private class LogOnExceptionCallable implements Callable {
291 private Callable theCallable;
293 LogOnExceptionCallable(Callable theCallable) {
294 this.theCallable = theCallable;
298 public Object call() throws Exception {
302 } catch (Exception e) {
304 root.warn("error in executing: " + theCallable + ". It will no longer be run!", e);
306 // rethrow so that the executor can do it's thing
307 throw new RuntimeException(e);