#!/usr/bin/python
# (C) 2012, Michael DeHaan, <michael.dehaan@gmail.com>

# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

#######################################################

# Miguel: Version modificada para permitir el paso como parametro de
# la password ssh del usuario, y evitar los callbacks de ansible
# por el problema con el lock


import sys
import os

import ansible.playbook
import ansible.constants as C
import ansible.utils.template
from ansible import errors
from ansible import utils
from ansible.color import ANSIBLE_COLOR, stringc
# Miguel, quito los callbacks de ansible y pongo los simplificados
from ansible_callbacks import display, banner, AggregateStats, PlaybookCallbacks, PlaybookRunnerCallbacks

def colorize(lead, num, color):
    """ Print 'lead' = 'num' in 'color' """
    return "%s=%-4s" % (lead, str(num))

def hostcolor(host, stats, color=True):
    return "%-26s" % host

def main(args):
    ''' run ansible-playbook operations '''

    # create parser for CLI options
    parser = utils.base_parser(
        constants=C,
        usage = "%prog playbook.yml",
        connect_opts=True,
        runas_opts=True,
        subset_opts=True,
        check_opts=True,
        diff_opts=True
    )
    parser.add_option('-e', '--extra-vars', dest="extra_vars", action="append",
        help="set additional variables as key=value or YAML/JSON", default=[])
    parser.add_option('-t', '--tags', dest='tags', default='all',
        help="only run plays and tasks tagged with these values")
    parser.add_option('--skip-tags', dest='skip_tags',
        help="only run plays and tasks whose tags do not match these values")
    parser.add_option('--syntax-check', dest='syntax', action='store_true',
        help="perform a syntax check on the playbook, but do not execute it")
    parser.add_option('--list-tasks', dest='listtasks', action='store_true',
        help="list all tasks that would be executed")
    parser.add_option('--step', dest='step', action='store_true',
        help="one-step-at-a-time: confirm each task before running")
    parser.add_option('--start-at-task', dest='start_at',
        help="start the playbook at the task matching this name")

    # Miguel: anyado la opcion para tomar el password por parametro
    parser.add_option('-p','--ssh-password', dest='ssh_password',
        help="specify the ssh password to connect to the hosts", default=None)
    # Miguel: anyado la opcion un numero de reintentos
    parser.add_option('-r','--play-retries', dest='play_retries', type="int",
        help="specify the number of times the playbook will be retried in case of failure", default=1)

    options, args = parser.parse_args(args)

    if len(args) == 0:
        parser.print_help(file=sys.stderr)
        return 1

    inventory = ansible.inventory.Inventory(options.inventory)
    inventory.subset(options.subset)
    if len(inventory.list_hosts()) == 0:
        raise errors.AnsibleError("provided hosts list is empty")

    # Miguel: elimino la opcion de que pida el password interactivo 
    # y anyado la opcion para tomar el password por parametro
    sshpass = None
    sshpass = None    
    sudopass = None
    options.sudo_user = options.sudo_user or C.DEFAULT_SUDO_USER
    if options.ssh_password:
        sshpass = options.ssh_password
        sudopass = options.ssh_password

    extra_vars = {}
    for extra_vars_opt in options.extra_vars:
        if extra_vars_opt.startswith("@"):
            # Argument is a YAML file (JSON is a subset of YAML)
            extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml_from_file(extra_vars_opt[1:]))
        elif extra_vars_opt and extra_vars_opt[0] in '[{':
            # Arguments as YAML
            extra_vars = utils.combine_vars(extra_vars, utils.parse_yaml(extra_vars_opt))
        else:
            # Arguments as Key-value
            extra_vars = utils.combine_vars(extra_vars, utils.parse_kv(extra_vars_opt))

    only_tags = options.tags.split(",")
    skip_tags = options.skip_tags
    if options.skip_tags is not None:
        skip_tags = options.skip_tags.split(",")

    for playbook in args:
        if not os.path.exists(playbook):
            raise errors.AnsibleError("the playbook: %s could not be found" % playbook)
        if not os.path.isfile(playbook):
            raise errors.AnsibleError("the playbook: %s does not appear to be a file" % playbook)

    # run all playbooks specified on the command line
    for playbook in args:
        num_retries = 0
        return_code = 4
        while return_code != 0 and num_retries < options.play_retries:
            num_retries += 1
            return_code = 0

            # let inventory know which playbooks are using so it can know the basedirs
            inventory.set_playbook_basedir(os.path.dirname(playbook))
    
            stats = AggregateStats()
            playbook_cb = PlaybookCallbacks(verbose=utils.VERBOSITY)
            if options.step:
                playbook_cb.step = options.step
            if options.start_at:
                playbook_cb.start_at = options.start_at
            runner_cb = PlaybookRunnerCallbacks(stats, verbose=utils.VERBOSITY)
    
            pb = ansible.playbook.PlayBook(
                playbook=playbook,
                module_path=options.module_path,
                inventory=inventory,
                forks=options.forks,
                remote_user=options.remote_user,
                remote_pass=sshpass,
                callbacks=playbook_cb,
                runner_callbacks=runner_cb,
                stats=stats,
                timeout=options.timeout,
                transport=options.connection,
                sudo=options.sudo,
                sudo_user=options.sudo_user,
                sudo_pass=sudopass,
                extra_vars=extra_vars,
                private_key_file=options.private_key_file,
                only_tags=only_tags,
                skip_tags=skip_tags,
                check=options.check,
                diff=options.diff
            )
    
            if options.listhosts or options.listtasks:
                print ''
                print 'playbook: %s' % playbook
                print ''
                playnum = 0
                for (play_ds, play_basedir) in zip(pb.playbook, pb.play_basedirs):
                    playnum += 1
                    play = ansible.playbook.Play(pb, play_ds, play_basedir)
                    label = play.name
                    if options.listhosts:
                        hosts = pb.inventory.list_hosts(play.hosts)
                        print '  play #%d (%s): host count=%d' % (playnum, label, len(hosts))
                        for host in hosts:
                            print '    %s' % host
                    if options.listtasks:
                        matched_tags, unmatched_tags = play.compare_tags(pb.only_tags)
    
                        # Remove skipped tasks
                        matched_tags = matched_tags - set(pb.skip_tags)
    
                        unmatched_tags.discard('all')
                        unknown_tags = ((set(pb.only_tags) | set(pb.skip_tags)) -
                                        (matched_tags | unmatched_tags))
    
                        if unknown_tags:
                            continue
                        print '  play #%d (%s):' % (playnum, label)
    
                        for task in play.tasks():
                            if (set(task.tags).intersection(pb.only_tags) and not
                                set(task.tags).intersection(pb.skip_tags)):
                                if getattr(task, 'name', None) is not None:
                                    # meta tasks have no names
                                    print '    %s' % task.name
                    print ''
                continue
    
            if options.syntax:
                # if we've not exited by now then we are fine.
                print 'Playbook Syntax is fine'
                return 0

            failed_hosts = []
            unreachable_hosts = []
    
            try:
    
                pb.run()
    
                hosts = sorted(pb.stats.processed.keys())
                display(banner("PLAY RECAP"))
                playbook_cb.on_stats(pb.stats)
    
                for h in hosts:
                    t = pb.stats.summarize(h)
                    if t['failures'] > 0:
                        failed_hosts.append(h)
                    if t['unreachable'] > 0:
                        unreachable_hosts.append(h)
    
                retries = failed_hosts + unreachable_hosts
    
                #if len(retries) > 0:
                #    filename = pb.generate_retry_inventory(retries)
                #    if filename:
                #        display("           to retry, use: --limit @%s\n" % filename)
    
                for h in hosts:
                    t = pb.stats.summarize(h)
    
                    display("%s : %s %s %s %s" % (
                        hostcolor(h, t),
                        colorize('ok', t['ok'], 'green'),
                        colorize('changed', t['changed'], 'yellow'),
                        colorize('unreachable', t['unreachable'], 'red'),
                        colorize('failed', t['failures'], 'red')),
                        screen_only=True
                    )
    
                    display("%s : %s %s %s %s" % (
                        hostcolor(h, t, False),
                        colorize('ok', t['ok'], None),
                        colorize('changed', t['changed'], None),
                        colorize('unreachable', t['unreachable'], None),
                        colorize('failed', t['failures'], None)),
                        log_only=True
                    )

    
                print ""
                if len(failed_hosts) > 0:
                    return_code = 2
                if len(unreachable_hosts) > 0:
                    return_code = 3
    
            except errors.AnsibleError, e:
                display("ERROR: %s" % e, color='red')
                return_code = 1
            
            if return_code != 0:
                display("ERROR executing playbook (%s/%s)" % (num_retries, options.play_retries), color='red')

    return return_code


if __name__ == "__main__":
    display(" ", log_only=True)
    display(" ".join(sys.argv), log_only=True)
    display(" ", log_only=True)
    try:
        sys.exit(main(sys.argv[1:]))
    except errors.AnsibleError, e:
        display("ERROR: %s" % e, color='red', stderr=True)
        sys.exit(1)

