#!/usr/bin/env python2
# vim: expandtab:tabstop=4:shiftwidth=4
''' scp wrapper using multi-inventory for hosts/nodes '''

import argparse
import traceback
import sys
import os
import re
import ConfigParser

# openshift-tools python RPMs not installed on jenkins instance
# pylint: disable=import-error
from openshift_tools.inventory_clients import inventory_util

CONFIG_MAIN_SECTION = 'main'

# Allow more than 7 instance attributes
# pylint: disable=too-many-instance-attributes
class Oscp(object):
    ''' Oscp class to wrap scp with multi-inventory awareness '''
    def __init__(self):
        self.host = None
        self.user = ''
        self.local_src = None
        self.path = None
        self.host_inventory = None
        self.file_path = os.path.join(os.path.dirname(os.path.realpath(__file__)))

        # Default the config path to /etc
        self.config_path = os.path.join(os.path.sep, 'etc',  \
                                        'openshift_tools', \
                                        'openshift_tools.conf')

        self.args, self.parser = self.parse_cli_args()
        self.parse_config_file()

        # parse host and user
        self.process_host()

        self.inventory_util = inventory_util.InventoryUtil()

        # get a dict of host inventory
        self.get_hosts()

        if (self.args.src == '' or self.args.dest == '') and not self.args.list:
            self.parser.print_help()
            return

        if self.args.debug:
            print self.host
            print self.args

        # perform the scp
        if self.args.list:
            self.list_hosts()
        else:
            self.scp()

    def parse_config_file(self):
        ''' read in configuration from file '''
        if os.path.isfile(self.config_path):
            config = ConfigParser.ConfigParser()
            config.read(self.config_path)

    # pylint: disable=no-self-use
    def parse_cli_args(self):
        ''' process cli flags/settings '''
        parser = argparse.ArgumentParser(description='OpenShift Online SSH Tool.')
        parser.add_argument('-d', '--debug', default=False,
                            action="store_true", help="debug mode")
        parser.add_argument('-v', '--verbose', default=False,
                            action="store_true", help="Verbose?")
        parser.add_argument('--refresh-cache', default=False,
                            action="store_true", help="Force a refresh on the host cache.")
        parser.add_argument('--list', default=False,
                            action="store_true", help="list out hosts")
        parser.add_argument('-r', '--recurse', action='store_true', default=False,
                            help='Recursively copy files to or from destination.')
        parser.add_argument('-o', '--ssh_opts', action='store',
                            help='options to pass to SSH.\n \
                                  "-oPort=22,TCPKeepAlive=yes"')

        parser.add_argument('src', nargs='?', default='')
        parser.add_argument('dest', nargs='?', default='')

        args = parser.parse_args()
        return args, parser

    def process_host(self):
        '''Determine host name and user name for SSH.
        '''
        # is the first param passed a valid file?
        if os.path.isfile(self.args.src) or os.path.isdir(self.args.src):
            self.local_src = True
            self.host = self.args.dest
        else:
            self.local_src = False
            self.host = self.args.src

        if '@' in self.host:
            re_host = re.compile("(.*@)(.*)(:.*$)")
        else:
            re_host = re.compile("(.*)(:.*$)")

        search = re_host.search(self.host)

        if search:
            if len(search.groups()) > 2:
                self.user = search.groups()[0]
                self.host = search.groups()[1]
                self.path = search.groups()[2]
            else:
                self.host = search.groups()[0]
                self.path = search.groups()[1]

    def get_hosts(self):
        '''Query our host inventory and return a dict where the format '''
        self.host_inventory = self.inventory_util.inventory['_meta']['hostvars']

    def find_host_by_attr(self, attribute):
        '''find self.host by an attribute'''
        results = {}
        for host, values in self.host_inventory.iteritems():
            if not values.has_key(attribute):
                continue
            if self.host == values[attribute]:
                results[host] = values

        return results

    def select_host(self):
        '''select host attempts to match the host specified
           on the command line with a list of hosts.
        '''
        results = {}
        if self.host_inventory.has_key(self.host):
            results[self.host] = self.host_inventory[self.host]
        # check for an ec2_private_dns_name, like: ip-172-31-49-44.ec2.internal
        elif self.host.startswith("ip-"):
            results.update(self.find_host_by_attr('ec2_private_dns_name'))
        # check for an aws instance id, like: i-0015104cc95b00f77
        elif self.host.startswith("i-"):
            results.update(self.find_host_by_attr('oo_id'))

        # if all else fails, try to match oo_name
        else:
            results.update(self.find_host_by_attr('oo_name'))

        if results == {}:
            print "Could not find specified host: %s." % self.host

        if len(results) > 1:
            print("Found multiple possible hosts: %s" % ",".join(results))
            return None

        if len(results) == 0:
            return None

        return results.items()[0]

    def list_hosts(self):
        '''Function to print out the host inventory.
        '''
        for _, server_info in self.host_inventory.items():
            print '%-42s %-18s %-8s %-25s %-18s %-18s' % (\
                  server_info.get('oo_name', 'None'),
                  server_info.get('oo_clusterid', 'None'),
                  server_info.get('oo_environment', 'None'),
                  server_info.get('oo_id', 'None'),
                  server_info.get('oo_public_ip', 'None'),
                  server_info.get('oo_private_ip', 'None'))

    def scp(self):
        '''scp files to or from a specified host
        '''
        try:
            # shell args start with the program name in position 1
            scp_args = ['/usr/bin/scp']

            if self.args.verbose:
                scp_args.append('-v')

            if self.args.recurse:
                scp_args.append('-r')

            if self.args.ssh_opts:
                for arg in self.args.ssh_opts.split(","):
                    scp_args.append("-o%s" % arg)

            results = self.select_host()

            if self.args.debug:
                print results

            if not results:
                return  # early exit, no results

            # Assume we have one and only one.
            server_info = results[1]

            host_str = "%s%s%s" % (self.user, server_info['oo_public_ip'], self.path)

            if self.local_src:
                scp_args.append(self.args.src)
                scp_args.append(host_str)
            else:
                scp_args.append(host_str)
                scp_args.append(self.args.dest)

            print "Running: %s\n" % ' '.join(scp_args)

            os.execve('/usr/bin/scp', scp_args, os.environ)
        # want to catch any and all exceptions
        # pylint: disable=bare-except
        except:
            print traceback.print_exc()
            print sys.exc_info()


if __name__ == '__main__':
    Oscp()

