#!/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, debops_root, playbooks_path, inventory_path):
    # Generate Ansible configuration file

    def plugin_paths(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)

    cfg = {}
    defaults = {
        'hostfile': inventory_path,
        'roles_path': PATHSEP.join((
            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))

    cfg = {'defaults': defaults}
    write_config(filename, cfg)


# ---- DebOps environment setup ----

def main(cmd_args):
    debops_root = find_debops_project(required=True)
    # :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)

    # 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
    tries = [
        (debops_root, "playbooks", "site.yml"),
        (debops_root, "ansible", "playbooks", "site.yml"),
        (playbooks_path, "site.yml"),
    ]
    if len(cmd_args) > 0:
        maybe_play = cmd_args[0]
        tries[:0] = [
            (maybe_play,),
            (debops_root, "playbooks", maybe_play + ".yml"),
            (debops_root, "ansible", "playbooks", maybe_play + ".yml"),
            (playbooks_path, maybe_play + ".yml"),
        ]
    else:
        # set for error message
        maybe_play = 'site.yml'
    for parts in tries:
        play = os.path.join(*parts)
        if os.path.isfile(play):
            break
    else:
        # loop finished without finding a playbook
        error_msg("Playbook %s not found" % maybe_play)

    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, debops_root, playbooks_path,
                    inventory_path)

    # Add custom configuration options to ansible.cfg
    # :todo: replace ansible_config_hook by data from .debops.cfg
    #if type(ansible_config_hook):
    #    ansible_config_hook #>> ${debops_root}/${ANSIBLE_CONFIG_FILE}

    # 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')
