#!/usr/bin/python
# -*- coding: utf-8 -*-
# @brief   : 2018/10/10 16:56
# @File    : app ctrl
#  @Software: Software management infrastructure
import logging
import os
import platform
import shutil
import sys
import tarfile
import threading
import time

import yaml

MY_DIR = os.path.dirname(os.path.realpath(__file__))
is_windows = (platform.system() == "Windows")
CONF_DIR = '/startup_disk/conf'
PKG_CUR = '/startup_disk/image/pkg_cur'
PKG_CUR_REALPATH = os.path.realpath(PKG_CUR)
PATCH_CUR_DIR = os.path.join(PKG_CUR_REALPATH, "../hotpatch/patch_cur")
PATCH_TEMP_DIR = os.path.join(PKG_CUR_REALPATH, "../hotpatch/patch_temp")
BOOT_LINK = '/startup_disk/image/boot'
PKG_UPD_DIR = '/startup_disk/image/pkg_upd'
COFFER_BIN_SIZE = os.popen("df -BG /OSM/coffer_bin/ |tail -n 1 |awk '{print $2}'").read(2)
EXTRACT_DIR = '/OSM/coffer_bin/startup' if COFFER_BIN_SIZE != '1G' else '/OSM/coffer_data/startup'

OSM_SCRIPT_DIR = "/OSM/script"
DISK_INSTALL_DIR = '/OSM/coffer_bin'
MEMORY_INSTALL_DIR = '/OSM/component'

PROC_OSP_BSP = '/proc/osp/bsp'
HOOK_REPLACE = '/OSM/coffer_data/install_hook/replace'
HOOK_SCRIPT = '/OSM/coffer_data/install_hook/script'

NODE_TYPE = None
DATA_NODE_FLAG_FILE = '/startup_disk/conf/conf_local/data_node'
SPARE_PART_FLAG = '/startup_disk/conf/conf_local/spare_part_flag'
VERIFY_EXEC_SCRIPT = '/OSM/script/startup_verify.py'
DATA_NODE_BOARD = ['PANGEA_V6R1C00_JBOD_SAS_EXP', 'PANGEA_V6R1C00_JBOF_PALM_EXP']
START_ERROR_ERROR_CODE = 203  # start failed record error code
CON_RESETS_ERR_CODE = 201  # more than three serious reset in 50 minutes
VERIFY_FAILED_ERR_CODE = 1073793484
THIRD_PAUSE_FLAG = "/startup_disk/image/boot/thirdpauseflag"
RECORD_ABNORMAL_CMD = "sh /OSM/script/os_record_abnormal_status.sh -s %s"
RECORD_ABNORMAL_SCRIPT = RECORD_ABNORMAL_CMD % START_ERROR_ERROR_CODE
FACTORY_FLAG_FILE = "/startup_disk/conf/equ_test/checkconfig.sh"
SWM_DB_PATH = '/OSM/coffer_data/swm/swm.db'
HOOK_PATH = '/OSM/script/startup.hook'
UPGRADE_FLAG_PATH = '/OSM/coffer_data/swm/upgrade_flag'
UPG_BIOS_SCRIPT = '/OSM/script/upgrade_bios.py {0} &>/dev/null'
NEED_SYNC_FW_FLAG_AFTER_REBOOT = os.path.join(CONF_DIR, 'conf_local',
                                              'sync_fw_flag_after_reboot')
IMAGE_DIR = '/startup_disk/image'
CUR_EULER_MANIFEST = os.path.join(BOOT_LINK, 'manifest.yml')
UNROOT_FLAG_PATH = '/OSM/conf/unroot_flag'


class BiosUpgType(object):
    STAND = '1'
    FORCE = '8'
    UPG_SLAVE = '16'


def decode_str(value):
    if isinstance(value, bytes):
        return value.decode()
    return value


def wait_service_finish(timeout=300, interval=5):
    services = ['pxe_server', 'nvram_log']
    times = timeout // (interval if interval else 1)
    cmd = 'systemctl status {service} | grep -E "FAILURE|SUCCESS|killed"'
    log.info('STARTUP: Begin wait service finish.')
    while times:
        for service in services:
            if os.system(cmd.format(service=service)):
                break
        else:
            break
        times -= 1
        time.sleep(interval)
    log.info('STARTUP: End wait service finish.')
    return


