blob: 310c96eb7717b5e9dc460bdedbc51b0a98692baf [file] [log] [blame]
Thomas Vachuskaf9c84362015-04-15 11:20:45 -07001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onlab.stc;
17
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070018import com.google.common.collect.ImmutableList;
Thomas Vachuska750ab042015-06-17 10:42:15 -070019import org.eclipse.jetty.server.Server;
20import org.eclipse.jetty.servlet.ServletHandler;
21import org.eclipse.jetty.util.log.Logger;
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070022import org.onlab.stc.Coordinator.Status;
23
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070024import java.io.FileInputStream;
25import java.io.FileNotFoundException;
26import java.text.SimpleDateFormat;
27import java.util.Date;
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070028import java.util.List;
29import java.util.Objects;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070030
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070031import static java.lang.System.currentTimeMillis;
32import static org.onlab.stc.Coordinator.Status.*;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070033import static org.onlab.stc.Coordinator.print;
34
35/**
36 * Main program for executing system test coordinator.
37 */
38public final class Main {
39
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070040 private static final String NONE = "\u001B[0m";
41 private static final String GRAY = "\u001B[30;1m";
42 private static final String RED = "\u001B[31;1m";
43 private static final String GREEN = "\u001B[32;1m";
44 private static final String BLUE = "\u001B[36m";
45
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070046 private enum Command {
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070047 LIST, RUN, RUN_RANGE, HELP
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070048 }
49
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070050 private final String scenarioFile;
51
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070052 private Command command = Command.HELP;
53 private String runFromPatterns = "";
54 private String runToPatterns = "";
55
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070056 private Coordinator coordinator;
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -070057 private Monitor monitor;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070058 private Listener delegate = new Listener();
59
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070060 private static boolean useColor = Objects.equals("true", System.getenv("stcColor"));
61
62 // usage: stc [<scenario-file>] [run]
63 // usage: stc [<scenario-file>] run [from <from-patterns>] [to <to-patterns>]]
64 // usage: stc [<scenario-file>] list
65
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070066 // Public construction forbidden
67 private Main(String[] args) {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070068 this.scenarioFile = args[0];
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070069
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070070 if (args.length <= 1 || args.length == 2 && args[1].equals("run")) {
71 command = Command.RUN;
72 } else if (args.length == 2 && args[1].equals("list")) {
73 command = Command.LIST;
74 } else if (args.length >= 4 && args[1].equals("run")) {
75 int i = 2;
76 if (args[i].equals("from")) {
77 command = Command.RUN_RANGE;
78 runFromPatterns = args[i + 1];
79 i += 2;
80 }
81
82 if (args.length >= i + 2 && args[i].equals("to")) {
83 command = Command.RUN_RANGE;
84 runToPatterns = args[i + 1];
85 }
86 }
87 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070088
89 /**
90 * Main entry point for coordinating test scenario execution.
91 *
92 * @param args command-line arguments
93 */
94 public static void main(String[] args) {
95 Main main = new Main(args);
96 main.run();
97 }
98
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070099 // Runs the scenario processing
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700100 private void run() {
101 try {
102 // Load scenario
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700103 Scenario scenario = Scenario.loadScenario(new FileInputStream(scenarioFile));
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700104
105 // Elaborate scenario
106 Compiler compiler = new Compiler(scenario);
107 compiler.compile();
108
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700109 // Setup the process flow coordinator
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700110 coordinator = new Coordinator(scenario, compiler.processFlow(),
111 compiler.logDir());
112 coordinator.addListener(delegate);
Thomas Vachuska750ab042015-06-17 10:42:15 -0700113
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700114 // Prepare the GUI monitor
115 monitor = new Monitor(coordinator, compiler);
116 startMonitorServer(monitor);
Thomas Vachuska750ab042015-06-17 10:42:15 -0700117
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700118 // Execute process flow
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700119 processCommand();
120
121 } catch (FileNotFoundException e) {
122 print("Unable to find scenario file %s", scenarioFile);
123 }
124 }
125
Thomas Vachuska750ab042015-06-17 10:42:15 -0700126 // Initiates a web-server for the monitor GUI.
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700127 private static void startMonitorServer(Monitor monitor) {
Thomas Vachuska750ab042015-06-17 10:42:15 -0700128 org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
129 Server server = new Server(9999);
130 ServletHandler handler = new ServletHandler();
131 server.setHandler(handler);
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700132 MonitorWebSocketServlet.setMonitor(monitor);
Thomas Vachuska750ab042015-06-17 10:42:15 -0700133 handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*");
134 try {
135 server.start();
136 } catch (Exception e) {
137 e.printStackTrace();
138 }
139 }
140
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700141 // Processes the appropriate command
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700142 private void processCommand() {
143 switch (command) {
144 case RUN:
145 processRun();
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700146 break;
147 case LIST:
148 processList();
149 break;
150 case RUN_RANGE:
151 processRunRange();
152 break;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700153 default:
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700154 print("Unsupported command %s", command);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700155 }
156 }
157
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700158 // Processes the scenario 'run' command.
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700159 private void processRun() {
160 try {
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700161 coordinator.reset();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700162 coordinator.start();
163 int exitCode = coordinator.waitFor();
164 pause(100); // allow stdout to flush
165 System.exit(exitCode);
166 } catch (InterruptedException e) {
167 print("Unable to execute scenario %s", scenarioFile);
168 }
169 }
170
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700171 // Processes the scenario 'list' command.
172 private void processList() {
173 coordinator.getRecords()
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700174 .forEach(event -> logStatus(event.time(), event.name(), event.status(), event.command()));
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700175 }
176
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700177 // Processes the scenario 'run' command for range of steps.
178 private void processRunRange() {
179 try {
180 coordinator.reset(list(runFromPatterns), list(runToPatterns));
181 coordinator.start();
182 int exitCode = coordinator.waitFor();
183 pause(100); // allow stdout to flush
184 System.exit(exitCode);
185 } catch (InterruptedException e) {
186 print("Unable to execute scenario %s", scenarioFile);
187 }
188 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700189
190 /**
191 * Internal delegate to monitor the process execution.
192 */
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700193 private static class Listener implements StepProcessListener {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700194 @Override
Thomas Vachuskab51b8bc2015-07-27 08:37:12 -0700195 public void onStart(Step step, String command) {
196 logStatus(currentTimeMillis(), step.name(), IN_PROGRESS, command);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700197 }
198
199 @Override
Thomas Vachuska86439372015-06-05 09:21:32 -0700200 public void onCompletion(Step step, Status status) {
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700201 logStatus(currentTimeMillis(), step.name(), status, null);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700202 }
203
204 @Override
205 public void onOutput(Step step, String line) {
206 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700207 }
208
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700209 // Logs the step status.
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700210 private static void logStatus(long time, String name, Status status, String cmd) {
211 if (cmd != null) {
212 print("%s %s%s %s%s -- %s", time(time), color(status), name, action(status), color(null), cmd);
213 } else {
214 print("%s %s%s %s%s", time(time), color(status), name, action(status), color(null));
215 }
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700216 }
217
218 // Produces a description of event using the specified step status.
219 private static String action(Status status) {
220 return status == IN_PROGRESS ? "started" :
221 (status == SUCCEEDED ? "completed" :
222 (status == FAILED ? "failed" :
223 (status == SKIPPED ? "skipped" : "waiting")));
224 }
225
226 // Produces an ANSI escape code for color using the specified step status.
227 private static String color(Status status) {
228 if (!useColor) {
229 return "";
230 }
231 return status == null ? NONE :
232 (status == IN_PROGRESS ? BLUE :
233 (status == SUCCEEDED ? GREEN :
234 (status == FAILED ? RED : GRAY)));
235 }
236
237 // Produces a list from the specified comma-separated string.
238 private static List<String> list(String patterns) {
239 return ImmutableList.copyOf(patterns.split(","));
240 }
241
242 // Produces a formatted time stamp.
243 private static String time(long time) {
244 return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date(time));
245 }
246
247 // Pauses for the specified number of millis.
248 private static void pause(int ms) {
249 try {
250 Thread.sleep(ms);
251 } catch (InterruptedException e) {
252 print("Interrupted!");
253 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700254 }
255
Thomas Vachuska750ab042015-06-17 10:42:15 -0700256 // Logger to quiet Jetty down
257 private static class NullLogger implements Logger {
258 @Override
259 public String getName() {
260 return "quiet";
261 }
262
263 @Override
264 public void warn(String msg, Object... args) {
265 }
266
267 @Override
268 public void warn(Throwable thrown) {
269 }
270
271 @Override
272 public void warn(String msg, Throwable thrown) {
273 }
274
275 @Override
276 public void info(String msg, Object... args) {
277 }
278
279 @Override
280 public void info(Throwable thrown) {
281 }
282
283 @Override
284 public void info(String msg, Throwable thrown) {
285 }
286
287 @Override
288 public boolean isDebugEnabled() {
289 return false;
290 }
291
292 @Override
293 public void setDebugEnabled(boolean enabled) {
294 }
295
296 @Override
297 public void debug(String msg, Object... args) {
298 }
299
300 @Override
301 public void debug(Throwable thrown) {
302 }
303
304 @Override
305 public void debug(String msg, Throwable thrown) {
306 }
307
308 @Override
309 public Logger getLogger(String name) {
310 return this;
311 }
312
313 @Override
314 public void ignore(Throwable ignored) {
315 }
316 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700317}