#!/usr/bin/python -W ignore::DeprecationWarning
# -*- coding: utf-8 -*-
#
# archipel-initinstall
#
# Copyright (C) 2010 Antoine Mercadal <antoine.mercadal@inframonde.eu>
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.


import os
import sqlite3
import sys
import string
import uuid
from socket import gethostname, getfqdn
from random import choice
from optparse import OptionParser
from pkg_resources import Requirement, resource_filename


def success(msg):
    """
    Print a standardized success message
    @type msg: String
    @param msg: the message to print
    """
    print "\033[32mSUCCESS: %s\033[0m" % msg

def warn(msg):
    """
    Print a standardized warning message
    @type msg: String
    @param msg: the message to print
    """
    print "\033[33mWARNING: %s\033[0m" % msg

def error(msg, exit=True):
    """
    Print a standardized success message
    @type msg: String
    @param msg: the message to print
    @type exit: Boolean
    @param exit: if True, exit after print
    """
    print "\033[31mERROR: %s\033[0m" % msg
    if exit:
        sys.exit(1)

def msg(msg, exit=True):
    """
    Print a standardized neutral message
    @type msg: String
    @param msg: the message to print
    @type exit: Boolean
    @param exit: if True, exit after print
    """
    print "\033[35mMESSAGE: %s\033[0m" % msg

def ask(message, answers=None, default=None):
    question = " * " + message
    if answers and default:
        question += " ["
        for a in answers:
            a = a
            if default and a in (default): a = "\033[32m" + a + "\033[0m"
            question += a + "/"
        question = question[:-1]
        question += "]"
    if not answers and default:
        question += " [\033[32m" + default + "\033[0m]"
    question += " : "
    resp = raw_input(question)
    if default:
        if resp == "": resp = default;
    if answers and default:
        if not resp in answers and len(answers) > 0:
            resp = ask("\033[33mYou must select of the following answer\033[0m", answers, default);
    return resp


def ask_bool(message, default="y"):
    if ask(message, ["y", "n"], default) == "y":
        return True
    return False


def clean_installation(prefix=""):
    """
    Clean all traces of Archipel
    """
    try:
        warn("Using --force will remove ALL your Archipel Installation files")
        warn("This mean you will lost all your configuration and data")
        warn("But, it keeps the virtual machine folder, so VM should be safe.")
        warn("Do this only if you are completely sure.")

        if not ask_bool("Do you really want to force the reinstallation?", default="n"):
            print "ouf.."
            sys.exit(0)
        if not ask_bool("Really really sure (last chance)?", default="n"):
            print "ouf.."
            sys.exit(0)

        warn("Ok. you have been warned...")
        msg("cleaning old existing files")
        msg("cleaning init script from %s/etc/init.d/archipel" % prefix)
        os.system("rm -rf %s/etc/init.d/archipel" % prefix)
        msg("Cleaning library folders from %s/var/lib/archipel" % prefix)
        os.system("rm -rf %s/var/lib/archipel" % prefix)
        msg("Cleaning configuration file from %s/etc/archipel" % prefix)
        os.system("rm -rf %s/etc/archipel" % prefix)
        msg("Cleaning log files %s/var/log/archipel" % prefix)
        os.system("rm -rf %s/var/log/archipel" % prefix)
        success("Previous installation cleaned")
    except Exception as ex:
        error(str(ex))


def install_init_script(prefix, init_script):
    """
    Install the init script
    @type prefix: string
    @param prefix: the installation prefix
    @type init_script: string
    @param init_script: origin path of the init script
    """
    msg("Installing init script to %s/etc/init.d/archipel" % prefix)

    if os.path.exists("%s/etc/init.d/archipel" % prefix):
        warn("Init script already exists. Leaving as it is.")
        return
    if os.system("cp '%s' '%s/etc/init.d/archipel'" % (init_script, prefix)) == 0:
        success("Init script installed")
    else:
        error("Unable to copy the init script. Please install it by yourself", exit=False)
    if os.system("chmod 755 '%s/etc/init.d/archipel'" % prefix) == 0:
        success("Init script chmoded")
    else:
        error("Unable to chmod the init script", exit=False)

def install_config_folder(prefix, conf_folder):
    """
    Install the config file
    @type prefix: string
    @param prefix: the installation prefix
    @type conf_folder: string
    @param conf_folder: the path of conf folder
    """
    msg("Installing configuration to %s/etc/archipel" % prefix)
    if os.path.isdir("%s/etc/archipel/" % prefix):
        warn("Configuration files already exist. Leaving as it is")
        return False
    os.makedirs("%s/etc/archipel/" % prefix)
    if os.system("cp -a %s/* '%s/etc/archipel/'" % (conf_folder.replace(" ", "\\ "), prefix)) == 0:
        success("Config folder installed")
    else:
        error("Unable to install install config folder", exit=False)
    if os.system("chmod 600 '%s/etc/archipel/archipel.conf'" % prefix) == 0:
        success("Config file chmoded")
    else:
        error("Unable to chmod the config file", exit=False)
    return True