# noinspection PyBroadException,PyPep8
class StartUp(object):
    """
    Startup manager.
    """

    STATE_INIT = 0
    STATE_SWM_INSTALLED = 1
    STATE_ALL_INSTALLED = 2
    STATE_ALL_STARTED = 3

    state_file = '/tmp/startup.stat'

    def __init__(self):
        """

        """
        self.lock = threading.RLock()
        self.state = self.load_state()
        self.result = True
        self.cmpt_list = []
        log.info("STARTUP: Load startup stat(%d).", self.state)
        return

    def load_state(self):
        """

        :return:
        """
        try:
            return int(open(self.state_file, 'r+').read())
        except:
            return self.STATE_INIT

    def save_state(self, state):
        self.state = state
        open(self.state_file, 'w+').write(str(self.state))
        return

    @staticmethod
    def _get_cmpt_path(pkg_dir, cmpt_name):
        dir_list = ['apollo', 'athena', 'posseidon', 'packages']
        for sub_dir in dir_list:
            sub_path = os.path.join(pkg_dir, sub_dir)
            if not os.path.isdir(sub_path):
                continue
            for root, dirs, files in os.walk(sub_path):
                cmpt_list = [x for x in files if x.startswith(cmpt_name + '-')]
                if cmpt_list:
                    return os.path.join(root, cmpt_list[0])

        log.warning("Can not find cmpt(%s) in dir(%s).", cmpt_name, pkg_dir)
        return ''

    @classmethod
    def _get_startup_cmpt_path(cls, cmpt_name):
        """
        Get component package path
        :param cmpt_name:
        :return:
        """
        cmpt_path = cls._get_cmpt_path(PKG_UPD_DIR, cmpt_name)
        if cmpt_path:
            return cmpt_path
        if cmpt_name == 'swm' and cls.has_swm_patch():
            cmpt_path = cls._get_cmpt_path(PATCH_CUR_DIR, cmpt_name)
        else:
            cmpt_path = cls._get_cmpt_path(PKG_CUR, cmpt_name)
        if cmpt_path:
            return cmpt_path
        log.error("STARTUP: The component(%s) pkg is not exist.", cmpt_path)
        return None

    @staticmethod
    def has_swm_patch():
        if not os.path.exists(PATCH_CUR_DIR):
            return False
        file_list = os.listdir(PATCH_CUR_DIR)
        for file_name in file_list:
            if file_name.startswith('swm-'):
                return True
        return False

    @staticmethod
    def _get_swm_root(root_dir):
        swm_dir_list = [d for d in os.listdir(root_dir) if d.startswith('swm')]
        if not swm_dir_list:
            log.error("STARTUP: Swm component install dir is not exist")
            return ''
        if len(swm_dir_list) > 1:
            log.warning("STARTUP: Swm dir is more than one.")
            return ''
        swm_dir = os.path.join(root_dir, swm_dir_list[0])
        return swm_dir

    def exec_cmpt_cmd(self, cmpt, cmd_name):
        log.info("STARTUP: Component(%s) begin to run cmd(%s).", cmpt.name, cmd_name)
        ret = cmpt.exec_cmd(cmd_name)
        if not ret:
            log.error("STARTUP: Component(%s) run cmd(%s) failed, then retry.", cmpt.name, cmd_name)
            time.sleep(2)
            ret = cmpt.exec_cmd(cmd_name)
        if ret:
            log.info("STARTUP: Component(%s) run cmd(%s) successfully.", cmpt.name, cmd_name)
            return True
        log.error("STARTUP: Component(%s) run cmd(%s) failed finally.", cmpt.name, cmd_name)
        with self.lock:
            self.result = ret
        return False

    def install_swm(self):
        """
        Extract swm self and install swm
        :return:
        """
        self.handle_pkg_cur()
        swm_pkg = self._get_startup_cmpt_path('swm')
        if not swm_pkg:
            return False
        with tarfile.open(swm_pkg, 'r:gz') as tar:
            tar.extractall(EXTRACT_DIR)
        swm_dir = self._get_swm_root(EXTRACT_DIR)
        if not swm_dir:
            log.error("STARTUP: Swm was extracted incorrectly.")
            return False
        log.info("STARTUP: Begin to custom EulerOS.")
        os_custom_script = os.path.join(swm_dir, 'action', 'custom_os.sh')
        ret_code = os.system('sh {script:s}'.format(script=os_custom_script))
        if ret_code:
            log.error("STARTUP: Exec script (%s) to custom euler os failed", os_custom_script)
            return False
        log.info("STARTUP: Begin to install component(swm).")
        app_ctl = os.path.join(swm_dir, 'action', 'appctl')
        if os.system('%s install' % app_ctl):
            log.warning("STARTUP: Install component(swm) failed.")
        else:
            log.info("STARTUP: Install component(swm) successfully.")
        self.save_state(self.STATE_SWM_INSTALLED)
        self.special_resume_handle(PATCH_TEMP_DIR)
        return True

    @staticmethod
    def load_deploy_cfg(cfg_path):
        """
        Load deploy info from swm.yml
        :param cfg_path:
        :return:
        """
        with open(cfg_path, 'r') as fp:
            cfg_info = yaml.safe_load(fp)
        cmpt_cfg = cfg_info.get('Components')
        cmpt_list = [cfg.get('Name') for cfg in cmpt_cfg if cfg.get('NodeType') in ['all', NODE_TYPE]]
        deploy_cfg = cfg_info.get('DeployFlow')
        deploy_cmpt = []
        for stage in deploy_cfg.get('Stages'):
            deploy_cmpt.append([])
            for cmpt in stage.get('Components'):
                cmpt_name = cmpt.get('Name')
                if cmpt_name in cmpt_list:
                    deploy_cmpt[-1].append(cmpt_name)
        return deploy_cmpt

    @staticmethod
    def get_euler_version():
        if not os.path.exists(CUR_EULER_MANIFEST):
            return ''
        with open(CUR_EULER_MANIFEST, 'r') as fp:
            euler_info = yaml.safe_load(fp)
        euler_ver = euler_info.get('Version', '').strip()
        log.info("STARTUP: Current euler version is %s.", euler_ver)
        return euler_ver

    def check_pkg_dir_euler_version(self, pkg_dir, euler_ver):
        euler_path = self._get_cmpt_path(pkg_dir, 'euler')
        if not euler_path:
            return False
        euler_ver_str = "euler-{0}-".format(euler_ver)
        if euler_ver_str in euler_path:
            return True
        return False

    def _load_cmpt_list(self):
        """

        :return:
        """
        if self.cmpt_list:
            return True
        sys.path.append(OSM_SCRIPT_DIR)
        sys.path.append(os.path.join(DISK_INSTALL_DIR, 'swm'))
        sys.path.append(os.path.join(MEMORY_INSTALL_DIR, 'swm'))

        cmpt_mod = __import__('cmpt', fromlist=['cmpt', ])
        deploy_cmpt = self.load_deploy_cfg(os.path.join(PKG_CUR, 'swm.yml'))
        for batch in deploy_cmpt:
            batch_cmpt = []
            for cmpt_name in batch:
                cmpt_path = self._get_startup_cmpt_path(cmpt_name)
                if not cmpt_path:
                    continue
                cmpt = cmpt_mod.Component.get(cmpt_name, os.path.dirname(cmpt_path), EXTRACT_DIR)
                batch_cmpt.append(cmpt)
            self.cmpt_list.append(batch_cmpt)
        return True

    @staticmethod
    def modify_group_user():
        # modify 文件权限，软连接
        cmd = 'sh /OSM/script/os_modify_sftp_group_user_dir.sh'
        os.system(cmd)

    @staticmethod
    def resume_euler_special_patch():
        cmd = 'sh /OSM/script/os_upgrade_coldpatch.sh -a'
        os.system(cmd)

    @staticmethod
    def special_resume_handle(patch_dir):
        if not os.path.exists(patch_dir):
            return True
        file_list = os.listdir(patch_dir)
        result = True
        for script in file_list:
            log.info("Find file(%s).", script)
            if not script.startswith('special_resume_'):
                continue
            cmd = os.path.join(patch_dir, script)
            ret = os.system(cmd)
            if ret == 0:
                log.info("Special resume handle cmd(%s) successfully.", cmd)
            else:
                result = False
                log.error("Special resume handle cmd(%s) failed, result %s.",
                          cmd, ret)
        return result

    @staticmethod
    def resume_euler_patch(resume_stage):
        try:
            if resume_stage == "after_install":
                wait_service_finish()
            cur_ver = os.readlink(PKG_CUR).split('/')[0]
            patch_path = os.path.join('/startup_disk/image/', cur_ver, 'hotpatch/patch_cur')
            patch_mod = __import__('service.hotpatch.agent.hotpatch_util', fromlist=['hotpatch_util', ])
            ret = patch_mod.PatchPkg.resume_euler_patch(patch_path, resume_stage)
            if not ret:
                log.error("STARTUP: Euler patch resume failed, "
                          "stage %s return %s", resume_stage, ret)
            else:
                log.info("STARTUP: Euler patch resume stage % return %s",
                         resume_stage, ret)
            return ret
        except Exception:
            log.exception("STARTUP: Resume stage %s excepted.", resume_stage)
        return

    @staticmethod
    def resume_dopra_patch():
        try:
            cur_ver = os.readlink(PKG_CUR).split('/')[0]
            patch_path = os.path.join('/startup_disk/image/', cur_ver, 'hotpatch/patch_cur')
            patch_mod = __import__('service.hotpatch.agent.hotpatch_util', fromlist=['hotpatch_util', ])
            ret = patch_mod.PatchPkg.resume_dopra_patch_file(patch_path)
            log.info("STARTUP: Dopra patch file resume return %s", ret)
        except Exception:
            log.exception("STARTUP: Dopra patch file resume excepted.")
        return

    def get_fast_upgrade_info(self):
        fast_upgrade_flag = None
        with open(UPGRADE_FLAG_PATH) as fp:
            upg_info = fp.readlines()
        for line in upg_info:
            if "fastupgflag" in line:
                fast_upgrade_flag = line.strip().split('=')[1]
        log.info("STARTUP: fast flag(%s)", fast_upgrade_flag)
        return fast_upgrade_flag

    @staticmethod
    def change_pkg_cur_to_version(version):
        log.info("STARTUP: Change pkg_cur to version %s.", version)
        mount_rw = "mount -o remount,rw,async /startup_disk/image/"
        mount_ro = "mount -o remount,ro,async /startup_disk/image/"
        os.system(mount_rw)
        os.unlink(PKG_CUR)
        os.symlink('{0}/system'.format(version), PKG_CUR)
        os.system("fsync {0}".format(PKG_CUR))
        os.system(mount_ro)
        return

    @staticmethod
    def _drop_caches():
        print_mem_cmd = 'echo m > /proc/sysrq-trigger'
        drop_caches_cmd = 'echo 3 > /proc/sys/vm/drop_caches'
        for c in [print_mem_cmd, drop_caches_cmd, print_mem_cmd]:
            ret = os.system(c)
            log.info('STARTUP: Execute cmd(%s) ret(%s).', c, ret)
        if NODE_TYPE == 'DATA_NODE':
            log.info('STARTUP: Sleep 5s after drop caches in data node.')
            time.sleep(5)
        return

    @staticmethod
    def change_permission_for_unroot_scenes(script):
        if not os.path.exists(UNROOT_FLAG_PATH):
            return
        script_file = os.path.join('/OSM/script', script)
        if os.path.exists(script_file):
            os.system("sh {0}".format(script_file))
            os.system("chown -h -R :root /OSM/log/cur_debug/omm")
            log.info("STARTUP: Change permission for unroot scenes, file:%s.", script)
        return

    def modify_pkg_cur(self, euler_ver):
        file_list = [per_file for per_file in os.listdir(IMAGE_DIR) if per_file.isdigit()]
        for version in file_list:
            if self.check_pkg_dir_euler_version(os.path.join(IMAGE_DIR, version, 'system'), euler_ver):
                self.change_pkg_cur_to_version(version)
                log.info("STARTUP: Pkg cur points to %s.", version)
                return
        log.warning("STARTUP: Failed to find compatible version.")
        return

    def handle_pkg_cur(self):
        """
        For the euler switch, package must match with euler os, else the node will can not start
        :return:
        """
        try:
            if os.path.exists(UPGRADE_FLAG_PATH):
                fast_upgrade_flag = self.get_fast_upgrade_info()
                if fast_upgrade_flag == "1":
                    log.info("STARTUP: No need to handle pkg_cur.")
                    return
            euler_ver = self.get_euler_version()
            if not euler_ver:
                log.warning("STARTUP: Get euler version failed.")
                return
            log.info("STARTUP: Get euler version %s", euler_ver)
            if self.check_pkg_dir_euler_version(PKG_CUR, euler_ver):
                log.info("STARTUP: Euler is compatible with pkg_cur.")
                return
            self.modify_pkg_cur(euler_ver)
            return
        except Exception:
            log.exception("STARTUP: Handle pkg_cur excepted.")
            return

    def install_other(self):
        """

        :return:
        """
        log.info("STARTUP: Begin to install components.")
        if not self._load_cmpt_list():
            return False
        # 启动盘设置读写
        mount = "mount -o remount,rw,async /startup_disk/image/"
        os.system(mount)
        try:
            for batch in self.cmpt_list[1:]:
                tpool = []
                # 不重复安装swm
                for cmpt in batch:
                    if cmpt == "swm":
                        continue
                    thread = threading.Thread(target=self.exec_cmpt_cmd, args=(cmpt, 'install',))
                    thread.start()
                    tpool.append(thread)
                for thread in tpool:
                    thread.join()
            self._drop_caches()
            # 变更权限
            self.modify_group_user()
            if not self.result:
                log.error("STARTUP: Any component install failed.")
                return False
            log.info("STARTUP: Install all components successfully.")
            self.save_state(self.STATE_ALL_INSTALLED)
            self.resume_dopra_patch()
            if not self.resume_euler_patch('after_install'):
                return False
            return True
        except Exception:
            log.exception("install other exceped")
            return False
        finally:
            not_mount = "mount -o remount,ro,sync /startup_disk/image/"
            os.system(not_mount)
            # 当前环境中使用该脚本完成ldconfig，待后续优化
            cmd = "/OSM/script/swm_second_load.sh"
            os.system(cmd)
            self.change_permission_for_unroot_scenes(script='changePermission.sh')
            self.resume_euler_special_patch()

    @staticmethod
    def data_node_rescue_mode():
        if NODE_TYPE != 'DATA_NODE':
            return False
        if os.readlink(BOOT_LINK) == 'boot_rescue':
            log.info("STARTUP: boot link to boot_rescue.")
            return True
        return False

    @staticmethod
    def exec_hook(stage):
        try:
            path_dict = {
                "before.install": os.path.join(HOOK_PATH, "before.install"),
                "after.install": os.path.join(HOOK_PATH, "after.install"),
                "after.start": os.path.join(HOOK_PATH, "after.start")
            }
            hook_dir = path_dict.get(stage)
            if not os.path.exists(hook_dir):
                log.info("STARTUP: hook dir(%s) does not exist.", hook_dir)
                return True
            log.info("STARTUP: start to exec hook, hook path(%s).", hook_dir)
            for script in os.listdir(hook_dir):
                cmd = 'chmod +x {0};{0} &>/dev/null'.format(
                    os.path.join(hook_dir, script))
                ret = os.system(cmd)
                log.info("STARTUP: exec script(%s), ret(%s).", script, ret)
            log.info("STARTUP: end to exec hook stage(%s).", stage)
        except Exception:
            log.exception("exec hook exception.")
        return True

    def hook_before_install(self):
        return self.exec_hook("before.install")

    def hook_after_install(self):
        return self.exec_hook("after.install")

    @staticmethod
    def check_pkg_cur():
        if not os.path.exists(VERIFY_EXEC_SCRIPT):
            log.info("STARTUP: No need to verify pkg.")
            return True
        ret = os.system(VERIFY_EXEC_SCRIPT)
        if ret != 0:
            log.error("STARTUP: Failed to verify pkg, result %s.", ret)
            cmd = RECORD_ABNORMAL_CMD % VERIFY_FAILED_ERR_CODE
            os.system(cmd)
            return False
        log.info("STARTUP: Verify pkg successfully.")
        return True

    def hook_after_start(self):
        return self.exec_hook("after.start")

    def start_other(self):
        """

        :return:
        """
        if not os.system('cat /proc/cmdline |grep os_factory'):
            warn_str = "STARTUP: not support to start component in os_factory mode."
            print(warn_str)
            log.warning(warn_str)
            return True

        if self.data_node_rescue_mode():
            cmd = "sh /OSM/script/os_record_abnormal_status.sh -s %s" % CON_RESETS_ERR_CODE
            os.system(cmd)
            return True

        #  power-on pause function
        log.info("STARTUP: Begin to start components.")
        if os.path.exists(THIRD_PAUSE_FLAG):
            log.warning("STARTUP: sys pause record flag exist, not start component.")
            return True
        if not self._load_cmpt_list():
            os.system(RECORD_ABNORMAL_SCRIPT)
            return False
        for batch in self.cmpt_list:
            # start 分组内也串行执行
            for cmpt in batch:
                if cmpt.name == 'swm':
                    continue
                elif cmpt.name == 'devm':
                    self.change_permission_for_unroot_scenes(script='hisi_dev_permision.sh')
                    self.change_permission_for_unroot_scenes(script='changeDevmPermission.sh')
                elif cmpt.name == 'app_data':
                    self.change_permission_for_unroot_scenes(script='changeSysPermission.sh')
                ret = self.exec_cmpt_cmd(cmpt, 'start')
                if not ret:
                    log.error("STARTUP: Any component start failed.")
                    os.system(RECORD_ABNORMAL_SCRIPT)
                    return False
        self.save_state(self.STATE_ALL_STARTED)
        log.info("STARTUP: Start all components successfully.")
        shutil.rmtree(EXTRACT_DIR, ignore_errors=True)
        if not self.resume_euler_patch('after_start'):
            return False
        return True

    @staticmethod
    def check_if_bios_es():
        bios_es = "cat /proc/osp/bsp | grep 'CPU Version : ES' >/dev/null"
        if not os.system(bios_es):
            log.info("BIOS_UPD: this is es equipment.")
            return True
        return False

    @staticmethod
    def exec_upg_bios_cmd(params):
        cmd = UPG_BIOS_SCRIPT.format(params)
        ret = os.system(cmd)
        log.info("UPG_BIOS: exec cmd(%s), ret(%s)", cmd, ret)
        return ret

    def bg_upg_opposite_bios(self):
        param = " ".join(['-t', BiosUpgType.UPG_SLAVE, '-k'])
        thread = threading.Thread(target=self.exec_upg_bios_cmd, args=(param,))
        thread.setDaemon(True)
        thread.start()
        return

    def upgrade_opposite_bios(self):
        """
        滚动、离线升级复位后，如果上电过程中复位类型为update bios(证明是升级过程中升级BIOS后正常复位）
        则说明bios完成升级，需要后台升级另外一片BIOS
        :return:
        """
        if os.path.exists(NEED_SYNC_FW_FLAG_AFTER_REBOOT):
            self.bg_upg_opposite_bios()

    def upg_bios(self):
        params = None
        if os.path.exists(FACTORY_FLAG_FILE):
            if not self.check_if_bios_es():
                params = " ".join(['-r', '-t', BiosUpgType.FORCE])
            else:
                params = " ".join(['-r', '-t', BiosUpgType.STAND])
        elif not os.system("cat /sys/kernel/nvram/last_reset_reason | "
                           "grep -E \"^bios reset$|^update bios$\" &>/dev/null"):
            params = " ".join(['-r', '-t', BiosUpgType.STAND])
            log.info("STARTUP: there is bios switch or update bios.")
        if params:
            ret = self.exec_upg_bios_cmd(params)
            if ret:
                return False
        self.upgrade_opposite_bios()
        return True

    def start_swm(self):
        """
        :description:just start swm part
        :return:
        """
        if platform.machine() == 'aarch64' and \
                not os.path.exists('/.dockerenv') and not self.upg_bios():
            return False

        log.info("STARTUP: Begin to start components.")
        if not self._load_cmpt_list():
            return False
        for batch in self.cmpt_list:
            for cmpt in batch:
                if cmpt.name != 'swm':
                    continue
                ret = self.exec_cmpt_cmd(cmpt, 'start')
                if not ret:
                    log.error("STARTUP: swm component start failed.")
                    return False
        log.info("STARTUP: Start swm components successfully.")
        return True

    @staticmethod
    def install_hook():
        if os.path.exists(HOOK_REPLACE):
            cmd = 'cd %s; cp -af * /' % HOOK_REPLACE
            os.system(cmd)
            log.info("STARTUP: install hook replace cmd(%s).", cmd)
        if os.path.exists(HOOK_SCRIPT):
            for script in os.listdir(HOOK_SCRIPT):
                cmd = 'sh %s/%s' % (HOOK_SCRIPT, script)
                os.system(cmd)
                log.info("STARTUP: install hook script(%s).", cmd)
        return True

    def prepare(self):
        """

        :return:
        """
        if self.STATE_INIT != self.state:
            return
        shutil.rmtree(EXTRACT_DIR, ignore_errors=True)
        os.makedirs(EXTRACT_DIR, 0o755)
        return

    def run(self):
        self.prepare()
        run_list = []
        if self.STATE_ALL_STARTED == self.state:
            return True
        if self.STATE_INIT == self.state:
            run_list = [self.hook_before_install, self.install_swm, self.install_other,
                        self.install_hook, self.hook_after_install,
                        self.check_pkg_cur, self.start_swm,
                        self.start_other, self.hook_after_start]
        elif self.STATE_SWM_INSTALLED == self.state:
            run_list = [self.install_other, self.install_hook,
                        self.hook_after_install, self.check_pkg_cur,
                        self.start_swm, self.start_other,
                        self.hook_after_start]
        elif self.STATE_ALL_INSTALLED == self.state:
            run_list = [self.install_hook, self.hook_after_install,
                        self.check_pkg_cur, self.start_swm,
                        self.start_other, self.hook_after_start]

        # 如果为os_only模式，且系统没有执行过startall，则只执行install，不执行start动作

        if not os.system('cat /proc/cmdline |grep os_only') \
                or not os.system('cat /proc/cmdline |grep os_rescue') \
                or not os.system('cat /proc/cmdline |grep os_netboot') \
                or not os.system('cat /proc/cmdline |grep os_memtest'):
            if self.state == self.STATE_INIT:
                log.info("STARTUP: System is os_only mode, do not start components.")
                run_list.remove(self.hook_after_install)
                run_list.remove(self.hook_after_start)
                run_list.remove(self.start_other)
                run_list.remove(self.install_hook)
                run_list.remove(self.check_pkg_cur)
                if os.system('cat /proc/cmdline |grep os_rescue'):
                    run_list.remove(self.start_swm)

        for func in run_list:
            if not func():
                return False
        return True


