aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java')
-rw-r--r--framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java353
1 files changed, 353 insertions, 0 deletions
diff --git a/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java b/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java
new file mode 100644
index 00000000..09b89456
--- /dev/null
+++ b/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Main.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onlab.stc;
+
+import com.google.common.collect.ImmutableList;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletHandler;
+import org.eclipse.jetty.util.log.Logger;
+import org.onlab.stc.Coordinator.Status;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static java.lang.System.currentTimeMillis;
+import static org.onlab.stc.Coordinator.Status.*;
+import static org.onlab.stc.Coordinator.print;
+
+/**
+ * Main program for executing system test coordinator.
+ */
+public final class Main {
+
+ private static final String NONE = "\u001B[0m";
+ private static final String GRAY = "\u001B[30;1m";
+ private static final String RED = "\u001B[31;1m";
+ private static final String GREEN = "\u001B[32;1m";
+ private static final String BLUE = "\u001B[36m";
+
+ private static final String SUCCESS_SUMMARY = "%sPassed! %d steps succeeded%s";
+ private static final String MIXED_SUMMARY =
+ "%s%d steps succeeded; %s%d steps failed; %s%d steps skipped%s";
+ private static final String FAILURE_SUMMARY = "%sFailed! " + MIXED_SUMMARY;
+ private static final String ABORTED_SUMMARY = "%sAborted! " + MIXED_SUMMARY;
+
+ private boolean isReported = false;
+
+ private enum Command {
+ LIST, RUN, RUN_RANGE, HELP
+ }
+
+ private final String scenarioFile;
+
+ private Command command = Command.HELP;
+ private String runFromPatterns = "";
+ private String runToPatterns = "";
+
+ private Coordinator coordinator;
+ private Monitor monitor;
+ private Listener delegate = new Listener();
+
+ private static boolean useColor = Objects.equals("true", System.getenv("stcColor"));
+
+ // usage: stc [<scenario-file>] [run]
+ // usage: stc [<scenario-file>] run [from <from-patterns>] [to <to-patterns>]]
+ // usage: stc [<scenario-file>] list
+
+ // Public construction forbidden
+ private Main(String[] args) {
+ this.scenarioFile = args[0];
+
+ if (args.length <= 1 || args.length == 2 && args[1].equals("run")) {
+ command = Command.RUN;
+ } else if (args.length == 2 && args[1].equals("list")) {
+ command = Command.LIST;
+ } else if (args.length >= 4 && args[1].equals("run")) {
+ int i = 2;
+ if (args[i].equals("from")) {
+ command = Command.RUN_RANGE;
+ runFromPatterns = args[i + 1];
+ i += 2;
+ }
+
+ if (args.length >= i + 2 && args[i].equals("to")) {
+ command = Command.RUN_RANGE;
+ runToPatterns = args[i + 1];
+ }
+ }
+ }
+
+ /**
+ * Main entry point for coordinating test scenario execution.
+ *
+ * @param args command-line arguments
+ */
+ public static void main(String[] args) {
+ Main main = new Main(args);
+ main.run();
+ }
+
+ // Runs the scenario processing
+ private void run() {
+ try {
+ // Load scenario
+ Scenario scenario = Scenario.loadScenario(new FileInputStream(scenarioFile));
+
+ // Elaborate scenario
+ Compiler compiler = new Compiler(scenario);
+ compiler.compile();
+
+ // Setup the process flow coordinator
+ coordinator = new Coordinator(scenario, compiler.processFlow(),
+ compiler.logDir());
+ coordinator.addListener(delegate);
+
+ // Prepare the GUI monitor
+ monitor = new Monitor(coordinator, compiler);
+ startMonitorServer(monitor);
+
+ // Execute process flow
+ processCommand();
+
+ } catch (FileNotFoundException e) {
+ print("Unable to find scenario file %s", scenarioFile);
+ }
+ }
+
+ // Initiates a web-server for the monitor GUI.
+ private static void startMonitorServer(Monitor monitor) {
+ org.eclipse.jetty.util.log.Log.setLog(new NullLogger());
+ Server server = new Server(9999);
+ ServletHandler handler = new ServletHandler();
+ server.setHandler(handler);
+ MonitorWebSocketServlet.setMonitor(monitor);
+ handler.addServletWithMapping(MonitorWebSocketServlet.class, "/*");
+ try {
+ server.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ // Processes the appropriate command
+ private void processCommand() {
+ switch (command) {
+ case RUN:
+ processRun();
+ break;
+ case LIST:
+ processList();
+ break;
+ case RUN_RANGE:
+ processRunRange();
+ break;
+ default:
+ print("Unsupported command %s", command);
+ }
+ }
+
+ // Processes the scenario 'run' command.
+ private void processRun() {
+ coordinator.reset();
+ runCoordinator();
+ }
+
+ // Processes the scenario 'run' command for range of steps.
+ private void processRunRange() {
+ coordinator.reset(list(runFromPatterns), list(runToPatterns));
+ runCoordinator();
+ }
+
+ // Processes the scenario 'list' command.
+ private void processList() {
+ coordinator.getRecords()
+ .forEach(event -> logStatus(event.time(), event.name(), event.status(), event.command()));
+ System.exit(0);
+ }
+
+ // Runs the coordinator and waits for it to finish.
+ private void runCoordinator() {
+ try {
+ Runtime.getRuntime().addShutdownHook(new ShutdownHook());
+ coordinator.start();
+ int exitCode = coordinator.waitFor();
+ pause(100); // allow stdout to flush
+ printSummary(exitCode, false);
+ System.exit(exitCode);
+ } catch (InterruptedException e) {
+ print("Unable to execute scenario %s", scenarioFile);
+ }
+ }
+
+ private synchronized void printSummary(int exitCode, boolean isAborted) {
+ if (!isReported) {
+ isReported = true;
+ Set<Step> steps = coordinator.getSteps();
+ int count = steps.size();
+ if (exitCode == 0) {
+ print(SUCCESS_SUMMARY, color(SUCCEEDED), count, color(null));
+ } else {
+ long success = steps.stream().filter(s -> coordinator.getStatus(s) == SUCCEEDED).count();
+ long failed = steps.stream().filter(s -> coordinator.getStatus(s) == FAILED).count();
+ long skipped = steps.stream().filter(s -> coordinator.getStatus(s) == SKIPPED).count();
+ print(isAborted ? ABORTED_SUMMARY : FAILURE_SUMMARY,
+ color(FAILED), color(SUCCEEDED), success,
+ color(FAILED), failed, color(SKIPPED), skipped, color(null));
+ }
+ }
+ }
+
+ /**
+ * Internal delegate to monitor the process execution.
+ */
+ private static class Listener implements StepProcessListener {
+ @Override
+ public void onStart(Step step, String command) {
+ logStatus(currentTimeMillis(), step.name(), IN_PROGRESS, command);
+ }
+
+ @Override
+ public void onCompletion(Step step, Status status) {
+ logStatus(currentTimeMillis(), step.name(), status, null);
+ }
+
+ @Override
+ public void onOutput(Step step, String line) {
+ }
+ }
+
+ // Logs the step status.
+ private static void logStatus(long time, String name, Status status, String cmd) {
+ if (cmd != null) {
+ print("%s %s%s %s%s -- %s", time(time), color(status), name, action(status), color(null), cmd);
+ } else {
+ print("%s %s%s %s%s", time(time), color(status), name, action(status), color(null));
+ }
+ }
+
+ // Produces a description of event using the specified step status.
+ private static String action(Status status) {
+ return status == IN_PROGRESS ? "started" :
+ (status == SUCCEEDED ? "completed" :
+ (status == FAILED ? "failed" :
+ (status == SKIPPED ? "skipped" : "waiting")));
+ }
+
+ // Produces an ANSI escape code for color using the specified step status.
+ private static String color(Status status) {
+ if (!useColor) {
+ return "";
+ }
+ return status == null ? NONE :
+ (status == IN_PROGRESS ? BLUE :
+ (status == SUCCEEDED ? GREEN :
+ (status == FAILED ? RED : GRAY)));
+ }
+
+ // Produces a list from the specified comma-separated string.
+ private static List<String> list(String patterns) {
+ return ImmutableList.copyOf(patterns.split(","));
+ }
+
+ // Produces a formatted time stamp.
+ private static String time(long time) {
+ return new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date(time));
+ }
+
+ // Pauses for the specified number of millis.
+ private static void pause(int ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ print("Interrupted!");
+ }
+ }
+
+ // Shutdown hook to report status even when aborted.
+ private class ShutdownHook extends Thread {
+ @Override
+ public void run() {
+ printSummary(1, true);
+ }
+ }
+
+ // Logger to quiet Jetty down
+ private static class NullLogger implements Logger {
+ @Override
+ public String getName() {
+ return "quiet";
+ }
+
+ @Override
+ public void warn(String msg, Object... args) {
+ }
+
+ @Override
+ public void warn(Throwable thrown) {
+ }
+
+ @Override
+ public void warn(String msg, Throwable thrown) {
+ }
+
+ @Override
+ public void info(String msg, Object... args) {
+ }
+
+ @Override
+ public void info(Throwable thrown) {
+ }
+
+ @Override
+ public void info(String msg, Throwable thrown) {
+ }
+
+ @Override
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ @Override
+ public void setDebugEnabled(boolean enabled) {
+ }
+
+ @Override
+ public void debug(String msg, Object... args) {
+ }
+
+ @Override
+ public void debug(Throwable thrown) {
+ }
+
+ @Override
+ public void debug(String msg, Throwable thrown) {
+ }
+
+ @Override
+ public Logger getLogger(String name) {
+ return this;
+ }
+
+ @Override
+ public void ignore(Throwable ignored) {
+ }
+ }
+}