blob: e656ca97a88ace5b4723cfc7f4e51d59710e0350 [file]
#!/usr/bin/env python3
'''
gpsync -- rsync to multiple hosts at once
Usage: gpsync [--version] [-?v] [-r] [-a] [-p port] [-u user]
[-h host] [-f hostfile] [-J host_substitution_character] [[user@]host1:]file1 [...] [[user@]hosts2:]file2
--version : print version information
-? : print this help screen
-v : verbose mode
-r : recursively copy entire directories
-a : archive mode; equals -rlptgoD (no -H,-A,-X)
-h host : ssh host to connect to (multiple -h is okay)
-f file : a file listing all hosts to connect to
-J character : character to be substitute as hostname [default='=']
'''
import os
import sys
import getopt
import subprocess
from gppylib.util import ssh_utils
from gppylib.gpparseopts import OptParser
from gppylib.parseutils import canonicalize_address
progname = os.path.split(sys.argv[0])[-1]
#
# all the command line options
#
class Global:
script_name = os.path.split(__file__)[-1]
USER = os.environ.get('LOGNAME') or os.environ.get('USER')
opt = {}
opt['-v'] = False
opt['-h'] = []
opt['-f'] = None
opt['-J'] = '=:'
opt['-r'] = False
opt['-a'] = False
filePath = []
GV = Global()
################
def usage(exitarg):
parser = OptParser()
try:
parser.print_help()
except:
print(__doc__)
sys.exit(exitarg)
#############
def print_version():
print('%s version $Revision$' % GV.script_name)
sys.exit(0)
#############
def parseCommandLine():
try:
(options, args) = getopt.getopt(sys.argv[1:], '?vraJ:p:u:h:f:', ['version'])
except Exception as e:
usage('Error: ' + str(e))
for (switch, val) in options:
if (switch == '-?'):
usage(0)
elif (switch == '-v'):
GV.opt[switch] = True
elif (switch == '-f'):
GV.opt[switch] = val
elif (switch == '-h'):
GV.opt[switch].append(val)
elif (switch == '-J'):
GV.opt[switch] = val + ':'
elif (switch == '-r'):
GV.opt[switch] = True
elif (switch == '-a'):
GV.opt[switch] = True
elif (switch == '--version'):
print_version()
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) < 2):
usage('Error: please specify local and remote file paths')
GV.filePath = args
#############
try:
parseCommandLine()
hostlist = ssh_utils.HostList()
for h in GV.opt['-h']:
hostlist.add(h)
if GV.opt['-f']:
hostlist.parseFile(GV.opt['-f'])
try:
hostlist.checkSSH()
except ssh_utils.SSHError as e:
sys.exit('[ERROR] ' + str(e))
GV.opt['-h'] = hostlist.filterMultiHomedHosts()
if len(GV.opt['-h']) == 0:
usage('Error: missing hosts in -h and/or -f arguments')
rsync = 'rsync -e "ssh -o BatchMode=yes -o StrictHostKeyChecking=no"'
if GV.opt['-r']: rsync += ' -r'
if GV.opt['-a']: rsync += ' -a'
proc = []
for peer in GV.opt['-h']:
peer = canonicalize_address(peer) # MPP-13617
cmd = rsync + ' '
for f in GV.filePath:
cmd += f.replace(GV.opt['-J'], '%s:' % peer) + ' '
if GV.opt['-v']: print('[INFO]', cmd)
proc.append(subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE))
withError = False
for p in proc:
for line in p.stdout.readlines():
print('[OUT %s] %s' % ("".join(p.args), line))
status = p.wait()
if status:
print('[ERROR %s] exit %d, cmd - %s' % (p.stderr.read(), status, "".join(p.args)))
withError=True
if withError: sys.exit(1)
if GV.opt['-v']: print('[INFO] completed successfully')
except KeyboardInterrupt:
sys.exit('\nInterrupted...')