#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'ice'
__version__ = '1.0.0'


from common import (
    exec_cmd,
    get_version,
    is_valid_package,
    get_inner_ip,
    write_hostname,
    PKG_CONF
)

import os
import sys
import time
import yaml
import traceback

import init_env
import deployer
import deploy_one
import common
from common import update_logger as logger
from reporters.version_reporter import VersionReporter


def clean():
    exec_cmd(['/bin/rm -rf %s' % common.DOWNLOAD_PATH])


def backup_db(args):
    pass


def deploy(args):
    d = deployer.Deployer(args.tar, args.install_path)
    is_ok, err = d.deploy()
    return err

def get_instance_conf_prefix(install_path):
    import hashlib
    pkg_real_path = os.path.realpath(install_path)
    m = hashlib.md5()
    m.update(pkg_real_path)
    instance_md5 = m.hexdigest()
    return instance_md5[:7]

def collect_app_status(app_name, install_path):
    prefix = get_instance_conf_prefix(install_path)
    status_file = os.path.join(PKG_CONF, app_name, prefix, "package.status")
    if not os.path.exists(status_file):
        return False
    with open(status_file) as f:
        for line in f.readlines():
            line = line.strip()
            if line.startswith("status:"):
                if "started" in line:
                    return True
                return False
    return False


def collect_app_service_list(app_name, install_path):
    prefix = get_instance_conf_prefix(install_path)
    pkg_conf_file = os.path.join(PKG_CONF, app_name, prefix, "package.conf.yaml")
    if not os.path.exists(pkg_conf_file):
        return []
    with open(pkg_conf_file) as f:
        pkg_conf = yaml.load(f)
        if not pkg_conf.get("service_list"):
            return []
        return pkg_conf["service_list"]


def collect_one(app_name, install_path, pkg_conf):
    version = get_version(os.path.join(install_path, 'version.ini'))
    if not version and app_name == "install_agent":
        version = "2.10.8"
    if not version:
        return None
    app = {
        'name': app_name,
        'install_path': install_path,
        'version': version,
        'is_started': collect_app_status(app_name, install_path),
        'service_list': collect_app_service_list(app_name, install_path),
        'dependencies': pkg_conf.get("dependencies", []) or [],
        'start_dependencies': pkg_conf.get("start_dependencies", []) or [],
    }
    return app

def get_install_path(pkg_name):
    """
    根据包名获取包的安装路径
    :param pkg_name:
    :return:
    """
    pkg_conf_path = os.path.join(common.PKG_CONF, pkg_name)
    if not os.path.isdir(pkg_conf_path):
        return None
    instances = os.listdir(pkg_conf_path)
    if not instances:
        return None
    instance = instances[0]
    if not os.path.isdir(os.path.join(common.PKG_CONF, pkg_name, instance)):
        return None
    if not os.path.exists(os.path.join(common.PKG_CONF, pkg_name, instance, "package.conf.yaml")):
        return None
    try:
        with open(os.path.join(common.PKG_CONF, pkg_name, instance, "package.conf.yaml")) as f:
            pkg_conf = yaml.safe_load(f)
            return common.format_install_path(pkg_name, pkg_conf.get("install_path"))
    except Exception, e:
        return None


def remove_console_qianguize():
    if os.path.exists("/usr/local/easyops/pkg/conf/console-private") and os.path.exists("/usr/local/easyops/pkg/conf/console"):
        os.system("rm -rf /usr/local/easyops/pkg/conf/console-private")
    elif os.path.exists("/usr/local/easyops/pkg/conf/console-private"):
        os.rename("/usr/local/easyops/pkg/conf/console-private", "/usr/local/easyops/pkg/conf/console")


def get_pkg_conf(pkg_name):
    base_dir = os.path.join(common.PKG_CONF, pkg_name)
    dirs = os.listdir(base_dir)
    instance_dir = None
    for d in dirs:
        if os.path.isdir(os.path.join(base_dir, d)):
            instance_dir = os.path.join(base_dir, d)
            break
    if not instance_dir:
        raise Exception("pkg conf not found, pkg_name={0}".format(pkg_name))

    with open(os.path.join(instance_dir, "package.conf.yaml")) as f:
        pkg_conf = yaml.load(f)
    return pkg_conf


