package sos.stacks.ganymed;
import com.trilead.ssh2.StreamGobbler;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import sos.spooler.Order;
import sos.spooler.Variable_set;
/**
* @author andreas.pueschel@sos-berlin.com
* @author ghassan.beydoun@sos-berlin.com
* $Id: JobSchedulerSSHJob.java 3178 2008-01-04 13:49:31Z al $
* see job documentation in the package jobdoc for details
*/
public class JobSchedulerSSHJob extends JobSchedulerSSHBaseJob {
/** default command delimiter */
final static String DEFAULT_COMMAND_DELIMITER = "%%";
/** regular expression for delimiter of multiple commands specified as job or order parameter */
protected String commandDelimiter = DEFAULT_COMMAND_DELIMITER;
/** array of commands that have been separated by the commandDelimiter */
protected String[] commands = {};
/** ignore errors reported by the exit status of commands */
protected boolean ignoreError = false;
/** ignore signals terminating remote execution */
protected boolean ignoreSignal = false;
/** ignore output to stderr */
protected boolean ignoreStderr = false;
/** Simulate a shell instead of just passing commands **/
protected boolean simulateShell = false;
/** Trigger for this string to know that the shell is waiting **/
protected String promptTrigger = "";
/** Inputstreams for stdout and stderr **/
protected InputStream stdout;
protected InputStream stderr;
protected int x=200;
protected int y=30;
/** timestamp of the last text from stdin or stderr **/
protected long lasttime =0;
/** time to wait if anything more is coming from stdout or stderr when logging in **/
protected long loginTimeout = 0;
/** time to wait if anything more is coming from stdout or stderr when executing commands **/
protected long commandTimeout = 0;
/** Output from stdout and stderr **/
protected StringBuffer stdoutOutput;
protected StringBuffer stderrOutput;
/** Line currently being displayed on the shell **/
protected String currentLine = "";
/**
* Processing
*
*/
public boolean spooler_process() {
stdoutOutput = new StringBuffer();
stderrOutput = new StringBuffer();
currentLine = "";
simulateShell=false;
promptTrigger = "";
loginTimeout = 0;
commandTimeout = 0;
Order order = null;
Variable_set params = null;
try {
try { // to fetch parameters, order parameters have precedence to job parameters
params = spooler_task.params();
if (spooler_task.job().order_queue() != null) {
order = spooler_task.order();
if ( order.params() != null)
params.merge(order.params());
}
// get basic authentication parameters
this.getBaseParameters();
if (params.value("command_delimiter") != null && params.value("command_delimiter").length() > 0) {
this.setCommandDelimiter(params.value("command_delimiter"));
spooler_log.info(".. parameter [command_delimiter]: " + this.getCommandDelimiter());
} else {
this.setCommandDelimiter(DEFAULT_COMMAND_DELIMITER);
}
if (params.value("command") != null && params.value("command").length() > 0) {
this.setCommands(params.value("command").split(this.getCommandDelimiter()));
spooler_log.info(".. parameter [command]: " + params.value("command"));
} else {
throw new Exception("no command has been specified for parameter [command]");
}
if (params.value("ignore_error") != null && params.value("ignore_error").length() > 0) {
if (params.value("ignore_error").equalsIgnoreCase("true") || params.value("ignore_error").equalsIgnoreCase("yes") || params.value("ignore_error").equals("1")) {
this.setIgnoreError(true);
} else {
this.setIgnoreError(false);
}
spooler_log.info(".. parameter [ignore_error]: " + this.isIgnoreError());
} else {
this.setIgnoreError(false);
}
if (params.value("ignore_signal") != null && params.value("ignore_signal").length() > 0) {
if (params.value("ignore_signal").equalsIgnoreCase("true") || params.value("ignore_signal").equalsIgnoreCase("yes") || params.value("ignore_signal").equals("1")) {
this.setIgnoreSignal(true);
} else {
this.setIgnoreSignal(false);
}
spooler_log.info(".. parameter [ignore_signal]: " + this.isIgnoreSignal());
} else {
this.setIgnoreSignal(false);
}
if (params.value("ignore_stderr") != null && params.value("ignore_stderr").length() > 0) {
if (params.value("ignore_stderr").equalsIgnoreCase("true") || params.value("ignore_stderr").equalsIgnoreCase("yes") || params.value("ignore_stderr").equals("1")) {
this.setIgnoreStderr(true);
} else {
this.setIgnoreStderr(false);
}
spooler_log.info(".. parameter [ignore_stderr]: " + this.isIgnoreStderr());
} else {
this.setIgnoreStderr(false);
}
if (params.value("simulate_shell") != null && params.value("simulate_shell").length() > 0) {
if (params.value("simulate_shell").equalsIgnoreCase("true") || params.value("simulate_shell").equalsIgnoreCase("yes") || params.value("simulate_shell").equals("1")) {
simulateShell= true;
} else {
simulateShell= false;
}
spooler_log.info(".. parameter [simulate_shell]: " + simulateShell);
} else {
simulateShell= false;
}
if (params.value("simulate_shell_prompt_trigger") != null && params.value("simulate_shell_prompt_trigger").length() > 0) {
this.setPromptTrigger(params.value("simulate_shell_prompt_trigger"));
spooler_log.info(".. parameter [simulate_shell_prompt_trigger]: " + this.getPromptTrigger());
} else {
this.setPromptTrigger("");
}
if (params.value("simulate_shell_login_timeout") != null && params.value("simulate_shell_login_timeout").length() > 0) {
this.loginTimeout=Long.parseLong(params.value("simulate_shell_login_timeout"));
spooler_log.info(".. parameter [simulate_shell_login_timeout]: " + this.loginTimeout);
} else {
loginTimeout=0;
}
if (params.value("simulate_shell_inactivity_timeout") != null && params.value("simulate_shell_inactivity_timeout").length() > 0) {
this.commandTimeout=Long.parseLong(params.value("simulate_shell_inactivity_timeout"));
spooler_log.info(".. parameter [simulate_shell_inactivity_timeout]: " + this.commandTimeout);
} else {
commandTimeout=0;
}
if (simulateShell && getPromptTrigger().length()==0 && (loginTimeout==0 || commandTimeout==0)){
throw new Exception("if simulate_shell=true then either simulate_shell_prompt_trigger or "+
"simulate_shell_login_timeout and simulate_shell_inactivity_timeout need to br set.");
}
} catch (Exception e) {
throw new Exception("error occurred processing parameters: " + e.getMessage());
}
RemoteConsumer stdoutConsumer=null;
RemoteConsumer stderrConsumer=null;
try { // to connect, authenticate and execute commands
this.getBaseAuthentication();
OutputStream stdin;
OutputStreamWriter stdinWriter=null;
if (simulateShell){
this.setSshSession(this.getSshConnection().openSession());
spooler_log.debug3("Requesting PTY...");
this.getSshSession().requestDumbPTY();
spooler_log.debug3("Starting shell...");
this.getSshSession().startShell();
stdout = getSshSession().getStdout();
stderr = getSshSession().getStderr();
stdoutConsumer = new RemoteConsumer(stdoutOutput, true, stdout);
stderrConsumer = new RemoteConsumer(stderrOutput, false, stderr);
stdoutConsumer.start();
stderrConsumer.start();
stdin = getSshSession().getStdin();
stdinWriter = new OutputStreamWriter(stdin);
spooler_log.debug3("Waiting for login prompt...");
boolean loggedIn=false;
while (!loggedIn){
if (lasttime>0){
long now = System.currentTimeMillis();
if (loginTimeout>0 && lasttime+loginTimeout<now){// kommt nichts mehr
loggedIn = true;
}
if (promptTrigger.length()>0 && currentLine.indexOf(promptTrigger)!=-1){
spooler_log.debug3("Found login prompt "+promptTrigger);
loggedIn = true;
currentLine="";
}
}
}
}
// execute commands
for (int i=0; i<this.getCommands().length; i++) {
try {
Integer exitStatus = null;
String exitSignal = null;
spooler_log.info("executing remote command: " + this.getCommands()[i]);
if (simulateShell){
stdinWriter.write(this.getCommands()[i]+"\n");
stdinWriter.flush();
boolean prompt = false;
while (!prompt){
long now = System.currentTimeMillis();
if (loginTimeout>0 && lasttime+loginTimeout<now){// kommt nichts mehr
prompt = true;
}
if (promptTrigger.length()>0 && currentLine.indexOf(promptTrigger)!=-1){
spooler_log.debug3("Found prompt "+promptTrigger);
prompt = true;
}
}
currentLine="";
spooler_log.info("output to stdout for remote command: " + this.getCommands()[i]);
spooler_log.info(stdoutOutput.toString());
stdoutOutput = new StringBuffer();
}else {
this.setSshSession(this.getSshConnection().openSession());
this.getSshSession().execCommand(this.getCommands()[i]);
spooler_log.info("output to stdout for remote command: " + this.getCommands()[i]);
stdout = new StreamGobbler(this.getSshSession().getStdout());
stderr = new StreamGobbler(this.getSshSession().getStderr());
BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout));
while (true) {
String line = stdoutReader.readLine();
if (line == null) break;
spooler_log.info(line);
}
spooler_log.info("output to stderr for remote command: " + this.getCommands()[i]);
// Beide StreamGobbler müssen hintereinander instanziiert werden
//InputStream stderr = new StreamGobbler(this.getSshSession().getStderr());
BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr));
stderrOutput = new StringBuffer();
while (true) {
String line = stderrReader.readLine();
if (line == null) break;
spooler_log.info(line);
stderrOutput.append( line + "\n");
}
}
if (stderrOutput != null && stderrOutput.length() > 0) {
if (this.isIgnoreStderr()) {
spooler_log.info("output to stderr is ignored: " + stderrOutput);
} else {
throw new Exception("remote execution reports error: " + stderrOutput);
}
}
try {
exitStatus = this.getSshSession().getExitStatus();
} catch (Exception e) {
spooler_log.info("could not retrieve exit status, possibly not supported by remote ssh server");
}
if (exitStatus != null) {
if (!exitStatus.equals(new Integer(0))) {
if (this.isIgnoreError()) {
spooler_log.info("exit status is ignored: " + exitStatus);
} else {
throw new Exception("remote command terminated with exit status: " + exitStatus);
}
}
}
try {
exitSignal = this.getSshSession().getExitSignal();
} catch (Exception e) {
spooler_log.info("could not retrieve exit signal, possibly not supported by remote ssh server");
}
if (exitSignal != null) {
if (exitSignal.length() > 0) {
if (this.isIgnoreSignal()) {
spooler_log.info("exit signal is ignored: " + exitSignal);
} else {
throw new Exception("remote command terminated with exit signal: " + exitSignal);
}
}
}
} catch (Exception e) {
throw new Exception(e.getMessage());
} finally {
if (this.getSshSession() != null) try { this.getSshSession().close(); this.setSshSession(null); } catch (Exception ex) {} // gracefully ignore this error
}
}
} catch (Exception e) {
throw new Exception("error occurred processing ssh command: " + e.getMessage());
} finally {
if (stderrConsumer!=null) stderrConsumer.end();
if (stdoutConsumer!=null) stdoutConsumer.end();
if (this.getSshConnection() != null) try { this.getSshConnection().close(); this.setSshConnection(null); } catch (Exception ex) {} // gracefully ignore this error
}
// return value for classic and order driven processing
return (spooler_task.job().order_queue() != null);
} catch (Exception e) {
spooler_log.warn(e.getMessage());
return false;
}
}
/**
* @return Returns the commands.
*/
private String[] getCommands() {
return commands;
}
/**
* @param commands The commands to set.
*/
private void setCommands(String[] commands) {
this.commands = commands;
}
/**
* @return Returns the commandDelimiter.
*/
private String getCommandDelimiter() {
return commandDelimiter;
}
/**
* @param commandDelimiter The commandDelimiter to set.
*/
private void setCommandDelimiter(String commandDelimiter) {
this.commandDelimiter = commandDelimiter;
}
/**
* @return Returns the ignoreError.
*/
public boolean isIgnoreError() {
return ignoreError;
}
/**
* @param ignoreError The ignoreError to set.
*/
public void setIgnoreError(boolean ignoreError) {
this.ignoreError = ignoreError;
}
/**
* @return Returns the ignoreSignal.
*/
public boolean isIgnoreSignal() {
return ignoreSignal;
}
/**
* @param ignoreSignal The ignoreSignal to set.
*/
public void setIgnoreSignal(boolean ignoreSignal) {
this.ignoreSignal = ignoreSignal;
}
/**
* @return Returns the ignoreStderr.
*/
public boolean isIgnoreStderr() {
return ignoreStderr;
}
/**
* @param ignoreStderr The ignoreStderr to set.
*/
public void setIgnoreStderr(boolean ignoreStderr) {
this.ignoreStderr = ignoreStderr;
}
/**
* This thread consumes output from the remote server puts it into
* fields of the main class
*/
class RemoteConsumer extends Thread
{
private StringBuffer sbuf;
private boolean writeCurrentline = false;
private InputStream stream;
boolean end = false;
private RemoteConsumer(StringBuffer buffer, boolean writeCurr, InputStream str){
this.sbuf = buffer;
this.writeCurrentline = true;
this.stream = str;
}
private void addText(byte[] data, int len)
{
lasttime = System.currentTimeMillis();
String outstring = new String(data).substring(0,len);
sbuf.append(outstring);
if (writeCurrentline){
int newlineIndex = outstring.indexOf("\n");
if (newlineIndex>-1){
String stringAfterNewline = outstring.substring(newlineIndex);
currentLine = stringAfterNewline;
} else currentLine += outstring;
}
}
public void run()
{
byte[] buff = new byte[64];
try
{
while (!end)
{
buff = new byte[8];
int len = stream.read(buff);
if (len == -1)
return;
addText(buff, len);
}
}
catch (Exception e)
{
}
}
public synchronized void end() {
end = true;
}
}
/**
* @return Returns the promptTrigger.
*/
public String getPromptTrigger() {
return promptTrigger;
}
/**
* @param promptTrigger The promptTrigger to set.
*/
public void setPromptTrigger(String promptTrigger) {
this.promptTrigger = promptTrigger;
}
}
|