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