| /* |
| * Copyright 2015-present 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 org.onlab.stc.Coordinator.Status; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.PrintWriter; |
| |
| import static java.lang.String.format; |
| import static org.onlab.stc.Coordinator.Status.FAILED; |
| import static org.onlab.stc.Coordinator.Status.SUCCEEDED; |
| import static org.onlab.stc.Coordinator.print; |
| |
| /** |
| * Manages execution of the specified step or a group. |
| */ |
| class StepProcessor implements Runnable { |
| |
| private static final String IGNORE_CODE = "~"; |
| private static final String NEGATE_CODE = "!"; |
| |
| private static final int FAIL = -1; |
| private static final int SECONDS = 1_000; |
| |
| static String launcher = "stc-launcher "; |
| |
| private final Step step; |
| private final File logDir; |
| private String command; |
| |
| private Process process; |
| private StepProcessListener delegate; |
| |
| /** |
| * Creates a process monitor. |
| * |
| * @param step step or group to be executed |
| * @param logDir directory where step process log should be stored |
| * @param delegate process lifecycle listener |
| * @param command actual command to execute |
| */ |
| StepProcessor(Step step, File logDir, StepProcessListener delegate, |
| String command) { |
| this.step = step; |
| this.logDir = logDir; |
| this.delegate = delegate; |
| this.command = command; |
| } |
| |
| @Override |
| public void run() { |
| delegate.onStart(step, command); |
| delayIfNeeded(); |
| int code = execute(); |
| boolean ignoreCode = step.env() != null && step.env.equals(IGNORE_CODE); |
| boolean negateCode = step.env() != null && step.env.equals(NEGATE_CODE); |
| Status status = ignoreCode || code == 0 && !negateCode || code != 0 && negateCode ? |
| SUCCEEDED : FAILED; |
| delegate.onCompletion(step, status); |
| } |
| |
| /** |
| * Pauses if the step requires it. |
| */ |
| private void delayIfNeeded() { |
| if (step.delay() > 0) { |
| try { |
| Thread.sleep(step.delay() * SECONDS); |
| } catch (InterruptedException e) { |
| throw new RuntimeException("Interrupted", e); |
| } |
| } |
| } |
| |
| /** |
| * Executes the step process. |
| * |
| * @return exit code |
| */ |
| private int execute() { |
| try (PrintWriter pw = new PrintWriter(logFile())) { |
| process = Runtime.getRuntime().exec(command()); |
| processOutput(pw); |
| |
| // Wait for the process to complete and get its exit code. |
| if (process.isAlive()) { |
| process.waitFor(); |
| } |
| return process.exitValue(); |
| |
| } catch (IOException e) { |
| print("Unable to run step %s using command %s", step.name(), step.command()); |
| } catch (InterruptedException e) { |
| print("Step %s interrupted", step.name()); |
| } |
| return FAIL; |
| } |
| |
| /** |
| * Returns ready-to-run command for the step. |
| * |
| * @return command to execute |
| */ |
| private String command() { |
| return format("%s %s %s %s", launcher, |
| step.env() != null ? step.env() : "-", |
| step.cwd() != null ? step.cwd() : "-", |
| command); |
| } |
| |
| /** |
| * Captures output of the step process. |
| * |
| * @param pw print writer to send output to |
| * @throws IOException if unable to read output or write logs |
| */ |
| private void processOutput(PrintWriter pw) throws IOException { |
| InputStream out = process.getInputStream(); |
| BufferedReader br = new BufferedReader(new InputStreamReader(out)); |
| |
| // Slurp its combined stderr/stdout |
| String line; |
| while ((line = br.readLine()) != null) { |
| pw.println(line); |
| delegate.onOutput(step, line); |
| } |
| } |
| |
| /** |
| * Returns the log file for the step output. |
| * |
| * @return log file |
| */ |
| private File logFile() { |
| return new File(logDir, step.name() + ".log"); |
| } |
| |
| } |