#!/usr/bin/python
# -*- coding: utf-8 -*-
# @brief   : 2018/10/10 16:56
# @File    : app ctrl
#  @Software: Software management infrastructure

import os
import re
import shutil
import sys
import logging
import tempfile
import time
import yaml
from distutils.sysconfig import get_python_lib

logging.basicConfig(level=logging.INFO,
                    filename="/OSM/log/cur_debug/startup.log",
                    format='[%(asctime)s][%(levelname)s][%(message)s][%(filename)s, %(lineno)d]',
                    datefmt='%Y-%m-%d %H:%M:%S')

TOP_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
MY_NAME = os.path.basename(__file__)
REPO_DIR = os.path.join(TOP_DIR, "repo")

DISK_RUN_DIR = "/OSM/coffer_bin/swm"
MEMORY_RUN_DIR = "/OSM/component/swm"
RUN_DIR = MEMORY_RUN_DIR
PKG_CUR = '/startup_disk/image/pkg_cur/packages'
SYS_CUR_PATH = "/startup_disk/image/pkg_cur"
OLD_SWM_LIB = "/OSM/coffer_data/swm/old_swm_lib"
NEW_SWM_LIB = "/OSM/coffer_data/swm/new_swm_lib"
USR_LOCAL_LIB = "/usr/local/lib"
SWM_LOCAL_LIB = "/OSM/component/swm/lib"
THRIFT_UPDATE_VERSION = "6200811026"

CMD = 'python {cmd:s} {param:s}'
START_CMD = CMD.format(cmd=os.path.join(RUN_DIR, 'deploy.py'), param='start')
STOP_CMD = CMD.format(cmd=os.path.join(RUN_DIR, 'deploy.py'), param='stop')
RESTART_CMD = 'nohup ' + CMD.format(cmd=os.path.join(RUN_DIR, 'deploy.py'), param='restart &')
SWM_RPM_DB_PATH = '/var/lib/swm_rpm'
NODE_TYPE = os.getenv('NODE_TYPE', None)

if NODE_TYPE == 'DATA_NODE':
    REPO_DIR = REPO_DIR + '_data'

# INSTALL 加载执行的脚本
INSTALL_EXEC_CMD = [
    'sh /OSM/script/product_mk_disk_dir.sh',
    'sh /OSM/script/parse_initrd_config.sh',
    'sh /OSM/script/os_modify_sftp_group_user_dir.sh',
    'sh /OSM/script/os_copy_versions.sh',
    'sh /OSM/script/patch_stage1.sh',
    'cp -af %s/manifest.yml %s/conf' % (TOP_DIR, RUN_DIR)
] if NODE_TYPE == 'CTRL_NODE' else [
    'sh /OSM/script/parse_initrd_config.sh',
    'sh /OSM/script/product_pre_handle.sh',
    'sh /OSM/script/product_mk_disk_dir.sh',
    'mkdir -p /OSM/script/setup.hook/before',
    'mkdir -p /OSM/script/setup.hook/after',
    'cp -af %s/manifest.yml %s/conf' % (TOP_DIR, RUN_DIR)
]
# start 加载执行的脚本
START_EXEC_CMD = [
    'sh /OSM/script/set_remote_ip_user.sh',
    '/OSM/bin/daemon',
    '/sbin/ldconfig'
 ]

USAGE = '''
Usage:
{name:s} [option]

Options:
    start
    stop
    online
    offline   
    install
    uninstall
    check_status
    restart
    pre_upgrade
    upgrade
    check_upgrade  
    '''.format(name=MY_NAME)


def show_help():
    print(USAGE)
    return


def symlink(src, link):
    """

    :param src:
    :param link:
    :return:
    """
    if os.path.islink(link):
        os.unlink(link)
    os.symlink(src, link)
    return


def unlink(link):
    """

    :param link:
    :return:
    """
    os.unlink(link)
    return