def set_node_type():
    global NODE_TYPE
    if os.getenv('BOARD_TYPE', None) in DATA_NODE_BOARD:
        cmd = 'mkdir -p /startup_disk/conf/conf_local/; touch %s; sync' % DATA_NODE_FLAG_FILE
        os.system(cmd)
    NODE_TYPE = 'CTL_NODE' if not os.path.exists(DATA_NODE_FLAG_FILE) else 'DATA_NODE'
    os.environ['NODE_TYPE'] = NODE_TYPE
    log.info('NODE_TYPE is %s.', NODE_TYPE)


def get_board_type():
    """
    The proc_osp_bsp.info has not been generated when this script is executed.
    :return:
    """
    with open(PROC_OSP_BSP, 'rb') as f:

        value_list = f.read().splitlines(False)
        for value in value_list:
            context = decode_str(value).lstrip().rstrip()
            if "Product Name is " in context:
                board_type = context.split(' ')[-1]
                return board_type


# 设置环境变量
def set_env():
    os.environ['PATH'] = "/OSM/bin:/OSM/bin/script:/OSM/script:" + os.getenv('PATH')
    # 生成crash需要使用的环境变量
    os.environ['MALLOC_ARENA_MAX'] = "1"
    board_type = get_board_type()
    os.environ['BOARD_TYPE'] = board_type
    os.environ['DEVICE_TYPE'] = board_type
    set_node_type()


