blob: aa2c4b4e3c88dde1e980c32a640ee801aa22e50e [file] [log] [blame]
#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
'''
gpssh -- ssh access to multiple hosts at once
Usage: gpssh [--version] [-?v] [-h host] [-f hostfile] [cmd]
--version : print version information
-? : print this help screen
-v : verbose mode
-e : echo commands as they are executed
-h host : the host to connect to (multiple -h is okay)
-f file : a file listing all hosts to connect to
-D : do not filter multi-homed hosts
cmd : the command to execute. If not present,
go into interactive mode
'''
import os
import sys
progname = os.path.split(sys.argv[0])[-1]
sys.path.append(sys.path[0] + '/lib')
if sys.version_info < (2, 5, 0):
sys.exit(
'''Error: %s is supported on Python versions 2.5 or greater
Please upgrade python installed on this machine.''' % progname)
import getopt
import atexit
import signal
import pexpect
import time
from gppylib.util import ssh_utils
from gppylib.gpparseopts import OptParser
from gppylib.gpcoverage import GpCoverage
#
# all the command line options
#
class __globals__:
script_name = os.path.split(__file__)[-1]
USER = os.environ.get('LOGNAME') or os.environ.get('USER')
opt = {}
opt['-v'] = False
opt['-e'] = False
opt['-h'] = []
opt['-f'] = False
opt['-D'] = False
argcmd = None
session = None
GV = __globals__()
################
def usage(exitarg):
parser = OptParser()
try:
parser.print_help()
except:
print __doc__
sys.exit(0)
#############
def print_version():
print '%s version $Revision$' % GV.script_name
sys.exit(0)
#############
def parseCommandLine():
try:
(options, args) = getopt.getopt(sys.argv[1:], '?evh:f:D:u:', ['version'])
except Exception, e:
usage('[ERROR] ' + str(e))
for (switch, val) in options:
if (switch == '-?'): usage(0)
elif (switch[1] in 'evD'): GV.opt[switch] = True
elif (switch[1] in 'f'): GV.opt[switch] = val
elif (switch == '-h'): GV.opt[switch].append(val)
elif (switch == '--version'): print_version()
elif (switch[1] in 'u'): GV.USER = val
hf = (len(GV.opt['-h']) and 1 or 0) + (GV.opt['-f'] and 1 or 0)
if hf != 1:
usage('Error: please specify at least one of -h or -f args, but not both')
if (len(args) >= 1):
GV.argcmd = " ".join(args)
def sessionCleanup():
while True:
try:
return_code = 0
if GV.session:
if GV.session.verbose: print '\n[Cleanup...]';
return_code = GV.session.close()
GV.session = None
return return_code
except KeyboardInterrupt:
pass
sigint_time = 0
def sigint_handle(signum, frame):
global sigint_time
now = time.time()
if now - sigint_time >= 3:
sigint_time = now
raise KeyboardInterrupt
signal.signal(signal.SIGINT, signal.SIG_IGN)
print '\n[Exiting...]'
sys.exit(1)
def sighup_handle(signum, frame):
sys.exit(1)
def interactive():
try:
import readline
#Read in the saved command history, if any
histfile = os.path.join(os.environ["HOME"], ".gshist")
# Set the maximum number of commands to 100
readline.set_history_length(500)
try:
readline.read_history_file(histfile)
except IOError:
pass
#MPP-4054 - let's check the permissons before we register
try:
f=open(histfile,'a')
atexit.register(readline.write_history_file, histfile)
f.close()
except IOError:
print "\n[WARN] Unable to write to gpssh history file: '%s'. Please check permissions." % histfile
except Exception, e:
print "Note: command history unsupported on this machine ..."
atexit.register(sessionCleanup)
signal.signal(signal.SIGINT, sigint_handle)
signal.signal(signal.SIGHUP, sighup_handle)
while True:
try:
if not GV.session:
GV.session = ssh_utils.Session()
GV.session.verbose=GV.opt['-v']
GV.session.login(GV.opt['-h'], GV.USER)
GV.session.echoCommand=GV.opt['-e']
GV.session.cmdloop()
except pexpect.EOF:
print '\n[Unexpected EOF from some hosts...]'
pass
except ssh_utils.Session.SessionCmdExit:
print ''
break
except ssh_utils.Session.SessionError, e:
print 'Error: %s' % e
pass
except KeyboardInterrupt:
print '\n[Interrupt...]'
GV.session.reset()
pass
#############
coverage = GpCoverage()
coverage.start()
try:
parseCommandLine() #Read options from the command line
#Acquire the list of hosts from command line arguments
hostlist = ssh_utils.HostList()
for h in GV.opt['-h']:
hostlist.add(h)
if GV.opt['-f']:
hostlist.parseFile(GV.opt['-f'])
#Filter out non-unique hostnames unless the -D option is provided
if not GV.opt['-D']:
GV.opt['-h'] = hostlist.filterMultiHomedHosts()
else:
GV.opt['-h'] = hostlist.get()
if len(GV.opt['-h']) == 0:
usage('Error: missing hosts in -h and/or -f arguments')
#If a single command was passed to us, implement a single command session
if GV.argcmd:
try:
GV.session = ssh_utils.Session()
GV.session.verbose=GV.opt['-v']
GV.session.login(GV.opt['-h'], GV.USER)
GV.session.echoCommand=GV.opt['-e']
output=GV.session.executeCommand(GV.argcmd)
GV.session.writeCommandOutput(output)
if GV.session.verbose: print '[INFO] completed successfully'
sys.stdout.flush()
except ssh_utils.Session.SessionError, e:
print 'Error: %s' % e
pass
else: #Otherwise, implement an interactive session
interactive()
return_code = sessionCleanup()
sys.exit(return_code)
except KeyboardInterrupt:
sessionCleanup()
sys.exit('\nInterrupted...')
finally:
coverage.stop()
coverage.generate_report()