diff --git a/TODO b/TODO index 6cd4630..54fe779 100644 --- a/TODO +++ b/TODO @@ -19,6 +19,9 @@ Gets done before Puts Gets prepend hostname to saved file +- Option to read from stdin + + diff --git a/tsshbatch.py b/tsshbatch.py index b404f4c..dac813c 100755 --- a/tsshbatch.py +++ b/tsshbatch.py @@ -17,7 +17,7 @@ PROGNAME = "tsshbatch.py" BASENAME = PROGNAME.split(".py")[0] PROGENV = BASENAME.upper() -CVSID = "$Id: tsshbatch.py,v 1.143 2013/10/21 19:28:21 tundra Exp $" +CVSID = "$Id: tsshbatch.py,v 1.144 2013/10/22 18:47:06 tundra Exp $" VERSION = CVSID.split()[2] CPRT = "(c)" @@ -65,8 +65,9 @@ OPTIONSLIST = "G:H:NP:ehkn:p:v" PADWIDTH = 30 PATHSEP = os.sep -SEPERATOR = " ---> " -SUCCESS = "SUCCESS" +SEPARATOR = " ---> " +STDIN = "-" +SUCCESS = "SUCCESSFUL CONNECTION" SUDO = 'sudo' SUDOPROMPT = 'READINGSUDOPW' SUDOARGS = '-S -p %s' % SUDOPROMPT @@ -74,7 +75,7 @@ USAGE = \ PROGVER + "\n" +\ HOMEPAGE + "\n\n" +\ - "Usage: tsshbatch.py [-Nehkv] [-n name] [-p pw] -G 'file dest' -P 'file dest' [-H 'host ..' | serverlistfile] command arg ... \n" +\ + "Usage: tsshbatch.py [-Nehkv] [-n name] [-p pw] -G 'file dest' -P 'file dest' [-H 'host ..' | serverlistfile] [command arg ... | - ]\n" +\ " where,\n" +\ "\n" +\ " -G 'file dest' GET file on host and write local dest directory\n" +\ @@ -200,7 +201,7 @@ # End of 'HostFileTransfer()' -def HostCommand(host, user, pw, command): +def HostCommands(host, user, pw, commands): ssh = paramiko.SSHClient() @@ -214,45 +215,48 @@ else: ssh.connect(host, username=user, password=pw) - # If this is a sudo run, force password to be read - # from stdin thereby avoiding fiddling around with ptys. + # Run all requested commands - if command.startswith(SUDO): - command = command.replace(SUDO, "%s %s" % (SUDO, SUDOARGS), 1) + for command in commands: - # Run the command + # If this is a sudo run, force password to be read + # from stdin thereby avoiding fiddling around with ptys. - stdin, stdout, stderr = ssh.exec_command(command) + if command.startswith(SUDO): + command = command.replace(SUDO, "%s %s" % (SUDO, SUDOARGS), 1) - # If doing a sudo command, send the password - - if command.startswith(SUDO): - stdin.write("%s\n" % pw) - stdin.flush() - - # If all we see on stderr at this point is our original - # prompt, then then the sudo promotion worked. A bad - # password or bad command will generate additional noise - # from sudo telling us to try again or that there was a - # command error. - - sudonoise = stderr.readline().split(SUDOPROMPT) - - if len(sudonoise) > 1: # sudo had problems - sudonoise = sudonoise[1].strip() - - else: # sudo OK - sudonoise = "" - - if sudonoise: - PrintReport([host, FAILURE % (eBADSUDO % sudonoise)], HANDLER=PrintStderr) - ssh.close() - return - - PrintReport([host + " (stdout)", SUCCESS] + stdout.readlines()) - - if REPORTERR: - PrintReport([host + " (stderr)", ""] + stderr.readlines(), HANDLER=PrintStderr) + + stdin, stdout, stderr = ssh.exec_command(command) + + # If doing a sudo command, send the password + + if command.startswith(SUDO): + stdin.write("%s\n" % pw) + stdin.flush() + + # If all we see on stderr at this point is our original + # prompt, then then the sudo promotion worked. A bad + # password or bad command will generate additional noise + # from sudo telling us to try again or that there was a + # command error. + + sudonoise = stderr.readline().split(SUDOPROMPT) + + if len(sudonoise) > 1: # sudo had problems + sudonoise = sudonoise[1].strip() + + else: # sudo OK + sudonoise = "" + + if sudonoise: + PrintReport([host + " [%s]" % command, FAILURE % (eBADSUDO % sudonoise)], HANDLER=PrintStderr) + ssh.close() + return + + PrintReport([host + " (stdout)" + " [%s]" % command, SUCCESS] + stdout.readlines()) + + if REPORTERR: + PrintReport([host + " (stderr)" + " [%s]" % command, ""] + stderr.readlines(), HANDLER=PrintStderr) # Catch authentication problems explicitly @@ -266,7 +270,7 @@ ssh.close() -# End of 'HostCommand()' +# End of 'HostCommands()' ##### @@ -279,7 +283,7 @@ def PrintReport(results, HANDLER=PrintStdout): - HANDLER(SEPERATOR + results[0] + + HANDLER(SEPARATOR + results[0] + TRAILER + (PADWIDTH - len(results[0])) * " " + results[1]) @@ -326,8 +330,9 @@ REPORTERR = True # Report stderr output from remote host UNAME = "" # Login name -# Place To Keep File Transfer Requests +# Data Structures +Commands = [] Put_Transfer_List = {} Get_Transfer_List = {} @@ -440,21 +445,41 @@ except: ErrorExit(eBADFILE % args[0]) - CMD = " ".join(args[1:]) + command = " ".join(args[1:]) # If hosts were passed on the command line, all the arguments # are understood to form the command. else: - CMD = " ".join(args[0:]) + command = " ".join(args[0:]) + +# Cleanup the command and check to see if the user really wants to use +# stdin instead of explicit command line spefication of the desired +# remote command. + +# Put it in a list data structure because this is what the +# HostCommands() function expects. This is necessary to handle multi +# command input from stdin. + +command = command.strip() +if (command == STDIN): + for command in sys.stdin.readlines(): + Commands.append(command.strip()) + +elif command: + Commands = [command,] # If user want 'sudo' execution, they MUST provide a password because # key exchange-based authentication is not part of sudo. If the # password has not been set by some other means (interactive, command # line or environment variable), ask for it here. -CMD = CMD.strip() -if CMD.startswith(SUDO) and not PWORD: +SUDOPW = False +for command in Commands: + if command.startswith(SUDO) and not PWORD: + SUDOPW = True + +if SUDOPW: PWORD = getpass.getpass(pSUDO) # Iterate over the list of hosts, executing any file transfers and @@ -471,6 +496,5 @@ if Put_Transfer_List: HostFileTransfer(host, UNAME, PWORD, Put_Transfer_List, GET=False) - if CMD: - HostCommand(host, UNAME, PWORD, CMD) - + if Commands: + HostCommands(host, UNAME, PWORD, Commands)