Add 'features/protocol-framework/' from commit 'cb42405784db97d0ce2c5991d12a89b46d185949'
[netconf.git] / netconf / tools / netconf-testtool / src / main / java / org / opendaylight / netconf / test / tool / ScaleUtil.java
1 /*
2  * Copyright (c) 2016 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;
10
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;
19 import java.io.File;
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.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class ScaleUtil {
39     private static final ScheduledExecutorService EXECUTOR = new LoggingWrapperExecutor(4);
40     private static final Semaphore SEMAPHORE = new Semaphore(0);
41     private static final Stopwatch STOPWATCH = Stopwatch.createUnstarted();
42
43     private static final long TIMEOUT = 20L;
44     private static final long RETRY_DELAY = 10L;
45     private static final int DEVICE_STEP = 1000;
46
47     private static ch.qos.logback.classic.Logger root;
48     private static Logger resultsLog;
49
50     @SuppressWarnings("checkstyle:illegalCatch")
51     public static void main(final String[] args) {
52         final TesttoolParameters params = TesttoolParameters.parseArgs(args, TesttoolParameters.getParser());
53
54         setUpLoggers(params);
55
56         // cleanup at the start in case controller was already running
57         final Runtime runtime = Runtime.getRuntime();
58         cleanup(runtime, params);
59
60         while (true) {
61             root.warn("Starting scale test with {} devices", params.deviceCount);
62             final ScheduledFuture timeoutGuardFuture = EXECUTOR.schedule(new TimeoutGuard(), TIMEOUT, TimeUnit.MINUTES);
63             final NetconfDeviceSimulator netconfDeviceSimulator = new NetconfDeviceSimulator(params.threadAmount);
64             try {
65                 final List<Integer> openDevices = netconfDeviceSimulator.start(params);
66                 if (openDevices.size() == 0) {
67                     root.error("Failed to start any simulated devices, exiting...");
68                     System.exit(1);
69                 }
70                 if (params.distroFolder != null) {
71                     final Main.ConfigGenerator configGenerator = new Main.ConfigGenerator(
72                         params.distroFolder, openDevices);
73                     final List<File> generated = configGenerator.generate(
74                             params.ssh, params.generateConfigBatchSize,
75                             params.generateConfigsTimeout, params.generateConfigsAddress,
76                             params.devicesPerPort);
77                     configGenerator.updateFeatureFile(generated);
78                     configGenerator.changeLoadOrder();
79                 }
80             } catch (final Exception e) {
81                 root.error("Unhandled exception", e);
82                 netconfDeviceSimulator.close();
83                 System.exit(1);
84             }
85
86             root.warn(params.distroFolder.getAbsolutePath());
87             try {
88                 runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/start");
89                 String status;
90                 do {
91                     final Process exec = runtime.exec(params.distroFolder.getAbsolutePath() + "/bin/status");
92                     try {
93                         Thread.sleep(2000L);
94                     } catch (InterruptedException e) {
95                         root.warn("Failed to sleep", e);
96                     }
97                     status = CharStreams.toString(new BufferedReader(new InputStreamReader(exec.getInputStream())));
98                     root.warn("Current status: {}", status);
99                 } while (!status.startsWith("Running ..."));
100                 root.warn("Doing feature install {}", params.distroFolder.getAbsolutePath()
101                     + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
102                 final Process featureInstall = runtime.exec(params.distroFolder.getAbsolutePath()
103                     + "/bin/client -u karaf feature:install odl-restconf-noauth odl-netconf-connector-all");
104                 root.warn(
105                     CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getInputStream()))));
106                 root.warn(
107                     CharStreams.toString(new BufferedReader(new InputStreamReader(featureInstall.getErrorStream()))));
108
109             } catch (IOException e) {
110                 root.warn("Failed to start karaf", e);
111                 System.exit(1);
112             }
113
114             root.warn("Karaf started, starting stopwatch");
115             STOPWATCH.start();
116
117             try {
118                 EXECUTOR.schedule(
119                     new ScaleVerifyCallable(netconfDeviceSimulator, params.deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
120                 root.warn("First callable scheduled");
121                 SEMAPHORE.acquire();
122                 root.warn("semaphore released");
123             } catch (InterruptedException e) {
124                 throw new RuntimeException(e);
125             }
126
127             timeoutGuardFuture.cancel(false);
128             params.deviceCount += DEVICE_STEP;
129             netconfDeviceSimulator.close();
130             STOPWATCH.reset();
131
132             cleanup(runtime, params);
133         }
134     }
135
136     private static void setUpLoggers(final TesttoolParameters params) {
137         System.setProperty("log_file_name", "scale-util.log");
138
139         root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
140         root.setLevel(params.debug ? Level.DEBUG : Level.INFO);
141         resultsLog = LoggerFactory.getLogger("results");
142     }
143
144     private static void cleanup(final Runtime runtime, final TesttoolParameters params) {
145         try {
146             stopKaraf(runtime, params);
147             deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
148
149         } catch (IOException | InterruptedException e) {
150             root.warn("Failed to stop karaf", e);
151             System.exit(1);
152         }
153     }
154
155     private static void stopKaraf(final Runtime runtime, final TesttoolParameters params)
156             throws IOException, InterruptedException {
157         root.info("Stopping karaf and sleeping for 10 sec..");
158         String controllerPid = "";
159         do {
160             final Process pgrep = runtime.exec("pgrep -f org.apache.karaf.main.Main");
161
162             controllerPid = CharStreams.toString(new BufferedReader(new InputStreamReader(pgrep.getInputStream())));
163             root.warn(controllerPid);
164             runtime.exec("kill -9 " + controllerPid);
165
166             Thread.sleep(10000L);
167         } while (!controllerPid.isEmpty());
168         deleteFolder(new File(params.distroFolder.getAbsoluteFile() + "/data"));
169     }
170
171     private static void deleteFolder(File folder) {
172         File[] files = folder.listFiles();
173         if (files != null) { //some JVMs return null for empty dirs
174             for (File f : files) {
175                 if (f.isDirectory()) {
176                     deleteFolder(f);
177                 } else {
178                     f.delete();
179                 }
180             }
181         }
182         folder.delete();
183     }
184
185     private static TesttoolParameters parseArgs(final String[] args, final ArgumentParser parser) {
186         final TesttoolParameters parameters = new TesttoolParameters();
187         try {
188             parser.parseArgs(args, parameters);
189             return parameters;
190         } catch (ArgumentParserException e) {
191             parser.handleError(e);
192         }
193
194         System.exit(1);
195         return null;
196     }
197
198     private static class ScaleVerifyCallable implements Callable {
199         private static final Logger LOG = LoggerFactory.getLogger(ScaleVerifyCallable.class);
200
201         private static final String RESTCONF_URL
202                 = "http://127.0.0.1:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/";
203         private static final Pattern PATTERN = Pattern.compile("connected");
204
205         private final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(new Builder()
206                 .setConnectTimeout(Integer.MAX_VALUE)
207                 .setRequestTimeout(Integer.MAX_VALUE)
208                 .setAllowPoolingConnections(true)
209                 .build());
210         private final NetconfDeviceSimulator simulator;
211         private final int deviceCount;
212         private final Request request;
213
214         ScaleVerifyCallable(final NetconfDeviceSimulator simulator, final int deviceCount) {
215             LOG.info("New callable created");
216             this.simulator = simulator;
217             this.deviceCount = deviceCount;
218             AsyncHttpClient.BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(RESTCONF_URL)
219                     .addHeader("content-type", "application/xml")
220                     .addHeader("Accept", "application/xml")
221                     .setRequestTimeout(Integer.MAX_VALUE);
222             request = requestBuilder.build();
223         }
224
225         @Override
226         public Object call() throws Exception {
227             try {
228                 final Response response = asyncHttpClient.executeRequest(request).get();
229
230                 if (response.getStatusCode() != 200 && response.getStatusCode() != 204) {
231                     LOG.warn("Request failed, status code: {}", response.getStatusCode() + response.getStatusText());
232                     EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
233                 } else {
234                     final String body = response.getResponseBody();
235                     final Matcher matcher = PATTERN.matcher(body);
236                     int count = 0;
237                     while (matcher.find()) {
238                         count++;
239                     }
240                     resultsLog.info("Currently connected devices : {} out of {}, time elapsed: {}",
241                         count, deviceCount + 1, STOPWATCH);
242                     if (count != deviceCount + 1) {
243                         EXECUTOR.schedule(
244                             new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
245                     } else {
246                         STOPWATCH.stop();
247                         resultsLog.info("All devices connected in {}", STOPWATCH);
248                         SEMAPHORE.release();
249                     }
250                 }
251             } catch (ConnectException | ExecutionException e) {
252                 LOG.warn("Failed to connect to Restconf, is the controller running?", e);
253                 EXECUTOR.schedule(new ScaleVerifyCallable(simulator, deviceCount), RETRY_DELAY, TimeUnit.SECONDS);
254             }
255             return null;
256         }
257     }
258
259     private static class TimeoutGuard implements Callable {
260         @Override
261         public Object call() throws Exception {
262             resultsLog.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
263             root.warn("Timeout for scale test reached after: {} ..aborting", STOPWATCH);
264             System.exit(0);
265             return null;
266         }
267     }
268
269     @SuppressWarnings("checkstyle:illegalCatch")
270     public static class LoggingWrapperExecutor extends ScheduledThreadPoolExecutor {
271         public LoggingWrapperExecutor(int corePoolSize) {
272             super(corePoolSize);
273         }
274
275         @Override
276         public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
277             return super.schedule(wrapCallable(callable), delay, unit);
278         }
279
280         private Callable wrapCallable(Callable callable) {
281             return new LogOnExceptionCallable(callable);
282         }
283
284         private class LogOnExceptionCallable implements Callable {
285             private Callable theCallable;
286
287             LogOnExceptionCallable(Callable theCallable) {
288                 super();
289                 this.theCallable = theCallable;
290             }
291
292             @Override
293             public Object call() throws Exception {
294                 try {
295                     theCallable.call();
296                     return null;
297                 } catch (Exception e) {
298                     // log
299                     root.warn("error in executing: " + theCallable + ". It will no longer be run!", e);
300
301                     // rethrow so that the executor can do it's thing
302                     throw new RuntimeException(e);
303                 }
304             }
305         }
306     }
307 }