blob: 3ef2ae58294dcb20dbe459f13cb1f08cc6c47746 [file] [log] [blame]
/*
* 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");
}
}