#!/usr/bin/env python
# vim: expandtab:tabstop=4:shiftwidth=4

import argparse
import traceback
import sys
import os
import re
import tempfile
import time
import subprocess
import ConfigParser

from openshift_ansible import awsutil

DEFAULT_PSSH_PAR = 200
PSSH = '/usr/bin/pssh'
CONFIG_MAIN_SECTION = 'main'
CONFIG_HOST_TYPE_ALIAS_SECTION = 'host_type_aliases'
CONFIG_INVENTORY_OPTION = 'inventory'

class ArgumentMismatchError(ValueError): pass

class Opssh(object):
    def __init__(self):
        self.inventory = None
        self.host_type_aliases = {}
        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_ansible', \
                                        'openshift_ansible.conf')

        self.parse_cli_args()
        self.parse_config_file()

        self.aws = awsutil.AwsUtil(self.inventory, self.host_type_aliases)

    def run(self):
        if self.args.list_host_types:
            self.aws.print_host_types()
            return 0

        if self.args.env and \
           self.args.host_type and \
           self.args.command:
            return self.run_pssh()

        # If it makes it here, we weren't able to determine what they wanted to do
        raise ArgumentMismatchError("Invalid combination of arguments")

    def run_pssh(self):
        """Actually run the pssh command based off of the supplied options
        """

        # Default set of options
        pssh_args = [PSSH, '-t', '0', '-p', str(self.args.par), '--user', self.args.user]

        if self.args.inline:
            pssh_args.append("--inline")

        if self.args.outdir:
            pssh_args.extend(["--outdir", self.args.outdir])

        if self.args.errdir:
            pssh_args.extend(["--errdir", self.args.errdir])

        hosts = self.aws.get_host_list(self.args.host_type, self.args.env)
        with tempfile.NamedTemporaryFile(prefix='opssh-', delete=True) as f:
            for h in hosts:
                f.write(h + os.linesep)
            f.flush()

            pssh_args.extend(["-h", f.name])
            pssh_args.append(self.args.command)

            print
            print "Running: %s" % ' '.join(pssh_args)
            print
            return subprocess.call(pssh_args)

        return None

    def parse_config_file(self):
        if os.path.isfile(self.config_path):
            config = ConfigParser.ConfigParser()
            config.read(self.config_path)

            if config.has_section(CONFIG_MAIN_SECTION) and \
               config.has_option(CONFIG_MAIN_SECTION, CONFIG_INVENTORY_OPTION):
                self.inventory = config.get(CONFIG_MAIN_SECTION, CONFIG_INVENTORY_OPTION)

            self.host_type_aliases = {}
            if config.has_section(CONFIG_HOST_TYPE_ALIAS_SECTION):
                for alias in config.options(CONFIG_HOST_TYPE_ALIAS_SECTION):
                    value = config.get(CONFIG_HOST_TYPE_ALIAS_SECTION, alias).split(',')
                    self.host_type_aliases[alias] = value

    def parse_cli_args(self):
        """Setup the command line parser with the options we want
        """

        parser = argparse.ArgumentParser(description='Openshift Online PSSH Tool.')

        parser.add_argument('--list-host-types', default=False, action='store_true',
                       help='List all of the host types')

        parser.add_argument('-e', '--env', action="store",
                       help="Which environment to use")

        parser.add_argument('-t', '--host-type', action="store",
                       help="Which host type to use")

        parser.add_argument('-c', '--command', action='store',
                       help='Command to run on remote host(s)')

        parser.add_argument('--user', action='store', default='root',
                       help='username')

        parser.add_argument('-i', '--inline', default=False, action='store_true',
                       help='inline aggregated output and error for each server')

        parser.add_argument('-p', '--par', action='store', default=DEFAULT_PSSH_PAR,
                       help=('max number of parallel threads (default %s)' % DEFAULT_PSSH_PAR))

        parser.add_argument('--outdir', action='store',
                       help='output directory for stdout files')

        parser.add_argument('--errdir', action='store',
                       help='output directory for stderr files')

        self.args = parser.parse_args()


if __name__ == '__main__':
    if len(sys.argv) == 1:
        print "\nError: No options given. Use --help to see the available options\n"
        sys.exit(0)

    try:
        opssh = Opssh()
        exitcode = opssh.run()
        sys.exit(exitcode)
    except ArgumentMismatchError as e:
        print "\nError: %s\n" % e.message