package org.radargun.stages;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.radargun.DistStageAck;
import org.radargun.config.Property;
import org.radargun.config.Stage;
import org.radargun.utils.ArgsConverter;
/**
* @author Radim Vansa <rvansa@redhat.com>
*/
@Stage(doc = "Stage that allows you to execute generic command on the slave machine.")
public class CommandStage extends AbstractDistStage {
@Property(doc = "Command that should be executed. No default, but must be provided unless 'var' is set.")
private String cmd;
@Property(doc = "Arguments to this command. Default are none", converter = ArgsConverter.class)
private List<String> args;
@Property(doc = "Argument which won't be parsed. Useful to run piped commands, like sh -c \"echo yes | some interactive script\"")
private String nonParsedArgs;
@Property(doc = "List of exit values that are allowed from the command. Default is {0}.")
private List<Integer> exitValues = Collections.singletonList(0);
@Property(doc = "Wait until the command finishes. Default is true.")
private boolean waitForExit = true;
@Property(doc = "Store/load process into/from state variable. Use this in combination with 'waitForExit=false'. By default the process is not stored/loaded.")
private String var;
@Property(doc = "Cancel running command, loaded using the 'var' property. By default not cancelling anything.")
private boolean cancel = false;
@Property(doc = "Output file. By default uses standard output.")
private String out;
@Property(doc = "Append output to the file instead of overwriting. Default is to overwrite.")
private boolean outAppend = false;
@Property(doc = "Error output file. By default uses standard error.")
private String err;
@Property(doc = "Append error output to the file instead of overwriting. Default is to overwrite.")
private boolean errAppend = false;
@Override
public DistStageAck executeOnSlave() {
try {
Process process;
if (cmd != null) {
process = startProcess();
if (var != null) {
slaveState.put(var, process);
}
} else if (var != null) {
process = (Process) slaveState.get(var);
if (process == null) {
return errorResponse("Could not find any process identified with '" + var + "'");
}
} else {
return errorResponse("Must specify either 'cmd' or 'var'");
}
if (cancel) {
process.destroy();
}
if (waitForExit) {
int exitValue = process.waitFor();
if (var != null) {
slaveState.remove(var);
}
if (exitValues.contains(exitValue)) return successfulResponse();
else return errorResponse("Command finished with exit value " + exitValue);
} else {
return successfulResponse();
}
} catch (IOException e) {
return errorResponse("Command failed", e);
} catch (InterruptedException e) {
return errorResponse("Interrupted while waiting for the command to finish", e);
}
}
protected Process startProcess() throws IOException {
List<String> command = new ArrayList<String>();
command.add(cmd);
if (args != null) {
command.addAll(args);
}
if (nonParsedArgs != null) {
command.add(nonParsedArgs);
}
log.info("Running: " + command);
ProcessBuilder pb = new ProcessBuilder().command(command);
pb.redirectInput(ProcessBuilder.Redirect.INHERIT);
pb.redirectOutput(getDestination(out, outAppend));
pb.redirectError(getDestination(err, errAppend));
return pb.start();
}
private ProcessBuilder.Redirect getDestination(String file, boolean append) {
return file == null ? ProcessBuilder.Redirect.INHERIT :
append ? ProcessBuilder.Redirect.appendTo(new File(file))
: ProcessBuilder.Redirect.to(new File(file));
}
}