# noinspection PyBroadException,PyPep8
def exec_cmd():
    exec_cmd_list = [
        'taskset -cp 0-255 %d' % os.getpid(),
        'cp -af %s/manifest.yml /OSM/conf/' % PKG_CUR,
    ]
    try:
        for cmd in exec_cmd_list:
            os.system(cmd)
        set_env()
        result = StartUp().run()
        return 0 if result else 1
    except:
        log.exception("STARTUP: Deploy system failed.")
        return 2


def wait_installed():
    su = StartUp()
    for _dummy in range(900):
        su.load_state()
        if su.load_state() >= su.STATE_ALL_INSTALLED:
            log.info("STARTUP: Wait system installed successfully.")
            return 0
        time.sleep(1)
    log.error("STARTUP: Wait system installed timeout.")
    return 1


if __name__ == '__main__':
    log = logging.getLogger("startup")
    formatter = logging.Formatter(fmt="[%(asctime)s][%(levelname)s][%(message)s][%(filename)s, %(lineno)d]",
                                  datefmt="%Y-%m-%d %H:%M:%S")
    file_handler = logging.FileHandler(filename="/OSM/log/cur_debug/startup.log")
    file_handler.setFormatter(formatter)
    log.addHandler(file_handler)
    log.setLevel(logging.INFO)
    os.chmod("/OSM/log/cur_debug/startup.log", 0o640)

    fork_ret = os.fork()
    if fork_ret:
        sys.exit(wait_installed())
    else:
        sys.exit(exec_cmd())