def pre_configure(prefix, conf_folder, hypervisor_name, xmpp_server):
    """
    Pre-configure the config file
    @type prefix: string
    @param prefix: the installation prefix
    @type conf_file: string
    @param conf_file: the path of conf file
    @type hypervisor_name: string
    @param hypervisor_name: requested name for hypervisor (Default: auto)
    @type xmpp_server: string
    @param xmpp_server: desired XMPP server FQDN (Default: auto)
    """
    msg("Pre-configuring %s/etc/archipel/archipel.conf:" % prefix)
    conf_in = open("%s/archipel.conf" % conf_folder)
    conf_out = open("%s/etc/archipel/archipel.conf" % prefix, "w")
    uuidgen = str(uuid.uuid4())
    if hypervisor_name == "auto":
        hypervisor_name = gethostname()
    if xmpp_server == "auto":
        xmpp_server = getfqdn()
    hypervisor_password = ''.join([choice(string.letters + string.digits) for i in xrange(8)])
    for line in conf_in:
        line = line.replace("PARAM_UUID",uuidgen)
        line = line.replace("PARAM_HYPERVISOR_NAME", hypervisor_name)
        line = line.replace("PARAM_HYPERVISOR_PASSWORD", hypervisor_password)
        line = line.replace("PARAM_XMPP_SERVER", xmpp_server)
        conf_out.write(line)
    conf_in.close()
    conf_out.close()

def install_data_folder(prefix, lib_folder):
    """
    Install the data folder
    @type prefix: string
    @param prefix: the installation prefix
    @type lib_folder: string
    @param lib_folder: the path of conf file
    """
    msg("Installing data folder to %s/var/lib/archipel" % prefix)
    if os.path.isdir("%s/var/lib/archipel" % prefix):
        warn("Lib folder already exists. Leaving as it is.")
        return
    os.makedirs("%s/var/lib/archipel" % prefix)
    if os.system("cp -a %s/* '%s/var/lib/archipel'" % (lib_folder.replace(" ", "\\ "), prefix)) == 0:
        success("Lib folder installed")
    else:
        error("Unable to install lib folder")

def get_data_files(prefix):
    """
    Try to find the data files
    @type prefix: string
    @param prefix: the installation prefix
    """
    init_script = resource_filename(Requirement.parse("archipel-agent"),"install/etc/init.d/archipel")
    conf_folder = resource_filename(Requirement.parse("archipel-agent"),"install/etc/archipel")
    lib_folder = resource_filename(Requirement.parse("archipel-agent"),"install/var/lib/archipel")

    # we are in the case of RPM installation
    if not os.path.exists(init_script):
        init_script = "%s/usr/install/etc/init.d/archipel" % prefix
    if not os.path.exists(conf_folder):
        conf_folder = "%s/usr/install/etc/archipel" % prefix
    if not os.path.exists(lib_folder):
        lib_folder = "%s/usr/install/var/lib/archipel" % prefix
    return (init_script, conf_folder, lib_folder)


def install(prefix="", xmpp_server="auto", hypervisor_name="auto"):

    init_script, conf_folder, lib_folder = get_data_files(prefix)

    msg("Installation initialization started")
    try:
        install_init_script(prefix, init_script)
        if install_config_folder(prefix, conf_folder):
            pre_configure(prefix, conf_folder, hypervisor_name, xmpp_server)
        install_data_folder(prefix,lib_folder )
    except Exception as ex :
        error(str(ex))
        sys.exit(1)

    success("Archipel installation complete")
    msg("IMPORTANT NOTE 1: you now need to edit /etc/archipel/archipel.conf top match your informations")
    msg("IMPORTANT NOTE 2: If you have used --name=auto and/or --xmpp-server=auto, please check values for xmpp_server and hypervisor_xmpp_jid")
    msg("IMPORTANT NOTE 3: if this is not already done, you need to run archipel-tagnode, archipel-rolesnode, archipel-adminaccount, archipel-vmparkingnode and archipel-vmrequestnode")


if __name__ == "__main__":
    parser = OptionParser()
    parser.add_option("-p", "--prefix",
                        dest="prefix",
                        help="all installation folders and files will be prefixed with given value",
                        metavar="PREFIX",
                        default="")
    parser.add_option("-f", "--force",
                        dest="clean",
                        help="before installing, clean all existing folder and files. IT WILL DESTROY YOUR CURRENT INSTALLATION, VM DRIVES etc. Be warned!",
                        action="store_true",
                        default=False)
    parser.add_option("-x", "--xmpp-server",
                        dest="xmpp_server",
                        help="set the fqdn of the XMPP server, defaults to local fqdn",
                        metavar="FQDN",
                        default="auto")
    parser.add_option("-n", "--hypervisor-name",
                        dest="hypervisor_name",
                        help="set the name of the hypervisor (left part of the JID), defaults to <hostname>",
                        metavar="SERVER",
                        default="auto")

    options, args = parser.parse_args()

    if options.clean:
        clean_installation(options.prefix)
    else:
        install(options.prefix, options.xmpp_server, options.hypervisor_name)
