#!/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...')
