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

import os
import sys
import logging
import time
import yaml
import shutil
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

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'
SIMU = os.path.exists('/.dockerenv')
FIRMWARE_HOOK_PATH = '/startup_disk/image/pkg_cur/firmware/install_hook/'
MOUNT_IMAGE_RW = 'mount -o remount,rw,async /startup_disk/image/'
PKG_CUR = '/startup_disk/image/pkg_cur'
OSM_CONF_MANIFEST = '/OSM/conf/manifest.yml'
UPGRADE_FLAG_PATH = '/OSM/coffer_data/swm/upgrade_flag'
UNROOT_FLAG_PATH_IMAGE = '/startup_disk/image/pkg_cur/unroot_flag'
DATA_NODE_FLAG_FILE = '/startup_disk/conf/conf_local/data_node'
UNROOT_FLAG_PATH = '/OSM/conf/unroot_flag'

# INSTALL 加载执行的脚本
INSTALL_EXEC_CMD = [
    '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',

    'sh /OSM/script/os_modify_sftp_group_user_dir.sh',
    'sh /OSM/script/patch_stage1.sh',
    'cp -af %s/manifest.yml %s/conf' % (TOP_DIR, RUN_DIR)
]
# start 加载执行的脚本
START_EXEC_CMD = [
    '/OSM/bin/daemon',
]

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.warn("The pkg has not been installed.")
            return True
        logging.info("Uninstall pkg model successfully.")
        return True


# set apollo version to /OSM/conf/manifest.yml
def set_apollo_version():
    apollo_link = os.path.join(PKG_CUR, 'apollo')
    if not os.path.islink(apollo_link):
        return
    apollo_dir = os.path.realpath(apollo_link)
    apollo_version = apollo_dir.split('-')[-1]
    content = "ApolloVersion: {0}"
    with open(OSM_CONF_MANIFEST, 'a+') as fp:
        fp.write(content.format(apollo_version))
    return


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
    cmd = "cd /OSM/conf; mv versions.conf versions_tmp.conf; mv tmp_use_versions.conf versions.conf"
    os.system(cmd)
    cmd = "cat /OSM/conf/versions_tmp.conf >> /OSM/conf/versions.conf; rm -rf /OSM/conf/versions_tmp.conf"
    os.system(cmd)
    cmd = 'cp -af {0}/manifest.yml /OSM/conf/'
    os.system(cmd.format(PKG_CUR))
    set_apollo_version()
    ret_code = os.system(START_CMD)
    if ret_code:
        logging.error("start swm service failed")
        return 1
    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 exec_firmware_hook():
    """
    install hook in firmware dir
    :return:
    """
    if os.path.exists(FIRMWARE_HOOK_PATH):
        os.system(MOUNT_IMAGE_RW)
        for script in os.listdir(FIRMWARE_HOOK_PATH):
            cmd = 'cd {0};dos2unix {1};chmod +x {1};./{1}'.format(FIRMWARE_HOOK_PATH, script)
            ret_code = os.system(cmd)
            if ret_code:
                logging.error("exec firmware hook failed, script is (%s).", script)
    return


def exec_new_cmpt_hook():
    """
    install hook if has new cmpt
    :return:
    """
    apollo_link = os.path.join(PKG_CUR, 'apollo')
    if not os.path.islink(apollo_link):
        return
    apollo_dir = os.path.realpath(apollo_link)
    apollo_version = apollo_dir.split('-')[-1]
    if apollo_version != '1.0.0.1':
        return
    os.system('mkdir -p /OSM/script/startup.hook/after.start')
    os.system('chmod +xr /OSM/script/resume_new_cmpt_in_pkg_upd.py')
    os.system('cp -af /OSM/script/resume_new_cmpt_in_pkg_upd.py'
              ' /OSM/script/startup.hook/after.start/')
    return


def set_unroot_flag():
    if os.path.exists(DATA_NODE_FLAG_FILE):
        return
    if os.path.exists(UNROOT_FLAG_PATH_IMAGE):
        shutil.copy(UNROOT_FLAG_PATH_IMAGE, "/OSM/conf")
        os.system("chmod 644 /OSM/conf/unroot_flag")
        logging.info("INSTALL: set unroot_flag finish.")
        os.system(MOUNT_IMAGE_RW)
        os.system("chmod o+rx -R /startup_disk/image/")
        os.system("mkdir -p /OSM/conf/thrift_rpc")
        os.system("chmod 770 /OSM/conf/thrift_rpc")
        if os.path.exists("/OSM/script/addcommuser.sh"):
            os.system("sh /OSM/script/addcommuser.sh")
            logging.info("INSTALL: add common user for unroot scenes.")
    return


def install():
    create_rpm_dir()
    # 到repo目录下取出rpm包来进行安装
    for rpm_file in os.listdir(REPO_DIR):
        if not rpm_file.endswith('.rpm'):
            continue
        cmd = "rpm -ivh --nodeps --force --dbpath=%s %s" % (SWM_RPM_DB_PATH, os.path.join(REPO_DIR, rpm_file))
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("install swm service failed")
            return 1
    PkgModel.install()
    dev_type = os.getenv('DEVICE_TYPE', None)
    if dev_type is not None:
        logging.info("INSTALL: DEVICE_TYPE is %s", dev_type)
        sas_fw_file = os.path.join("/startup_disk/image/pkg_cur/firmware", dev_type, "Quark_fw_pkg.bin")
        sas_hda_file = os.path.join("/startup_disk/image/pkg_cur/firmware", dev_type, "Quark_HDA_Boot.bin")
        if not os.path.exists("/OSM/modules/Quark_fw_pkg.bin") and \
                os.path.exists(sas_fw_file):
            shutil.copy(sas_fw_file, "/OSM/modules")
        if not os.path.exists("/OSM/modules/Quark_HDA_Boot.bin") and \
                os.path.exists(sas_hda_file):
            shutil.copy(sas_hda_file, "/OSM/modules")
            logging.info("INSTALL: copy hda file")
    else:
        logging.error("INSTALL: board type is None")
    for cmd in INSTALL_EXEC_CMD:
        ret_code = os.system(cmd)
        if ret_code:
            logging.error("INSTALL:exec script (%s) failed", cmd)
            # return 1
    exec_firmware_hook()
    exec_new_cmpt_hook()
    set_unroot_flag()
    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 restart_ssh():
    script_path = "/OSM/script/modify_sshd.sh"
    if not os.path.exists(script_path):
        logging.warn("Failed to get script(%s).", script_path)
        return True
    ret_code = os.system(script_path)
    logging.info("Return code of script is (%s).", ret_code)
    return True


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
        cmd = "rpm -ivh --nodeps --force --dbpath=%s %s" % (SWM_RPM_DB_PATH, os.path.join(REPO_DIR, rpm_file))
        ret_code = os.system(cmd)
        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
    if not SIMU:
        restart_ssh()
        ret_code = os.system('sh /OSM/script/parse_initrd_config.sh')
        if ret_code:
            logging.error("Parse initrd config failed.")
            return 1
    if os.path.exists(UNROOT_FLAG_PATH):
        ret_code = os.system('sh /OSM/script/addcommuser.sh')
        if ret_code:
            logging.error("Add comm user failed ret(%s).", ret_code)
            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)
