#!python

"""ants
====


Run ansible-pull with a set of defined parameters
and log the content of these runs.
"""

__version__ = '1.3.3'

__author__ = "Balz Aschwanden"
__email__ = "balz.aschwanden@unibas.ch"
__copyright__ = "Copyright 2017, University of Basel"

__credits__ = ["Balz Aschwanden", "Jan Welker"]
__license__ = "GPL"


import os
import sys
import subprocess
import datetime


from antslib import argparser
from antslib import logger
from antslib import configer

CFG = configer.read_config('main')

ANTS_PATH = os.path.dirname(os.path.realpath(__file__))
DYNAMIC_INVENTORY = os.path.join(ANTS_PATH, CFG['inventory_script'])
DESTINATION = os.path.expanduser(CFG['destination'])
_ROOT = os.path.abspath(os.path.dirname(__file__))


def find_file(f):
    """Search for a file in environment PATH and return first match or None"""
    paths = os.environ['PATH'].split(':')
    paths.insert(0, _ROOT)
    for path in paths:
        file_path = os.path.join(path, f)
        logger.console_logger.debug("Looking for %s in %s" % (f, path))
        if os.path.isfile(file_path):
            logger.console_logger.debug("Found %s at %s" % (f, file_path))
            return file_path
    return None


def parse_proc(proc):
    """Read subprocess output and dispatch it to logger.


    Cases handled separately:
        * task_line
            * Line with the name of a task.
            * Printed directly befor the task status.
        * recap_line
            * A single line a the end of an Ansible run.
            * It indicates the number of failes/changed/ok tasks.
            * The line after 'PLAY RECAP' contains the recap
    """
    task_line = None
    recap_line = None
    get_recap = False
    start_run_time = datetime.datetime.now()
    for line in iter(proc.stdout.readline, ''):
        logger.write_log(line, task_line)
        if line.startswith('TASK'):
            task_line = line
        if get_recap:
            recap_line = line
        get_recap = bool(line.startswith('PLAY RECAP'))

    end_run_time = datetime.datetime.now()
    if recap_line is not None:
        logger.log_recap(start_run_time, end_run_time, recap_line)
    return


def run_ansible(args):
    """Run ansible-pull.


    The ansible python api is provided as is and the core team
    reserve the right to push breakting changed.

    Hence, we call the cli directly and do not attempt to work with the api.

    Documentation:
    http://docs.ansible.com/ansible/dev_guide/developing_api.html
    """
    ansible_pull_exe = args.ansible_pull_exe
    if not ansible_pull_exe:
        logger.console_logger.debug(
            "Variable ansible_pull_exe is not set. Searching in PATH.")
        ansible_pull_exe = find_file('ansible-pull')
    if not ansible_pull_exe:
        sys.exit("Could not find executable ansible-pull. Aborting.")
    logger.console_logger.debug("Using %s" % ansible_pull_exe)

    for f in [ansible_pull_exe, args.inventory]:
        if not os.path.isfile(f):
            sys.exit("Could not find file at %s. Aborting." % f)
        if not os.access(f, os.X_OK):
            sys.exit("File is not executable at %s. Aborting." % f)

    cmd = [ansible_pull_exe,
           '--clean',
           '-f',
           '-i', args.inventory,
           '-d', args.destination,
           '-U', args.git_repo,
           '-C', args.branch,
           args.playbook]
    if os.path.isfile(args.ssh_key):
        logger.console_logger.debug("Found ssh key at %s" % args.ssh_key)
        cmd.append('--private-key')
        cmd.append(args.ssh_key)
    else:
        logger.console_logger.debug("No key found at %s" % args.ssh_key)
    if args.verbose:
        cmd.append('-v')
    if not args.stricthostkeychecking:
        logger.console_logger.debug(
            "Strict host key checking for ansible-pull is disabled.")
        cmd.append('--accept-host-key')
    if args.wait:
        logger.console_logger.debug(
            "Running ansible-pull with a random wait intervall of %s sec" % CFG['wait_interval'])
        cmd.append('-s')
        cmd.append(CFG['wait_interval'])

    proc = subprocess.Popen(cmd, bufsize=1, stdout=subprocess.PIPE,
                            stderr=subprocess.PIPE)
    parse_proc(proc)
    return proc.poll()


def __main__():
    args = argparser.parse_args(__version__, os.path.join(CFG['log_dir'], 'recap.log'),
                                DESTINATION, CFG)
    if args.verbose:
        logger.console_logger.setLevel(logger.logging.DEBUG)
    if not configer.is_root():
        sys.exit('Script must be run as root')
    logger.console_logger.debug("Running ansible-pull in verbose mode")
    logger.status_file_rollover()
    run_ansible(args)


if __name__ == "__main__":
    __main__()
