blob: 01ebe36524e4cb03e71e1546704309e043003bfa [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;
57 private Listener delegate = new Listener();
58
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070059 private static boolean useColor = Objects.equals("true", System.getenv("stcColor"));
60
61 // usage: stc [<scenario-file>] [run]
62 // usage: stc [<scenario-file>] run [from <from-patterns>] [to <to-patterns>]]
63 // usage: stc [<scenario-file>] list
64
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070065 // Public construction forbidden
66 private Main(String[] args) {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070067 this.scenarioFile = args[0];
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070068
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070069 if (args.length <= 1 || args.length == 2 && args[1].equals("run")) {
70 command = Command.RUN;
71 } else if (args.length == 2 && args[1].equals("list")) {
72 command = Command.LIST;
73 } else if (args.length >= 4 && args[1].equals("run")) {
74 int i = 2;
75 if (args[i].equals("from")) {
76 command = Command.RUN_RANGE;
77 runFromPatterns = args[i + 1];
78 i += 2;
79 }
80
81 if (args.length >= i + 2 && args[i].equals("to")) {
82 command = Command.RUN_RANGE;
83 runToPatterns = args[i + 1];
84 }
85 }
86 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070087
88 /**
89 * Main entry point for coordinating test scenario execution.
90 *
91 * @param args command-line arguments
92 */
93 public static void main(String[] args) {
94 Main main = new Main(args);
95 main.run();
96 }
97
Thomas Vachuska50ec1af2015-06-02 00:42:52 -070098 // Runs the scenario processing
Thomas Vachuskaf9c84362015-04-15 11:20:45 -070099 private void run() {
100 try {
101 // Load scenario
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700102 Scenario scenario = Scenario.loadScenario(new FileInputStream(scenarioFile));
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700103
104 // Elaborate scenario
105 Compiler compiler = new Compiler(scenario);
106 compiler.compile();
107
108 // Execute process flow
109 coordinator = new Coordinator(scenario, compiler.processFlow(),
110 compiler.logDir());
111 coordinator.addListener(delegate);
Thomas Vachuska750ab042015-06-17 10:42:15 -0700112
113 startMonitorServer();
114
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700115 processCommand();
116
117 } catch (FileNotFoundException e) {
118 print("Unable to find scenario file %s", scenarioFile);
119 }
120 }
121
Thomas Vachuska750ab042015-06-17 10:42:15 -0700122 // Initiates a web-server for the monitor GUI.
123 private static void startMonitorServer() {
124 org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
125 Server server = new Server(9999);
126 ServletHandler handler = new ServletHandler();
127 server.setHandler(handler);
128 handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*");
129 try {
130 server.start();
131 } catch (Exception e) {
132 e.printStackTrace();
133 }
134 }
135
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700136 // Processes the appropriate command
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700137 private void processCommand() {
138 switch (command) {
139 case RUN:
140 processRun();
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700141 break;
142 case LIST:
143 processList();
144 break;
145 case RUN_RANGE:
146 processRunRange();
147 break;
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700148 default:
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700149 print("Unsupported command %s", command);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700150 }
151 }
152
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700153 // Processes the scenario 'run' command.
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700154 private void processRun() {
155 try {
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700156 coordinator.reset();
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700157 coordinator.start();
158 int exitCode = coordinator.waitFor();
159 pause(100); // allow stdout to flush
160 System.exit(exitCode);
161 } catch (InterruptedException e) {
162 print("Unable to execute scenario %s", scenarioFile);
163 }
164 }
165
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700166 // Processes the scenario 'list' command.
167 private void processList() {
168 coordinator.getRecords()
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700169 .forEach(event -> logStatus(event.time(), event.name(), event.status(), event.command()));
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700170 }
171
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700172 // Processes the scenario 'run' command for range of steps.
173 private void processRunRange() {
174 try {
175 coordinator.reset(list(runFromPatterns), list(runToPatterns));
176 coordinator.start();
177 int exitCode = coordinator.waitFor();
178 pause(100); // allow stdout to flush
179 System.exit(exitCode);
180 } catch (InterruptedException e) {
181 print("Unable to execute scenario %s", scenarioFile);
182 }
183 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700184
185 /**
186 * Internal delegate to monitor the process execution.
187 */
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700188 private static class Listener implements StepProcessListener {
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700189 @Override
190 public void onStart(Step step) {
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700191 logStatus(currentTimeMillis(), step.name(), IN_PROGRESS, step.command());
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700192 }
193
194 @Override
Thomas Vachuska86439372015-06-05 09:21:32 -0700195 public void onCompletion(Step step, Status status) {
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700196 logStatus(currentTimeMillis(), step.name(), status, null);
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700197 }
198
199 @Override
200 public void onOutput(Step step, String line) {
201 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700202 }
203
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700204 // Logs the step status.
Thomas Vachuska0ec6ff42015-07-17 11:00:02 -0700205 private static void logStatus(long time, String name, Status status, String cmd) {
206 if (cmd != null) {
207 print("%s %s%s %s%s -- %s", time(time), color(status), name, action(status), color(null), cmd);
208 } else {
209 print("%s %s%s %s%s", time(time), color(status), name, action(status), color(null));
210 }
Thomas Vachuska50ec1af2015-06-02 00:42:52 -0700211 }
212
213 // Produces a description of event using the specified step status.
214 private static String action(Status status) {
215 return status == IN_PROGRESS ? "started" :
216 (status == SUCCEEDED ? "completed" :
217 (status == FAILED ? "failed" :
218 (status == SKIPPED ? "skipped" : "waiting")));
219 }
220
221 // Produces an ANSI escape code for color using the specified step status.
222 private static String color(Status status) {
223 if (!useColor) {
224 return "";
225 }
226 return status == null ? NONE :
227 (status == IN_PROGRESS ? BLUE :
228 (status == SUCCEEDED ? GREEN :
229 (status == FAILED ? RED : GRAY)));
230 }
231
232 // Produces a list from the specified comma-separated string.
233 private static List<String> list(String patterns) {
234 return ImmutableList.copyOf(patterns.split(","));
235 }
236
237 // Produces a formatted time stamp.
238 private static String time(long time) {
239 return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date(time));
240 }
241
242 // Pauses for the specified number of millis.
243 private static void pause(int ms) {
244 try {
245 Thread.sleep(ms);
246 } catch (InterruptedException e) {
247 print("Interrupted!");
248 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700249 }
250
Thomas Vachuska750ab042015-06-17 10:42:15 -0700251 // Logger to quiet Jetty down
252 private static class NullLogger implements Logger {
253 @Override
254 public String getName() {
255 return "quiet";
256 }
257
258 @Override
259 public void warn(String msg, Object... args) {
260 }
261
262 @Override
263 public void warn(Throwable thrown) {
264 }
265
266 @Override
267 public void warn(String msg, Throwable thrown) {
268 }
269
270 @Override
271 public void info(String msg, Object... args) {
272 }
273
274 @Override
275 public void info(Throwable thrown) {
276 }
277
278 @Override
279 public void info(String msg, Throwable thrown) {
280 }
281
282 @Override
283 public boolean isDebugEnabled() {
284 return false;
285 }
286
287 @Override
288 public void setDebugEnabled(boolean enabled) {
289 }
290
291 @Override
292 public void debug(String msg, Object... args) {
293 }
294
295 @Override
296 public void debug(Throwable thrown) {
297 }
298
299 @Override
300 public void debug(String msg, Throwable thrown) {
301 }
302
303 @Override
304 public Logger getLogger(String name) {
305 return this;
306 }
307
308 @Override
309 public void ignore(Throwable ignored) {
310 }
311 }
Thomas Vachuskaf9c84362015-04-15 11:20:45 -0700312}