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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
15 import java.io.BufferedReader;
17 import java.io.IOException;
18 import java.io.InputStreamReader;
19 import java.net.ConnectException;
21 import java.net.http.HttpClient;
22 import java.net.http.HttpRequest;
23 import java.net.http.HttpResponse;
24 import java.util.List;
25 import java.util.concurrent.Callable;
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 org.opendaylight.netconf.test.tool.config.Configuration;
34 import org.opendaylight.netconf.test.tool.config.ConfigurationBuilder;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 @SuppressFBWarnings({"DM_EXIT", "DM_DEFAULT_ENCODING", "SLF4J_LOGGER_SHOULD_BE_FINAL"})
39 public final class ScaleUtil {
40 private static final ScheduledExecutorService EXECUTOR = new LoggingWrapperExecutor(4);
41 private static final Semaphore SEMAPHORE = new Semaphore(0);
42 private static final Stopwatch STOPWATCH = Stopwatch.createUnstarted();
44 private static final long TIMEOUT = 20L;
45 private static final long RETRY_DELAY = 10L;
46 private static final int DEVICE_STEP = 1000;
48 private static ch.qos.logback.classic.Logger root;
49 private static Logger resultsLog;
54 @SuppressWarnings("checkstyle:illegalCatch")
55 public static void main(final String[] args) {
56 final TesttoolParameters params = TesttoolParameters.parseArgs(args, TesttoolParameters.getParser());
60 // cleanup at the start in case controller was already running
61 final Runtime runtime = Runtime.getRuntime();
62 cleanup(runtime, params);
65 root.warn("Starting scale test with {} devices", params.deviceCount);
66 final ScheduledFuture<?> timeoutGuardFuture = EXECUTOR.schedule(new TimeoutGuard(), TIMEOUT,
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...");
77 if (params.distroFolder == null) {
78 root.error("Distro folder is not set, exiting...");
82 root.warn(params.distroFolder.getAbsolutePath());
84 runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/start");
87 final Process exec = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/status");
90 } catch (InterruptedException e) {
91 root.warn("Failed to sleep", e);
93 status = CharStreams.toString(new BufferedReader(new InputStreamReader(exec.getInputStream())));
94 root.warn("Current status: {}", status);
95 } while (!status.startsWith("Running ..."));
96 root.warn("Doing feature install {}", params.distroFolder.getAbsolutePath()
97 + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
98 final Process featureInstall = runtime.exec(params.distroFolder.getAbsolutePath()
99 + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
101 CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getInputStream()))));
103 CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getErrorStream()))));
105 } catch (IOException e) {
106 root.warn("Failed to start karaf", e);
110 root.warn("Karaf started, starting stopwatch");
115 new ScaleVerifyCallable(netconfDeviceSimulator, params.deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
116 root.warn("First callable scheduled");
118 root.warn("semaphore released");
119 } catch (InterruptedException e) {
120 throw new RuntimeException(e);
123 timeoutGuardFuture.cancel(false);
124 params.deviceCount += DEVICE_STEP;
125 netconfDeviceSimulator.close();
128 cleanup(runtime, params);
132 private static void setUpLoggers(final TesttoolParameters params) {
133 System.setProperty("log_file_name", "scale-util.log");
135 root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
136 root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
137 resultsLog = LoggerFactory.getLogger("results");
140 private static void cleanup(final Runtime runtime, final TesttoolParameters params) {
142 stopKaraf(runtime, params);
143 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
145 } catch (IOException | InterruptedException e) {
146 root.warn("Failed to stop karaf", e);
151 private static void stopKaraf(final Runtime runtime, final TesttoolParameters params)
152 throws IOException, InterruptedException {
153 root.info("Stopping karaf and sleeping for 10 sec..");
154 String controllerPid = "";
156 final Process pgrep = runtime.exec("pgrep -f org.apache.karaf.main.Main");
158 controllerPid = CharStreams.toString(new BufferedReader(new InputStreamReader(pgrep.getInputStream())));
159 root.warn(controllerPid);
160 runtime.exec("kill -9 " + controllerPid);
162 Thread.sleep(10000L);
163 } while (!controllerPid.isEmpty());
164 deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
167 private static void deleteFolder(final File folder) {
168 File[] files = folder.listFiles();
169 if (files != null) { //some JVMs return null for empty dirs
170 for (File f : files) {
171 if (f.isDirectory()) {
175 root.warn("Failed to delete {}", f);
180 if (!folder.delete()) {
181 root.warn("Failed to delete {}", folder);
185 private static class ScaleVerifyCallable implements Callable<Void> {
186 private static final Logger LOG = LoggerFactory.getLogger(ScaleVerifyCallable.class);
188 private static final String RESTCONF_URL = "http://127.0.0.1:8181/restconf/operational/"
189 + "network-topology:network-topology/topology/topology-netconf/";
190 private static final Pattern PATTERN = Pattern.compile("connected");
192 private final HttpClient httpClient = HttpClient.newBuilder().build();
193 private final NetconfDeviceSimulator simulator;
194 private final int deviceCount;
195 private final HttpRequest request;
197 ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) {
198 LOG.info("New callable created");
199 this.simulator = simulator;
200 this.deviceCount = deviceCount;
201 request = HttpRequest.newBuilder(URI.create(RESTCONF_URL))
203 .header("Content-Type", "application/xml")
204 .header("Accept", "application/xml")
209 public Void call() throws Exception {
211 final HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
213 if (response.statusCode() != 200 && response.statusCode() != 204) {
214 LOG.warn("Request failed, status code: {}", response.statusCode());
215 EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
217 final String body = response.body();
218 final Matcher matcher = PATTERN.matcher(body);
220 while (matcher.find()) {
223 resultsLog.info("Currently connected devices : {} out of {}, time elapsed: {}",
224 count, deviceCount + 1, STOPWATCH);
225 if (count != deviceCount + 1) {
227 new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
230 resultsLog.info("All devices connected in {}", STOPWATCH);
234 } catch (ConnectException e) {
235 LOG.warn("Failed to connect to Restconf, is the controller running?", e);
236 EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
242 private static class TimeoutGuard implements Callable<Void> {
245 resultsLog.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
246 root.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
252 @SuppressWarnings("checkstyle:illegalCatch")
253 public static class LoggingWrapperExecutor extends ScheduledThreadPoolExecutor {
254 public LoggingWrapperExecutor(final int corePoolSize) {
259 public <V> ScheduledFuture<V> schedule(final Callable<V> callable, final long delay, final TimeUnit unit) {
260 return super.schedule(new LogOnExceptionCallable<>(callable), delay, unit);
263 private static class LogOnExceptionCallable<T> implements Callable<T> {
264 private final Callable<T> theCallable;
266 LogOnExceptionCallable(final Callable<T> theCallable) {
267 this.theCallable = theCallable;
273 return theCallable.call();
274 } catch (Exception e) {
276 root.warn("error in executing: " + theCallable + ". It will no longer be run!", e);
278 // rethrow so that the executor can do it's thing
279 throw new RuntimeException(e);