#!/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

from debops import *
from debops.cmds import *

PATHSEP = ':'

def gen_ansible_cfg(filename, debops_root, playbooks_path, inventory_path):
    # Generate Ansible configuration file
    def p(*args): print(*args, file=fh)
    with open(filename, "w") as fh:
        p("# Ansible configuration file generated by DebOps, all changes will be lost")
        p()
        p("[defaults]")
        p("hostfile           = ", inventory_path)
        p()
        p("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")
        ))
        p("action_plugins     =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "action_plugins"),
            os.path.join(playbooks_path, "action_plugins"),
            "/usr/share/ansible_plugins/action_plugins")
        ))
        p("callback_plugins   =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "callback_plugins"),
            os.path.join(playbooks_path, "callback_plugins"),
            "/usr/share/ansible_plugins/callback_plugins")
        ))
        p("connection_plugins =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "connection_plugins"),
            os.path.join(playbooks_path, "connection_plugins"),
            "/usr/share/ansible_plugins/connection_plugins")
         ))
        p("lookup_plugins     =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "lookup_plugins"),
            os.path.join(playbooks_path, "lookup_plugins"),
            "/usr/share/ansible_plugins/lookup_plugins")
        ))
        p("vars_plugins       =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "vars_plugins"),
            os.path.join(playbooks_path, "vars_plugins"),
            "/usr/share/ansible_plugins/vars_plugins")
        ))
        p("filter_plugins     =", PATHSEP.join((
            os.path.join(debops_root, "ansible", "filter_plugins"),
            os.path.join(playbooks_path, "filter_plugins"),
            "/usr/share/ansible_plugins/filter_plugins")
        ))

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