class PkgModel(object):
    """

    """
    PTH_FILE = os.path.join(get_python_lib(), "swm.pth")
    DIAGNOSE_LINK = "/usr/local/bin/swm_diagnose"
    DIAGNOSE_CLIENT = os.path.join(RUN_DIR, "infra/diagnose/diag_client.py")

    @classmethod
    def add_pth_2_path(cls, pkg_dir):
        """

        :return:
        """
        with open(cls.PTH_FILE, "w+") as f:
            f.write(pkg_dir)
        return

    @classmethod
    def remove_pth_from_path(cls):
        """

        :return:
        """
        if os.path.isfile(cls.PTH_FILE):
            os.remove(cls.PTH_FILE)
        return

    @classmethod
    def link_diagnose(cls):
        """

        :return:
        """
        cls.unlink_diagnose()
        symlink(cls.DIAGNOSE_CLIENT, cls.DIAGNOSE_LINK)
        return

    @classmethod
    def unlink_diagnose(cls):
        """

        :return:
        """
        if os.path.exists(cls.DIAGNOSE_LINK):
            unlink(cls.DIAGNOSE_LINK)
        return

    @classmethod
    def install(cls):
        """

        :return:
        """
        # Link to pkg dir
        try:
            cls.add_pth_2_path(RUN_DIR)
            cls.link_diagnose()
        except Exception as e:
            logging.error("install package model excepted %s.", e)
            return False
        logging.info("Install package model successfully.")
        return True

    @classmethod
    def uninstall(cls):
        """

        :return:
        """
        cls.remove_pth_from_path()
        cls.unlink_diagnose()

        # Delete history installed files
        if not os.path.exists(RUN_DIR):
            logging.warning("The pkg has not been installed.")
            return True
        logging.info("Uninstall pkg model successfully.")
        return True


def start():
    for cmd in START_EXEC_CMD:
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("START: exec script (%s) failed", cmd)
    time.sleep(1)
    # temp use for start auto test

    ret_code = os.system(START_CMD)
    if ret_code:
        logging.error("start swm service failed")
        return 0
    return 0


def stop():
    ret_code = os.system(STOP_CMD)
    if ret_code:
        logging.error("stop swm service failed")
        return 1
    return 0


def restart():
    ret_code = os.system(RESTART_CMD)
    if ret_code:
        logging.error("restart swm service failed")
        return 1
    return 0


def online():
    return 0


def offline():
    return 0


def create_rpm_dir():
    """
    创建rpm的db目录
    :return:
    """
    if not os.path.exists(SWM_RPM_DB_PATH):
        cmd = "mkdir -p %s" % SWM_RPM_DB_PATH
        os.system(cmd)


def process_swm_pkg():
    tmp_dir = ""
    try:
        upgrade_flag = "/OSM/coffer_data/swm/upgrade_flag"
        if not os.path.exists(upgrade_flag):
            return
        if str(os.readlink(SYS_CUR_PATH)).split('/')[0] >= THRIFT_UPDATE_VERSION:
            return
        tmp_dir = tempfile.mkdtemp('_temp_swm_pkg')
        swm_pkg = os.path.join(tmp_dir, "swm_pkg")
        os.mkdir(swm_pkg)
        cmd = "tar xzf %s -C %s" % (PKG_CUR + "/swm-*", swm_pkg)
        if os.system(cmd):
            logging.error("STARTUP: decompress swm pkg failed.")
            return
        swm_dir_list = [d for d in os.listdir(swm_pkg) if d.startswith('swm')]
        if not swm_dir_list:
            logging.error("STARTUP: pkg contents error.")
            return
        app_ctl = os.path.join(swm_pkg, swm_dir_list[0], 'action', 'appctl')
        if os.system('%s install' % app_ctl):
            logging.error("STARTUP: install swm pkg failed.")
            return
        logging.info("STARTUP: Install component(swm) lib files successfully.")
    except Exception as e:
        logging.error("STARTUP:process swm lib error, " + str(e))
    finally:
        shutil.rmtree(tmp_dir, ignore_errors=True)
    return


def install():
    create_rpm_dir()
    # 到repo目录下取出rpm包来进行安装
    process_swm_pkg()
    upgrade_flag = "/OSM/coffer_data/swm/upgrade_flag"

    if not os.path.exists(upgrade_flag) and os.path.exists(DISK_RUN_DIR):
        try:
            shutil.rmtree(DISK_RUN_DIR)
            logging.info('INSTALL: delete dir:%s successfully.', DISK_RUN_DIR)
        except Exception:
            logging.error('INSTALL: delete dir:%s fail.', DISK_RUN_DIR)

    for rpm_file in os.listdir(REPO_DIR):
        if not rpm_file.endswith('.rpm'):
            continue
        cmd = "rpm -ivh --nodeps --force --noscripts --dbpath=%s %s" % (
            SWM_RPM_DB_PATH, os.path.join(REPO_DIR, rpm_file))
        temp = ""
        if os.path.exists(upgrade_flag):
            cur_version = str(os.readlink(SYS_CUR_PATH)).split('/')[0]
            image_dir = [d for d in os.listdir("/startup_disk/image") if re.match(r"^\d+$", d)]
            for d in image_dir:
                if d > cur_version and cur_version < THRIFT_UPDATE_VERSION:
                    temp = tempfile.mkdtemp("_swm_lib_")
                    cmd = "rpm -ivh --relocate /usr/local/lib=%s --nodeps --force --noscripts --dbpath=%s %s" % (
                        temp, SWM_RPM_DB_PATH, os.path.join(REPO_DIR, rpm_file))
                    break
        if temp:
            shutil.rmtree(temp, ignore_errors=True)
        os.system("rm -rf /OSM/component/swm/lib/libthrift*")
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("install swm service failed, cmd(%s)", cmd)
            return 1
    PkgModel.install()
    for cmd in INSTALL_EXEC_CMD:
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("INSTALL:exec script (%s) failed", cmd)
            continue
    return 0


