Newer
Older
tsshbatch / tsshbatch.py
#!/usr/bin/env python
# Non-Interactive ssh Connection


#####
# Program Housekeeping
#####

PROGNAME = "tsshbatch.py"
BASENAME = PROGNAME.split(".py")[0]
PROGENV  = BASENAME.upper()
RCSID    = "$Id: tsshbatch.py,v 1.114 2011/11/04 19:03:25 tundra Exp $"
VERSION = RCSID.split()[2]


#####
# Suppress Deprecation Warnings until Paramiko catches up
# to latest Python modules
#####

import warnings
warnings.filterwarnings("ignore", "", DeprecationWarning)


#####
# Imports
#####

import getopt
import getpass
import os
import paramiko
import shlex
import socket
import sys


#####
# Constants And Literals
#####

FAILURE     = "FAILURE"
INDENTWIDTH = 8
OPTIONSLIST = "h:kn:p:"
PADWIDTH    = 30
SEPARATOR   = " --->  "
SUCCESS     = "SUCCESS"
TRAILER     = ": "
USAGE       = "Usage:  tsshbatch.py -n username -p password serverlistfile command\n" +\
    "        tsshbatch.py [-k] serverlistfile command\n"                    +\
    "          where,\n"                                                   +\
    "\n"                                                                   +\
    "                 -k       Turns on key-exchange authentication\n"     +\
    "                 -n name  Specifies login name\n"                     +\
    "                 -p pw    Specifies login password\n"
#####
# Error Messages
#####

eBADARG       =  "Invalid command line: %s!"
eBADFILE      =  "Cannot open '%s'!"
eNOCONNECT    =  "Cannot Connect! (Name/Address Bad?  Destination Unreachable?)"
eNOLOGIN      =  "Cannot Login! (Login/Password Bad?)"


#####
# Prompts
#####

pPASS = "Password: "
pUSER = "Username: "


#####
# Print Message(s) To stdout
#####

def PrintStdout(msg, TERMINATOR="\n"):
    sys.stdout.write(msg + TERMINATOR)

# End of 'PrintStdout()'


#####
# Display An Error Message And Exit
#####

def ErrorExit(msg):

    PrintStdout(msg)
    sys.exit()

# End Of 'ErrorExit()'


#####
# Process A Command On A Host
#####

def HostCommand(host, user, pw, command):

    ssh = paramiko.SSHClient()

    # Connect and run the command, reporting results as we go
    
    try: 
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(host, username=user, password=pw)
        stdin, stdout, stderr = ssh.exec_command(command)
        PrintReport([host, SUCCESS] + stdout.readlines() + stderr.readlines())
        
    # Catch authentication problems explicitly
        
    except paramiko.AuthenticationException:
        PrintReport([host, FAILURE, eNOLOGIN])
    
    # Everything else is some kind of connection problem

    except:
        PrintReport([host, FAILURE, eNOCONNECT])

    ssh.close()

# End of 'HostCommand()'

    
#####
# Print Report
#####

# Expects input as [host, success/failure message, result1, result2, ...]

def PrintReport(results):

    PrintStdout(SEPARATOR + results[0] +
                TRAILER +
                (PADWIDTH - len(results[0])) * " " +
                results[1])

    for r in results[2:]:                             # Command Results
        PrintStdout(INDENTWIDTH * " " + r.strip())

# End of 'PrintReport()'


# ---------------------- Program Entry Point ---------------------- #

#####
# Process Any Options User Set In The Environment Or On Command Line
#####

# Options that can be overriden by user

HOSTS        = ""        # List of hosts to target
KEYEXCHANGE  = False     # Do key exchange-based auth?
PWORD        = ""        # Password
UNAME        = ""        # Login name

# Handle any options set in the environment

OPTIONS = sys.argv[1:]
envopt = os.getenv(PROGENV)
if envopt:
    OPTIONS = shlex.split(envopt) + OPTIONS

# Combine them with those given on the command line
# This allows the command line to override defaults
# set in the environment

try:
    opts, args = getopt.getopt(OPTIONS, OPTIONSLIST)
except getopt.GetoptError, (errmsg, badarg):
    ErrorExit(eBADARG % errmsg)

# Make sure we have sufficient command line args
# to do something useful

if len(args) < 2:
    ErrorExit(USAGE)

for opt, val in opts:

    if opt == "-h":
        HOSTS = val.split()
        
    if opt == "-k":
        KEYEXCHANGE = True

    if opt == "-n":
        UNAME = val

    if opt == "-p":
        PWORD = val
    
#####
# Go Do The Requested Work
#####

# If we're not doing key exchange-based authentication, get
# user name & password.

# Only do this if they've not been set in the environment
# variable/command line

if not KEYEXCHANGE:

    if not UNAME:
        UNAME = raw_input(pUSER)

    if not PWORD:
        PWORD  = getpass.getpass(pPASS)


# Host list and command parsing
        
# Get the list of hosts if not specified on command line.
# The assumption is that the first argument is the file
# containing the list of targeted hosts and the remaining
# arguments form the command.

if not HOSTS:

    try:
        f = open(args[0])
        HOSTS = f.readlines()
        f.close()

    except:
        ErrorExit(eBADFILE % sys.argv[1])

    CMD = " ".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:])

# Iterate over the list of hosts, executing the command

for host in HOSTS:
    host = host.strip()
    if host:
     HostCommand(host, UNAME, PWORD, CMD)