def collect(args):
    # 干掉console, console-private在pkg/conf的潜规则
    remove_console_qianguize()

    org = common.get_org()
    start_time = time.time()
    total_tasks = 1
    reporter = VersionReporter(total_tasks, org, args.client_ip, args.server)
    if not os.path.exists(common.PKG_CONF):
        try:
            reporter.finish_task(start_time, reporter.STATUS_OK, 'collect success', {
                'all_pkg': [],
                'deploy_status': {},
                'easyops_info': "",
                'org': org,
            })
        except Exception as e:
            logger.error(traceback.format_exc())
        return

    apps = []
    for path in os.listdir(common.PKG_CONF):
        # ignore /usr/local/easyops/agent
        if path == 'agent':
            continue
        install_path = get_install_path(path)
        if not install_path:
            continue
        if not is_valid_package(install_path):
            continue
        pkg_conf = get_pkg_conf(path)
        app = collect_one(path, install_path, pkg_conf)
        if app:
            apps.append(app)

    # data agent
    install_path = '/data/easyops/agent'
    app = collect_one("agent", install_path, {})
    if app:
        apps.append(app)

    # python
    install_path = '/usr/local/easyops/python'
    app = collect_one("python", install_path, {})
    if app:
        app["is_started"] = True
        apps.append(app)

    # collect deploy status
    easyops_info = common.get_easyops_info()
    data = {
        'all_pkg': apps,
        'easyops_info': easyops_info
    }
    
    try:
        reporter.finish_task(start_time, reporter.STATUS_OK, 'collect success', data)
    except Exception as e:
        logger.error(traceback.format_exc())

        
def update_env(args):
    env = '/usr/local/easyops/deploy_init/env.config'
    cmd = "cat %s | grep 'inner_ip'" % env
    code, msg = exec_cmd([cmd], check_call=False)
    if code != 0:  # inner_ip not found in env.config
        inner_ip = get_inner_ip()
        cmd = 'echo -e "\ninner_ip=%s\n" >> %s' % (inner_ip, env)
        code, msg = exec_cmd([cmd], check_call=False)
        if code != 0:
            raise Exception('failed to write inner_ip into env.config')

    hosts = args.hosts
    if hosts == 'null':
        return

    if '127.0.0.1' not in hosts:  # means it is a cluster env
        cmd = "cat %s | grep 'is_cluster'" % env
        code, msg = exec_cmd([cmd], check_call=False)
        if code != 0:  # is_cluster not found in env.config
            cmd = 'echo -e "\nis_cluster=true\n" >> %s' % (env)
            code, msg = exec_cmd([cmd], check_call=False)
            if code != 0:
                raise Exception('failed to write is_cluster into env.config')

    hosts = [item.strip() for item in hosts.split(',')]
    ip2host = {}
    for host in hosts:
        ip, host = host.split(':')
        ip2host[ip.strip()] = host.strip()

    # write hosts
    write_hostname(ip2host)


if __name__ == '__main__':
    from optparse import OptionParser

    op2func = {
        'init': init_env.init,
        'backup_db': backup_db,
        'deploy': deploy,
        'collect': collect,
        'update_env': update_env,
        'deploy_one': deploy_one.deploy_one,
    }

    logger.info('receive command, %s', sys.argv)

    parser = OptionParser('uc deploy script, version %s' % __version__)
    parser.add_option('-c', '--command', choices=op2func.keys(), help='uc deploy subcommand')
    parser.add_option('--hosts', help='init parameter, format like `host1:1.2.3.4,host2:1.2.3.5`')
    parser.add_option('--org', help='init parameter, set org in env.config')
    parser.add_option('--tar_path', help='path to tar package')
    parser.add_option('--install_path', help='install path of the tar package')
    parser.add_option('--pkg', help='pkg name to install')
    parser.add_option('-v', '--version', action='store_true', help='show version')
    parser.add_option('-s', '--server', help='uc server ip')
    parser.add_option('--client_ip', help='client ip')
    parser.add_option('--deploy_component',
                      choices=["ens", "runtime", "runtime-db", "logic", "logic-db", "easyops_init", "install_agent"],
                      help='deploy component')
    parser.add_option('--deploy_action',
                      choices=["deploy", "update_start_postscript", "install_start_postscript"],
                      default="deploy",
                      help='deploy action')
    parser.add_option('--ignore_deps', dest='ignore_deps', default=False, help='是否忽略依赖')
    (args, _) = parser.parse_args()

    if args.version:
        print('uc deploy, version %s' % __version__)
        sys.exit(0)

    if args.command == 'deploy':
        if not args.tar or not args.install_path or not args.server:
            print('deploy command, but miss parameter of tar or install_path or server')
            sys.exit(1)

        if not os.path.exists(args.tar):
            print('tar package %s not exists' % args.tar)
            sys.exit(1)

    if args.command == 'init':
        if not args.hosts or not args.org:
            print('init command, but miss hosts or org parameter')
            sys.exit(1)

    if args.command == 'update_env':
        if not args.hosts:
            print('update_env command, but miss hosts parameter')
            sys.exit(1)

    if args.command == 'deploy_one':
        if not args.org or not args.server or not args.client_ip or not args.deploy_action:
            print('deploy_all command, but miss parameter of org or server or client_ip or deploy_action')
            sys.exit(1)

    try:
        err = op2func[args.command](args)
    except Exception, e:
        logger.error("error, e={0}, traceback={1}".format(e, traceback.format_exc()))
        sys.exit(1)
    else:
        if err:
            if isinstance(err, unicode):
                err = err.encode("utf-8")
            print(err)
            sys.exit(1)
