package com.redhat.qe.tools; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.LogRecord; import java.util.logging.Logger; import com.redhat.qe.auto.testng.Assert; import com.redhat.qe.auto.testng.LogMessageUtil; import com.trilead.ssh2.Connection; import com.trilead.ssh2.SCPClient; public class RemoteFileTasks { protected static Logger log = Logger.getLogger(RemoteFileTasks.class.getName()); /** * Create a file on a remote machine with given contents * @param conn - A connection object already created to connect to ssh server * @param filePath - path to the file you want to create (including dir and filename) * @param contents - contents of the file you want to create * @throws IOException * @author jweiss */ public static void createFile(Connection conn, String filePath, String contents, String mode) throws IOException { String dir = new File(filePath).getParent(); String fn = new File(filePath).getName(); log.log(Level.INFO, "Creating " + fn + " in " + dir + " on " + conn.getHostname(), LogMessageUtil.Style.Action); SCPClient scp = new SCPClient(conn); scp.put(contents.getBytes(), fn, dir, mode); } public static void createFile(Connection conn, String filePath, String contents) throws IOException { createFile(conn, filePath, contents, "0755"); } /** * Use echo to create a file with the given contents. Then use chmod to give permissions to the file. * @param runner * @param filePath - absolute path to the file create * @param contents - contents of the file * @param perms - optional chmod options to apply to the filePath (e.g. "a+x") * @return - exit code * @author jsefler */ public static int createFile(SSHCommandRunner runner, String filePath, String contents, String perms) { int exitCode = runCommandAndWait(runner, "echo -n -e '"+contents+"' > "+filePath, LogMessageUtil.action()); if (exitCode==0 && perms!=null) exitCode = runCommandAndWait(runner, "chmod "+perms+" "+filePath, LogMessageUtil.action()); return exitCode; } /** * Copy file(s) onto a remote machine * @param conn - A connection object already created to connect to ssh server * @param destDir - path where the file(s) should go on the remote machine (must be dir) * @param source - one or more paths to the file(s) you want to copy to the remote dir * @throws IOException * @author jweiss */ public static void putFiles(Connection conn, String destDir, String... sources ) throws IOException { for (String source: sources) log.log(Level.INFO, "Copying " + source + " to " + destDir + " on " + conn.getHostname(), LogMessageUtil.Style.Action); SCPClient scp = new SCPClient(conn); scp.put(sources, destDir); } /** * @param conn - A connection object already created to connect to ssh server * @param source - path to the file you want to copy * @param dest - full path to the destination where you want the file to go * (if path ends in trailing slash, it's assumed to be a dir, and the source filename is used) * @param mask - permissions on file, eg, "0755" * @throws IOException * @author jweiss */ public static void putFile(Connection conn, String source, String dest, String mask) throws IOException { log.log(Level.INFO, "Copying local file " + source + " to " + dest + " on " + conn.getHostname() + " with mask " + mask, LogMessageUtil.Style.Action); SCPClient scp = new SCPClient(conn); if (dest.endsWith("/")) { scp.put(new String[] {source}, null, dest, mask); } else { String destDir = new File(dest).getParentFile().getCanonicalPath(); String destFile = new File(dest).getName(); scp.put(new String[] {source}, new String[] {destFile}, destDir, mask); } } /** * Copy file(s) from a remote machine * @param conn - can be retrieved from your SSHCommandRunner instance * @param localTargetDirectory - Local directory to put the downloaded file(s). * @param remoteFiles - Path and name(s) of the remote file(s) * @throws IOException * @author jsefler */ public static void getFiles(Connection conn, String localTargetDirectory, String... remoteFiles ) throws IOException { for (String remoteFile: remoteFiles) log.log(Level.INFO, "Copying remote file "+remoteFile+" on "+conn.getHostname()+" to local directory "+localTargetDirectory+".", LogMessageUtil.Style.Action); SCPClient scp = new SCPClient(conn); scp.get(remoteFiles, localTargetDirectory); } public static void getFile(Connection conn, String localTargetDirectory, String remoteFile ) throws IOException { getFiles(conn,localTargetDirectory,remoteFile); } /** * Use sed to search and replace content within a file.<br> * sed -i 's/regexp/replacement/g' filePath * @param runner * @param filePath - absolute path to the file to be searched and replaced * @param regexp - the regular expression used to match a pattern for replacement * @param replacement - the replacement content * <BR>Note: in case your regexp or replacement contains a / character in it, you will need to call .replaceAll("/", "\\/") as you pass them to this method. * @return - exit code from sed * */ public static int searchReplaceFile (SSHCommandRunner runner, String filePath, String regexp, String replacement) { return runCommandAndWait(runner, "sed -i 's/"+regexp+"/"+replacement+"/g' " + filePath, LogMessageUtil.action()); } /** * Use grep to search for the existence of an extended regular expression within a file.<br> * grep -E 'searchTerm' filePath * @param runner * @param filePath - absolute path to the file to be searched * @param pattern - an extended regular expression (man grep for help) * @return - exit code from grep */ public static int grepFile (SSHCommandRunner runner, String filePath, String pattern) { return runCommandAndWait(runner, "grep -E '" + pattern + "' " + filePath, LogMessageUtil.info()); } /** * Use sed to delete lines from a file.<br> * sed -i '/containingText/d' filePath * @param runner * @param filePath - absolute path to the file from which lines will be deleted * @param containingText - delete lines containing a match to this text * @return - exit code from sed * @author jsefler */ public static int deleteLines (SSHCommandRunner runner, String filePath, String containingText) { return runCommandAndWait(runner, "sed -i '/"+containingText+"/d' " + filePath, LogMessageUtil.action()); } /** * Use echo to append a marker string to the end of an existing file (e.g. a log file).<br> * @param runner * @param filePath - path to an existing file on the runner machine. If this file does not exist, then 1 is returned and no mark is made. * @param marker - this string will be appended to the file and act as a marker. DO NOT USE CHARACTERS THAT WILL BE INTERPRETED BY BASH IN THIS STRING (e.g. PARENTHESIS). * @return exit code from echo * @author jsefler */ public static int markFile (SSHCommandRunner runner, String filePath, String marker) { if (!testExists(runner, filePath)) return 1; // avoid inadvertently creating a file that does not exist return runCommandAndWait(runner, "echo '"+marker+"' >> "+filePath, LogMessageUtil.action()); } /** * Return the tail of a file up to, but not including, the marker string (that was previously appended to the file).<br> * This method is intended for use with the static markFile() method. The idea is that a log file is first marked by your test, * then the program that you are testing will append information to the log file, then by calling this method your test can get the tail * of the log from the point after the mark to the end of file. * @param runner * @param filePath - path to the file on the runner machine * @param marker - string that was previously appended to the file by calling the markFile(...) method * @param grepPattern - if not null, the tail of the file past the marker string will be greped for this pattern and the matching lines are returned. * @return stdout * @author jmolet * @author jsefler */ public static String getTailFromMarkedFile (SSHCommandRunner runner, String filePath, String marker, String grepPattern) { // INEFFICIENT ALGORITHM... // String grepCommand = ""; // if (grepPattern!=null) grepCommand = " | grep -E '"+grepPattern+"'"; // SSHCommandResult result = runCommandAndAssert(runner,"(TAIL=''; IFS=$'\\n'; for line in $(tac "+filePath+"); do if [[ $line = '"+marker+"' ]]; then break; fi; if [[ $TAIL = '' ]]; then TAIL=$line; else TAIL=$line'\\n'${TAIL}; fi; done; echo -e $TAIL)"+grepCommand,0,1); // when grepCommand!=null, exitCode=0 means a match was found exitCode=1 means no match was found // return result.getStdout(); if (grepPattern!=null) { return runCommandAndAssert(runner,"(TAIL=''; IFS=$'\\n'; for line in $(egrep '"+grepPattern+"|"+marker+"' "+filePath+" | tac); do if [[ $line = '"+marker+"' ]]; then break; fi; if [[ $TAIL = '' ]]; then TAIL=$line; else TAIL=$line'\\n'${TAIL}; fi; done; echo -e $TAIL)",0).getStdout(); } else { return runCommandAndAssert(runner,"(TAIL=''; IFS=$'\\n'; for line in $(tac "+filePath+"); do if [[ $line = '"+marker+"' ]]; then break; fi; if [[ $TAIL = '' ]]; then TAIL=$line; else TAIL=$line'\\n'${TAIL}; fi; done; echo -e $TAIL)",0).getStdout(); } } /** * Test for the existence of a file.<br> * test -e filePath && echo 1 || echo 0 * @param runner * @param filePath - absolute path to the file to test for existence * @return 1 (file exists), 0 (file does not exist), -1 (could not determine existence) * @author jsefler */ @Deprecated public static int testFileExists (SSHCommandRunner runner, String filePath) { runCommandAndWait(runner, "test -e "+filePath+" && echo 1 || echo 0", LogMessageUtil.info()); if (runner.getStdout().trim().equals("1")) return 1; if (runner.getStdout().trim().equals("0")) return 0; return -1; // Note: Another more informative way to implement this is using: stat filePath } /** * Test for the existence of a file.<br> * @param runner * @param filePath - absolute path to the file to test for existence * @return boolean * @author jsefler */ public static boolean testExists (SSHCommandRunner runner, String filePath) { SSHCommandResult result = runner.runCommandAndWait("test -e "+filePath); return (result.exitCode==0)?true:false; // Note: Another more informative way to implement this is using: stat filePath } public static int runCommandAndWait(SSHCommandRunner runner, String command, LogRecord logRecord){ return runner.runCommandAndWait(command,logRecord).getExitCode(); //return runner.runCommandAndWait(command,Long.valueOf(30000),logRecord); // timeout after 30 sec } public static int runAugeasCommand(SSHCommandRunner runner, String command, LogRecord logRecord){ return runCommandAndWait(runner, String.format("echo -e \"%s\nsave\" | augtool", command), logRecord); } public static int updateAugeasConfig(SSHCommandRunner runner, String augeusPath, String newValue){ if (newValue == null) return runAugeasCommand(runner, String.format("rm %s", augeusPath), LogMessageUtil.action()); else return runAugeasCommand(runner, String.format("set %s '%s'", augeusPath, newValue), LogMessageUtil.action()); } public static SSHCommandResult runCommandAndAssert(SSHCommandRunner sshCommandRunner, String command, Integer exitCode, List<String> stdoutRegexs, List<String> stderrRegexs) { List<Integer> exitCodes = null; if (exitCode != null) { exitCodes = new ArrayList<Integer>(); exitCodes.add(exitCode); } return runCommandAndAssert(sshCommandRunner, command, exitCodes, stdoutRegexs, stderrRegexs); } /** * Use the sshCommandRunner to execute the given command and verify that stdout and stderr * contains one or more matches to an expected regex expression. <br> * Note: Assert.assertContainsMatch(...) will be used verify the output. That means the regex * does not have to match the entire output to be a successful match. * @param sshCommandRunner * @param command - command to execute with options * @param validExitCodes - a list of expected exit codes from the command (usually 0 on success, non-0 on failure). If the actual exit code matches * any code in this list, the assert passes. * @param stdoutRegexs - List of expected regex expressions. Each regex is asserted to match a substring from the command's stdout * @param stderrRegexs - List of expected regex expressions. Each regex is asserted to match a substring from the command's stderr * @author jsefler */ public static SSHCommandResult runCommandAndAssert(SSHCommandRunner sshCommandRunner, String command, List<Integer> validExitCodes, List<String> stdoutRegexs, List<String> stderrRegexs) { SSHCommandResult sshCommandResult = sshCommandRunner.runCommandAndWait(command); if (validExitCodes!=null) { Assert.assertContains(validExitCodes, sshCommandResult.getExitCode()); } if (stdoutRegexs!=null) { for (String regex : stdoutRegexs) { Assert.assertContainsMatch(sshCommandResult.getStdout(),regex,"Stdout",String.format("Stdout from command '%s' contains matches to regex '%s',",command,regex)); } } if (stderrRegexs!=null) { for (String regex : stderrRegexs) { Assert.assertContainsMatch(sshCommandResult.getStderr(),regex,"Stderr",String.format("Stderr from command '%s' contains matches to regex '%s',",command,regex)); } } return sshCommandResult; } public static SSHCommandResult runCommandAndAssert(SSHCommandRunner sshCommandRunner, String command, Integer exitCode, String stdoutRegex, String stderrRegex) { List<String> stdoutRegexs = null; if (stdoutRegex!=null) { stdoutRegexs = new ArrayList<String>(); stdoutRegexs.add(stdoutRegex); } List<String> stderrRegexs = null; if (stderrRegex!=null) { stderrRegexs = new ArrayList<String>(); stderrRegexs.add(stderrRegex); } return runCommandAndAssert(sshCommandRunner,command,exitCode,stdoutRegexs,stderrRegexs); } public static SSHCommandResult runCommandAndAssert(SSHCommandRunner sshCommandRunner, String command, Integer... exitCodes) { return runCommandAndAssert(sshCommandRunner,command,Arrays.asList(exitCodes),new ArrayList<String>(),new ArrayList<String>()); } /** * Occasionally, you may need to run commands, expecting a nonzero exit code. * * If you run into this situation, this is your method. * @param sshCommandRunner your preferred sshCommandRunner * @param command - command to execute with options * @author ssalevan */ public static SSHCommandResult runCommandExpectingNonzeroExit(SSHCommandRunner sshCommandRunner, String command){ return runCommandExpectingNonzeroExit(sshCommandRunner, command, null); } /** * Occasionally, you may need to run commands, expecting a nonzero exit code. * * If you run into this situation, this is your method. * @param sshCommandRunner your preferred sshCommandRunner * @param command - command to execute with options * @param timeout - in milliseconds * @author whayutin */ public static SSHCommandResult runCommandExpectingNonzeroExit(SSHCommandRunner sshCommandRunner, String command, Long timeout){ // THIS WILL ALWAYS RETURN FALSE SINCE THE COMPARISON IS BETWEEN TWO DIFFERENT INSTANTIATED OBJECTS EVEN THOUGH THEIR VALUES MAY BE EQUAL - jsefler 7/9/2010 // Assert.assertNotSame(sshCommandRunner.runCommandAndWait(command,timeout), // 0, // "Command returns nonzero error code: "+command); SSHCommandResult sshCommandResult = sshCommandRunner.runCommandAndWait(command,timeout); Assert.assertTrue(!sshCommandResult.getExitCode().equals(0),"Command '"+command+"' returns nonzero error code: "+sshCommandResult.getExitCode()); return sshCommandResult; } public static SSHCommandResult runCommandExpectingNoTracebacks(SSHCommandRunner sshCommandRunner, String command){ return runCommandExpectingNoTracebacks( sshCommandRunner, command, null); } public static SSHCommandResult runCommandExpectingNoTracebacks(SSHCommandRunner sshCommandRunner, String command, Long timeout){ SSHCommandResult sshCommandResult = sshCommandRunner.runCommandAndWait(command,timeout); Assert.assertFalse(sshCommandResult.getStdout().toLowerCase().contains("traceback"), "Traceback string not in stdout"); Assert.assertFalse(sshCommandResult.getStderr().toLowerCase().contains("traceback"), "Traceback string not in stderr"); return sshCommandResult; } }