#!/usr/bin/env python
# -*- coding: utf-8 -*-

# debops: run ansible-playbook with some customization
# Copyright (C) 2014 Hartmut Goebel <h.goebel@crazy-compilers.com>
# Part of the DebOps project - http://debops.org/


# This program 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 2 of the License,
# or (at your option) any later version.
#
# This program 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 this program; if not,
# write to the Free Software Foundation, Inc., 59
# Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# An on-line copy of the GNU General Public License can
# be downloaded from the FSF web page at:
# http://www.gnu.org/copyleft/gpl.html

from __future__ import print_function

import sys
import os
import ConfigParser

from debops import *
from debops.cmds import *

PATHSEP = ':'


def write_config(filename, config):
    cfgparser = ConfigParser.ConfigParser()
    for section, pairs in config.items():
        cfgparser.add_section(section)
        for option, value in pairs.items():
            cfgparser.set(section, option, value)

    with open(filename, "w") as fh:
        print("# Ansible configuration file generated by DebOps, "
              "all changes will be lost", file=fh)
        print(file=fh)
        cfgparser.write(fh)


def gen_ansible_cfg(filename, config, debops_root, playbooks_path,
                    inventory_path):
    # Generate Ansible configuration file

    def plugin_paths(type):
        if type in defaults:
            # prepend value from .debops.cfg
            yield defaults[type]
        yield os.path.join(debops_root, "ansible", plugin_type)
        yield os.path.join(playbooks_path, plugin_type)
        yield os.path.join("/usr/share/ansible_plugins", plugin_type)

    # Add custom configuration options to ansible.cfg: Take values
    # from [ansible ...] sections of the .debops.cfg file
    cfg = dict((sect.split(None, 1)[1], pairs)
               for sect, pairs in config.items() if sect.startswith('ansible '))

    defaults = cfg.setdefault('defaults', {})
    defaults['hostfile'] = inventory_path
    defaults['roles_path'] = PATHSEP.join(filter(None, (
        defaults.get('roles_path'), # value from .debops.cfg or None
        os.path.join(debops_root, "roles"),
        os.path.join(debops_root, "ansible", "roles"),
        os.path.join(playbooks_path, "..", "roles"),
        os.path.join(playbooks_path, "roles"),
        "/etc/ansible/roles")))
    for plugin_type in ('action', 'callback', 'connection',
                        'filter', 'lookup', 'vars'):
        plugin_type = plugin_type+"_plugins"
        defaults[plugin_type] = PATHSEP.join(plugin_paths(plugin_type))

    write_config(filename, cfg)


def main(cmd_args):
    debops_root = find_debops_project(required=True)
    config = read_config(debops_root)
    # :todo: Source DebOps configuration file
    #[ -r ${debops_config} ] && source ${debops_config}

    # Make sure required commands are present
    require_commands('ansible-playbook')

    playbooks_path = find_playbookpath(debops_root, required=True)

    def find_playbook(playbook):
        tries = [
            (debops_root, "playbooks", playbook),
            (debops_root, "ansible", "playbooks", playbook),
            (playbooks_path, playbook),
            ]
        for parts in tries:
            play = os.path.join(*parts)
            if os.path.isfile(play):
                return play

    # Check if user specified a potential playbook name as the first
    # argument. If yes, use it as the playbook name and remove it from
    # the argument list
    play = None
    if len(cmd_args) > 0:
        maybe_play = cmd_args[0]
        if os.path.isfile(maybe_play):
            play = maybe_play
        else:
            play = find_playbook(maybe_play + ".yml")
        if play:
            cmd_args.pop(0)
        del maybe_play
    if not play:
        play = find_playbook("site.yml")

    inventory_path = find_inventorypath(debops_root)
    os.environ['ANSIBLE_HOSTS'] = inventory_path

    ansible_config_file = os.path.join(debops_root, ANSIBLE_CONFIG_FILE)
    os.environ['ANSIBLE_CONIG'] = os.path.abspath(ansible_config_file)
    gen_ansible_cfg(ansible_config_file, config, debops_root, playbooks_path,
                    inventory_path)

    # Allow insecure SSH connections if requested
    if INSECURE:
        os.environ['ANSIBLE_HOST_KEY_CHECKING'] = 'False'

    # Create path to EncFS encrypted directory, based on inventory name
    encfs_encrypted = os.path.join(os.path.dirname(inventory_path),
                                   ENCFS_PREFIX + SECRET_NAME)

    # Check if encrypted secret directory exists and use it
    revert_unlock = padlock_unlock(encfs_encrypted)
    try:
        # Run ansible-playbook with custom environment
        print("Running Ansible playbook from:")
        print(play, "...")
        subprocess.call(['ansible-playbook', play] + cmd_args)
    finally:
        if revert_unlock:
            padlock_lock(encfs_encrypted)


try:
    main(sys.argv[1:])
except KeyboardInterrupt:
    raise SystemExit('... aborted')