def uninstall():
    create_rpm_dir()
    PkgModel.uninstall()
    # 到repo目录下取出rpm包来卸载
    for rpm_file in os.listdir(REPO_DIR):
        rpm_name = rpm_file[0:rpm_file.index(".rpm")]
        cmd = "rpm -e --nodeps --dbpath=%s %s" % (SWM_RPM_DB_PATH, rpm_name)
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("uninstall swm service failed")
            return -1
    return 0


def check_status():
    return 0


# 检查组件升级模式
def check_upgrade():
    old_cfg_path = os.path.join(MEMORY_RUN_DIR, 'conf', 'manifest.yml')
    if not os.path.exists(old_cfg_path):
        old_cfg_path = os.path.join(DISK_RUN_DIR, 'conf', 'manifest.yml')

    new_cfg_path = os.path.join(TOP_DIR, 'manifest.yml')
    old_cfg = None
    new_cfg = None
    with open(old_cfg_path, 'r') as fp:
        content = fp.read()
        old_cfg = yaml.safe_load(content)
    with open(new_cfg_path, 'r') as fp:
        content = fp.read()
        new_cfg = yaml.safe_load(content)
    if not (old_cfg and new_cfg):
        logging.error("Get yaml info failed(old: %s, new: %s).", old_cfg, new_cfg)
        return 0
    old_ver = old_cfg.get('Version')
    new_ver = new_cfg.get('Version')
    if old_ver == new_ver:
        logging.info("The old version is same(%s) with new version, no need upgrade.", old_ver)
        return 1
    logging.info("The old version(%s) is different with new version(%s), need upgrade.", old_ver, new_ver)
    return 2


def upgrade():
    create_rpm_dir()
    if os.path.exists("/OSM/conf/product.ini"):
        cmd = "mv /OSM/conf/product.ini /OSM/conf/product_backups.ini"
        os.system(cmd)
    # 到repo目录下取出rpm包来进行安装
    for rpm_file in os.listdir(REPO_DIR):
        if not rpm_file.endswith('.rpm'):
            continue
        os.system("rm -rf /OSM/component/swm/lib/libthrift*")
        temp = tempfile.mkdtemp("_swm_lib_upgrade_")
        cmd = "rpm -ivh --relocate /usr/local/lib=%s --nodeps --force --dbpath=%s %s" % (temp, SWM_RPM_DB_PATH, os.path.join(REPO_DIR, rpm_file))
        ret_code = os.system(cmd)
        if temp:
            shutil.rmtree(temp, ignore_errors=True)
        if ret_code:
            logging.error("upgrade swm service failed")
            if os.path.exists("/OSM/conf/product_backups.ini"):
                cmd = "mv /OSM/conf/product_backups.ini /OSM/conf/product.ini;dos2unix /OSM/conf/product.ini"
                os.system(cmd)
            return 1
    if os.path.exists("/OSM/conf/product_backups.ini"):
        cmd = "mv /OSM/conf/product_backups.ini /OSM/conf/product.ini;dos2unix /OSM/conf/product.ini"
        os.system(cmd)
    ret_code = os.system('cp -af %s/manifest.yml %s/conf' % (TOP_DIR, RUN_DIR))
    if ret_code:
        logging.error("copy manifest.yml file failed.")
        return 1
    return restart()


def rollback():
    return upgrade()


def pre_upgrade():
    return 0


def pre_rollback():
    return 0


def exec_cmd(args):
    """

    :param args:
    :return:
    """
    if 2 != len(args):
        show_help()
        return -1
    func_name = args[1]
    this_module = sys.modules[__name__]
    try:
        func = getattr(this_module, func_name)
    except Exception as e:
        logging.exception("param is error %s", e)
        return -1
    if func is None:
        return -1
    try:
        return func()
    except:
        logging.exception("swm run cmd(%s) failed.", func_name)
        return 1


if __name__ == "__main__":
    ret = exec_cmd(sys.argv)
    sys.exit(ret)
