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

import os
import shutil
import sys
import logging
import time
import datetime
import yaml
import pwd
import grp
import platform
import codecs
from ctypes import *

logging.basicConfig(level=logging.INFO,
                    filename= "/OSM/log/cur_debug/messages",
                    format='[%(asctime)s][%(levelname)s][%(message)s][%(filename)s, %(lineno)d]')

TOP_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
MY_DIR = os.path.dirname(os.path.realpath(__file__))
MY_NAME = os.path.basename(__file__)
COMPONENT_NAME = 'APP_DATA'
RPM_DB_PATH = '/var/lib/app_data_rpm'
REPO_DIR = os.path.join(TOP_DIR, "repo")
DISK_FIRMWARE_PATH = "/startup_disk/image/boot_osp/update_pkg/diskfirmware/"
PROCESS_SCRIPT = '/OSM/component/app_data/bin/'
RUN_DIR = '/OSM/component/app_data/'
PROCESS_ID = 12
LD_LIBRARY_PATH = 'LD_LIBRARY_PATH=/OSM/component/app_data/lib:/lib64:/usr/lib64:/usr/local/lib'
CO_PATCH_PATH = os.path.join(TOP_DIR, 'hotpatch')
CO_PATCH_RUNNING_PATH = '/OSM/component/app_data/hotpatch/'
NOT_STOP_SERVER_FILE_FLAG = "/OSM/coffer_data/sys/upgrade_not_stop_service"
ROOT_FLAG_FILE_PATH = "/OSM/conf/unroot_flag"
GEN_PRODUCT_INI_AUTO = "sh /OSM/script/ftds_gen_product_ini.sh install"
# 杀APP_DATA不同步，导致I/0跌落两次，时间差25s
T_EXP = 40

ARCH = os.popen('uname -r').read().rstrip('\n').split('.')[-1]
ARM = True if ARCH == 'aarch64' else False

class ProcessInfoStruct(Structure):
    _fields_ = [
        ("state", c_int),
        ("uiVersionId", c_uint),
        ("restart_type", c_uint),
        ("notNormalTime", c_longlong),
		("failureTime", c_longlong)
    ]

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

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

USER_GROUP = "OceanStorApp_dataGroup"
USER_GROUP_ID = 64000
USER_NAME = "OceanStorApp_dataUser"
USER_NAME_ID = 64000
USER_ROOT = "root"

def get_tmp_path():
    if not os.access(ROOT_FLAG_FILE_PATH, os.F_OK):
        return "/tmp"
    else:
        return "/OSM/tmp"

INSTALL_EXEC_CMD = [
    'mkdir -p {path:s}'.format(path=DISK_FIRMWARE_PATH),
    'mv /OSM/conf/hddfw.conf /startup_disk/image/boot_osp/update_pkg/diskfirmware/',
    'cd /OSM/; ln -sf conf/ config',
    'dd if=/dev/zero of={0}/.flow_trace_mem bs=32MB count=1'.format(get_tmp_path()), # 创建系统控制全局FLOWTRACE共享内存文件
    ]

CLEAR_CERT_INI_EXEC_CMD = [
    'rm /OSM/conf/cert_conf/certificate_config_401.ini',
    'rm /OSM/conf/cert_conf/certificate_config_402.ini',
    'rm /OSM/conf/cert_conf/certificate_config_639.ini',
    'rm /OSM/conf/cert_conf/certificate_config_366.ini',
    ]

CHMOD_CERT_INI_DIR_CMD = [
    'chmod 750 /OSM/conf/cert_conf',
    ]

# copy文件默认会给权限，做dos2unix
FILE_OWNER_CONF_TABLE = (
    dict(username=USER_NAME, usergroup="OceanStorCommonGroup", filePath='/OSM/component/app_data', isRecur=True),
    dict(username=USER_ROOT, usergroup="OceanStorCommonGroup", filePath='/OSM/component/app_data', isRecur=False),
    dict(username=USER_ROOT, usergroup="OceanStorCommonGroup", filePath='/OSM/component/app_data/bin', isRecur=True),
    dict(username=USER_ROOT, usergroup="OceanStorCommonGroup", filePath='/OSM/component/app_data/lib', isRecur=True),
)

USER_GROUP_CONF_TABLE = (
    dict(usergroup="OceanStorInfraGroup",   username=USER_NAME),
    dict(usergroup="disk",                  username=USER_NAME),
    dict(usergroup="OceanStorCommonGroup",  username=USER_NAME),
    dict(usergroup="OceanStorOAMGroup",     username=USER_NAME),
    dict(usergroup="OceanStorPangeaGroup",  username=USER_NAME),
    dict(usergroup="OceanStorDhaGroup",     username=USER_NAME),
    dict(usergroup="OceanStorOAMGroup",     username="ibc_os_hs"),
)

# 备份用户和组
BACKUP_USER_GROUP_CONF_TABLE = (
    dict(name="OceanStorApp_dataGroup", src="/etc/group", dst="/etc/group_default"),
    dict(name="OceanStorApp_dataUser", src="/etc/shadow", dst="/etc/shadow_default"),
    dict(name="OceanStorApp_dataUser", src="/etc/passwd", dst="/etc/passwd_default"),
)

def is_release_crack_package():
    cmd = "ls -l /startup_disk/image/pkg_cur/packages/ | sed -n '2p' | grep -i 'release_crack'"
    if os.system(cmd) == 0:
        return True
    return False


def is_release_package():
    cmd = "ls -l /startup_disk/image/pkg_cur/packages/ | sed -n '2p' | grep -i 'release'"
    if os.system(cmd) == 0:
        return not is_release_crack_package()
    return False

def read_file_content(file_name):
    content = ""
    py_version = sys.version_info.major
    logging.info("APP_DATA_UPGRADE: Open file(%s) with python(%s)." % (file_name, py_version))
    if py_version < 3:
        with codecs.open(file_name, "r", encoding="ISO-8859-1") as file_handle:
            content = file_handle.readlines()
    else:
        with open(file_name, "r", encoding="ISO-8859-1") as file_handle:
            content = file_handle.readlines()
    return content

def search_key_word(file_path, key_word):
    content = read_file_content(file_path)
    for line in content:
        if key_word in line:
            return line
    return None

# 目标备份文件不存在，不需要处理（OMM后续创建备份文件时备份）；
# 目标备份文件存在： 1）文件中已存在name用户，不需要处理；2）文件中不存在name的用户，则在源文件找出指定name的条目，以append方式写入备份文件；
def backup_user_group(conf_table):
    try:
        for conf_row in conf_table:
            if os.path.exists(conf_row['dst']) is False or os.path.exists(conf_row['src']) is False:
                logging.error("APP_DATA_UPGRADE: Src(%s) or des file(%s) is not exist." % (conf_row['src'], conf_row['dst']))
                continue

            str = search_key_word(conf_row['dst'], conf_row['name'])
            if str is not None:
                logging.info("APP_DATA_UPGRADE: Dst file(%s) is exist key word." % (conf_row['dst']))
                continue

            str = search_key_word(conf_row['src'], conf_row['name'])
            if str is None:
                logging.info("APP_DATA_UPGRADE: Src file(%s) is not exist key word." % (conf_row['dst']))
                continue

            ret_code = os.system("echo -n '{0}'>> {1}".format(str, conf_row['dst']))
            if ret_code != 0:
                logging.error("APP_DATA_UPGRADE: back user(%s) to file(%s) failed(%d)." % (str,  conf_row['dst'], ret_code))
                return 1
        logging.info("APP_DATA_UPGRADE: backup user group successed.")
        return 0
    except Exception as e:
        logging.exception("APP_DATA_UPGRADE: query info exception{0}.".format(e))
        return 1

def add_user_to_group():
    for conf_row in USER_GROUP_CONF_TABLE:
        cmd = "usermod -a -G {0} {1}".format(conf_row['usergroup'],conf_row['username'])
        ret_code = os.system(cmd)
        if ret_code != 0:
            logging.error("APP_DATA_UPGRADE: Execute cmd({0}) failed{1}.".format(cmd, ret_code))
            return 1
    return 0

def add_user_bin_to_path():
    ret_code = os.system("echo 'PATH=$PATH:/usr/sbin:/usr/local/sbin'>> /home/OceanStorApp_dataUser/.bashrc")
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: add user sbin to path failed(%d)", ret_code)
        return 1
    ret_code = os.system("echo 'export PATH'>> /home/OceanStorApp_dataUser/.bashrc")
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: export path failed(%d)", ret_code)
        return 1
    ret_code = os.system("source /home/OceanStorApp_dataUser/.bashrc")
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: source user failed(%d)", ret_code)
        return 1
    return 0

def need_no_root_run():
    """
    判断是否是ARM设备
    :return:
    """
    if not os.access(ROOT_FLAG_FILE_PATH, os.F_OK):
        logging.info("APP_DATA_UPGRADE: Need root, don't create user")
        return 1

    cpu_type = platform.uname()
    for name in cpu_type:
        if "aarch" in name:
            return 0

    logging.info("APP_DATA_UPGRADE: This is x86 device, don't create user")
    return 1


def chmod_app_data_owner():
    if  need_no_root_run() != 0:
        logging.info("APP_DATA_UPGRADE: Don't need to change app data file owner")
        return 0
    ret_code = os.system("chown -h :64000 /OSM/bin/app_data")
    ret_code = os.system("chown -h :64000 /OSM/bin/huge/app_data")
    logging.info("APP_DATA_UPGRADE: Change app data permission return code(%d)", ret_code)


def change_file_owner():

    for conf_row in FILE_OWNER_CONF_TABLE:
        if conf_row['isRecur']:
            cmd = "chown -Rh {0}:{1} {2}".format(conf_row['username'], conf_row['usergroup'], conf_row['filePath'])
        else:
            cmd = "chown -h {0}:{1} {2}".format(conf_row['username'], conf_row['usergroup'], conf_row['filePath'])
        ret_code = os.system(cmd)
        if ret_code != 0:
            logging.error("APP_DATA_UPGRADE: chown {0} user {1}, group {2} failed {3}.".format(conf_row['filePath'], conf_row['username'], conf_row['usergroup'], ret_code))
            return 1
    return 0

def cfg_power_file():
    ret_code = os.system("cp -f /OSM/component/app_data/conf/OceanStorApp_dataUser_* /etc/sudoers.d")
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Update app data sudoer file failed.")
        return 1
    os.system("chmod 440 /etc/sudoers.d/OceanStorApp_dataUser_*")
    return 0

def create_user():

    usr_info = None
    try:
        usr_info = pwd.getpwnam(USER_NAME)
    except Exception as e:
        logging.exception("APP_DATA_UPGRADE: query info exception{0}.".format(e))
    finally:
        if usr_info:
            logging.info("APP_DATA_UPGRADE: system exist user name(%s) ,don't create user." % USER_NAME)
            return 0

        ret_code = os.system("useradd -u {0} -g {1} -d /home/{2} -s /sbin/nologin {2}".format(USER_NAME_ID, USER_GROUP, USER_NAME))
        if ret_code != 0:
            logging.error("APP_DATA_UPGRADE: Ceate user(%s) failed(%d)." % (USER_NAME, ret_code))
            return 1
        return 0

def create_group():

    group_info = None
    try:
        group_info = grp.getgrnam(USER_GROUP)
    except Exception as e:
        logging.exception("APP_DATA_UPGRADE: query info exception{0}.".format(e))
    finally:
        if group_info:
            logging.info("APP_DATA_UPGRADE: system exist group(%s) ,don't create group." % USER_GROUP)
            return 0
        ret_code = os.system("groupadd -g {0} {1}".format(USER_GROUP_ID, USER_GROUP))
        if ret_code != 0:
            logging.error("APP_DATA_UPGRADE: Create usr group(%s) failed(%d)." % (USER_GROUP, ret_code))
            return 1
        return 0

def add_user():

    ret_code = create_group()
    if ret_code != 0:
        return ret_code

    ret_code = create_user()
    if ret_code != 0:
        return ret_code

    ret_code = cfg_power_file()
    if ret_code != 0:
        return ret_code

    ret_code = backup_user_group(BACKUP_USER_GROUP_CONF_TABLE)
    if ret_code != 0:
        return ret_code
    return 0

def setup_sys_relation_dirs():
    sys_exec_cmd = [
        'mkdir -m 750 -p /OSM/coffer_data/sys',
        'chown -h OceanStorApp_dataUser:OceanStorCommonGroup /OSM/coffer_data/sys',
        'chmod g+s /OSM/coffer_data/sys',
        'setfacl -mmask:rwx /OSM/coffer_data/sys',
        'setfacl -m u:5000:rwx /OSM/coffer_data/sys',
    ]
    ret_code = execute_cmd(sys_exec_cmd)
    if ret_code != 0:
        return 1
    return 0


def setup_relation_dirs():
    PROCESS_RELATION_DIRS = ["/OSM/dha/ai_slow_disk_drm",
                             "/OSM/data/disk_auth",
                             "/OSM/coffer_data/data_erase",
                             "/OSM/coffer_runningdata/nand_written_collect",
                             "/OSM/coffer_runningdata/data_erase"]
    # 该目录在去root和非去root版本都需要保持root权限
    fault_disklog_dir = "/OSM/coffer_data/fault_disklog"
    if os.system("mkdir -p %s && chmod 750 %s" %(fault_disklog_dir, fault_disklog_dir)) != 0:
        logging.error("APP_DATA_UPGRADE: create dir /OSM/coffer_data/fault_disklog failed")
        return 1

    for d in PROCESS_RELATION_DIRS:
        if not os.path.exists(d):
            cmd = "mkdir -p %s" %d
            if os.system(cmd) != 0:
                logging.error("APP_DATA_UPGRADE: create dir %s failed" %d)
                return 1

        if need_no_root_run() == 0:
            cmd = "chown -h %d:%d %s" %(USER_NAME_ID, USER_GROUP_ID, d)
            if os.system(cmd) != 0:
                logging.error("APP_DATA_UPGRADE: chown dir %s failed." %d)
                return 1
            cmd = "chmod 750 %s" %d
            if os.system(cmd) != 0:
                logging.error("APP_DATA_UPGRADE: chmod dir %s failed." %d)
                return 1
    return 0

def process_remove_root():

    if  need_no_root_run() != 0:
        os.system("rm -rf /OSM/component/app_data/conf/OceanStorApp_dataUser_*")
        logging.info("APP_DATA_UPGRADE: This is x86 device, don't create user")
        return 0

    ret_code = add_user()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Add user failed.")
        return 1

    ret_code = add_user_bin_to_path()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: add user sbin/bin to path failed.")
        return 1

    ret_code = change_file_owner()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Change file owner failed.")
        return 1

    ret_code = setup_sys_relation_dirs()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: mkdir sys dir.")
        return 1

    ret_code = add_user_to_group()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Add user to group failed.")
        return 1

    logging.info("APP_DATA_UPGRADE: Process remove root success.")
    return 0


def time_sync_consistency():
    """
     等待相同时间去停I/O
     :return:
     """
    t1 = float(os.getenv('CMPT_UPG_BEGIN_TIME', 0))
    if t1 == 0:
        logging.info("APP_DATA_UPGRADE: time stamp is 0, get time failed.")
        return
    t2 = float(time.time())
    t_sleep = t1 + T_EXP - t2
    if t_sleep < 0 or t_sleep > T_EXP:
        logging.info("APP_DATA_UPGRADE: Time(%s) is exception, don't need wait time sync.", t_sleep)
        return
    logging.info("APP_DATA_UPGRADE: Sleep (%s)s, then kill app data.", t_sleep)
    time.sleep(t_sleep)

def get_process_num():
    """
    get process num
    :return:
    """
    cmd = 'ps -ef | grep -w \"app_data -l 12 -v\" | grep -v grep | wc -l'
    r = os.popen(cmd)
    str = r.read()
    r.close()
    return int(str)


def check_process_num():
    """
    check process num
    :return:
    """
    print_time_by_type("check app data num", "start")
    check_time = 0
    while True:
        if check_time >= 10:
            return 1
        process_num = get_process_num()
        if process_num == 1:
            print_time_by_type("check app data num", "end")
            return 0
        time.sleep(0.5)
        check_time = check_time + 1
        logging.error("APP_DATA_UPGRADE: The process have abnormal, num(%d), retry time(%d).",process_num, check_time)

def get_process_state(process_id):

    retry_time = 0
    while True:
        if retry_time >= 6:
            return
        cmd = "ps -aux | grep app_data | grep %s | grep -v grep | awk '{print $8}'" % process_id
        r = os.popen(cmd)
        state = r.read().strip('\n')
        r.close()
        if state == "D" or state == "":
            time.sleep(0.5)
            retry_time = retry_time + 1
            logging.error("APP_DATA_UPGRADE: The process state is not normal, state(%s), retry time(%d).", state, retry_time)
            continue
        return

def get_process_id():

    retry_time = 0
    while retry_time < 5:
        cmd = "ps -ef | grep -w \"app_data -l 12 -v\" | grep -v grep | awk {'print $2,$12'} | tr \"\n\" \" \""
        r = os.popen(cmd)
        process_info = r.read()
        r.close()
        if process_info != "":
            break
        retry_time += 1
        time.sleep(0.2)

    logging.info("APP_DATA_UPGRADE: Upgrade current app data(%s).", process_info)
    return process_info.split(' ')[0]

def print_time_by_type(desc, type):
    record_time = int(round(time.time()*1000))
    logging.info("APP_DATA_UPGRADE: %s %s (cost time %d(ms))", desc, type, record_time)

def get_app_data_status_by_diagnose():
    app_data_pipe = os.popen("echo -e 'srv showproc\n exit\n' | diagnose --auto | grep app_data | awk '{print $4}'")
    try:
        app_data_status = int(app_data_pipe.read().strip())
    except BaseException as e:
        logging.info('APP_DATA_UPGRADE: diagnose get app_data status failed, info: %s, return PROCESS_STATE_NULL', e)
        app_data_status = 0
    app_data_pipe.close()
    return app_data_status

def check_proc_state(process, proc_id):
    max_check_times = 360
    check_time = 1
    need_get_status_by_diagnose = 0
    SRVSTATEPATH = "/OSM/component/app_data/lib/"
    if SRVSTATEPATH not in sys.path:
        sys.path.append(SRVSTATEPATH)
    p_info = ProcessInfoStruct()
    try:
        lib_srv_client = CDLL("libsrv_client.so")
    except BaseException as e:
        logging.info('APP_DATA_UPGRADE: CDLL error, info: %s, need get app_data status by diagnose', e)
        need_get_status_by_diagnose = 1

    logging.info("APP_DATA_UPGRADE: Online check process service status.")
    while True:
        if need_get_status_by_diagnose == 0:
            result = lib_srv_client.process_get_info(proc_id, byref(p_info))
            if result != 0:
                logging.error("APP_DATA_UPGRADE: Get %s state failed, ret(%d), using diagnose.", process, result)
                app_data_status = get_app_data_status_by_diagnose()
            else:
                app_data_status = p_info.state
        else:
            app_data_status = get_app_data_status_by_diagnose()

        if app_data_status == 0:
            logging.error("APP_DATA_UPGRADE: app_data process not exit.")
            return 1

        if app_data_status == 2:
            logging.info("APP_DATA_UPGRADE: Check %s process is normal ,continue to check sys service status.", process)
            break
        if check_time % 5 == 0:
            logging.error("APP_DATA_UPGRADE: %s process is not normal state(%d),check time(%d).", process, app_data_status, check_time)
        check_time += 1
        if check_time >= max_check_times:
            logging.error("APP_DATA_UPGRADE: check process status is failed, reach max times(%d), online failed.", check_time)
            return 1
        time.sleep(0.5)

    global lib_sys_python_client
    try:
        import libsys_python_client as lib_sys_python_client
    except ImportError as e:
        logging.error('APP_DATA_UPGRADE: import python lib failed, info: %s, try CDLL', e)
        lib_sys_python_client = CDLL("libsys_python_client.so")
        pass

    while True:
        result = lib_sys_python_client.GetProcDataServiceStatus()
        if  result == 0:
            logging.info("APP_DATA_UPGRADE: %s is online success.", process)
            return 0
        if check_time % 10 == 0:
            logging.error("APP_DATA_UPGRADE: %s sys service status is not normal, check time(%d).", process, check_time)
        check_time += 1
        if check_time >= max_check_times:
            logging.error("APP_DATA_UPGRADE: check sys service status failed, reach max times(%d), online failed.", check_time)
            return 1
        time.sleep(0.5)
    return 1


def show_help():
    print(USAGE)
    return


def get_cna_type():
    """
    get board_type
    :return:
    """
    if os.getenv('NODE_TYPE') == 'DATA_NODE':
        cna_type = 'DATA_NODE'
        return cna_type
    else:
        cna_type = os.getenv('BOARD_TYPE')
        if not (cna_type == 'PANGEA_V2R1C00' or cna_type == 'PANGEA_V2R1C01'):
            cna_type = 'PANGEA_V2R3C01'
        return cna_type

def print_oops_msg():
    messages = 'APP_DATA_UPGRADE: compile app_data failed, node need oops, sysrq-trigger c'
    cmd = 'echo %s >> /dev/kmsg' % (messages)
    os.system(cmd)
    return 0

def start():
    retryTime = 0
    ret_code = None
    while retryTime <= 2:
        ret_code = compile_app_data()
        if ret_code:
            logging.error("APP_DATA_UPGRADE: compile app_data failed.")
            retryTime = retryTime+1
            time.sleep(2)
            continue
        else:
            break
    # 节点上电场景，超过2次compile app_data失败，节点oops复位
    if ret_code:
        logging.error("compile app_data failed, node need oops.")
        time.sleep(120)
        print_oops_msg()
        os.system("echo c > /proc/sysrq-trigger")
        time.sleep(100)
    print_time_by_type("start app_data", "start")
    cmd = 'snm.sh -s {0}/app_data_snm.py -f {0}/app_data_snm.xml'.format(PROCESS_SCRIPT)
    ret_code = os.system(cmd)
    if ret_code:
        logging.error("APP_DATA_UPGRADE: start app_data failed.")
        return 1

    print_time_by_type("start app_data", "end")
    return 0

def stop():
    print_time_by_type("stop app_data", "start")
    cmd = 'snm.sh -t {0}/app_data_snm.py -f {0}/app_data_snm.xml'.format(PROCESS_SCRIPT)
    ret_code = os.system(cmd)
    if ret_code:
        logging.error("APP_DATA_UPGRADE: Stop app_data failed.")
        return 1

    check_time = 1
    while check_time <= 50:
        cmd = "ps afx|grep -E 'app_data -l [0-9]+'|grep -v grep"
        ret_code = os.system(cmd)
        # 注意，这里返回失败是代表app_data已经杀成功，查询不到是预期结果
        if ret_code:
            print_time_by_type("stop app_data", "end")
            return 0

        logging.error("APP_DATA_UPGRADE: app_data existed, check time(%d)" % check_time)
        check_time = check_time + 1
        time.sleep(0.1)
    return 1

def kill_app_data(process_id):

    retry_time = 0
    while True:
        if retry_time >= 3:
            return 1
        ret_code = os.system("killall -9 app_data")
        if ret_code == 0:
            break
        logging.error("APP_DATA_UPGRADE: kill app data failed, retry time(%d).", retry_time)
        retry_time += 1
        time.sleep(0.5)

    retry_time = 0
    while retry_time < 1000:
        new_process_id = get_process_id()
        if process_id !=  new_process_id:
            logging.error("APP_DATA_UPGRADE: app data not existed, old pid(%s) ,new pid(%s)",process_id, new_process_id)
            return 0
        retry_time +=1
        time.sleep(0.005)

    logging.error("APP_DATA_UPGRADE: app data pid not change, kill failed." )
    return 1


def online():
    cmd = '%s python %s/action/appctl get_online_result' % (LD_LIBRARY_PATH, TOP_DIR)
    ret_code = os.system(cmd)
    if ret_code:
        return 1
    return 0


def get_online_result():
    # 检查app_data进程状态normal
    if check_proc_state('app_data', PROCESS_ID):
        logging.error('APP_DATA_UPGRADE: App_data state not normal')
        return 1
    return 0


def offline():
    return 0


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

def post_install_release_rpm():
    cmd_list = [
        'rm -f /OSM/bin/app_data',
        'mv /OSM/component/app_data/bin/app_data /OSM/bin/huge',
        'ln -s /OSM/bin/huge/app_data /OSM/bin/app_data',
    ]
    ret_code = execute_cmd(cmd_list)
    if ret_code != 0:
        return 1

    return 0

def compile_app_data():

    # 拷贝link.tgz到磁盘
    if os.path.exists("/OSM/component/app_data/bin/app_data"):
        logging.info("APP_DATA_UPGRADE: begin to copy app_data")
        ret_code = post_install_release_rpm()
        if ret_code != 0:
            return 1
        logging.info("APP_DATA_UPGRADE: end to copy app_data")
    else:
        print_time_by_type("compile app data", "start")
        if os.system('/OSM/bin/link_app_data') != 0:
            logging.error("APP_DATA_UPGRADE: compile app_data failed")
            return 1
        print_time_by_type("compile app data", "end")
    chmod_app_data_owner()
    return 0

def execute_cmd(exec_cmd_list):
    for exec_cmd in exec_cmd_list:
        ret_code = os.system(exec_cmd)
        if ret_code != 0:
            logging.error("APP_DATA_UPGRADE: exec cmd (%s) failed(%d).", exec_cmd, ret_code)
            return 1
    return 0

def install_rpm():
    print_time_by_type("install rpm", "start")
    create_rpm_dir()
    # 到repo目录下取出rpm包来进行安装
    for file in os.listdir(REPO_DIR):
        if file.endswith('.rpm'):
            cmd = "rpm -ivh --nodeps --force --dbpath=%s %s" % (RPM_DB_PATH, os.path.join(REPO_DIR, file))
            ret_code = os.system(cmd)
            if ret_code:
                logging.error("APP_DATA_UPGRADE: install app_data service failed")
                return 1

    if os.path.exists("/OSM/component/app_data/bin/app_data"):
        logging.info("APP_DATA_UPGRADE: don't need to copy lik.tgz.")
    else:
        logging.info("APP_DATA_UPGRADE: begin to install link.tgz")
        if os.system('cp ' + os.path.join(REPO_DIR, 'link.tgz') + ' /OSM/coffer_data/dha') != 0:
            logging.error("APP_DATA_UPGRADE: cp link.tgz failed")
            return 1
        logging.info("APP_DATA_UPGRADE: end to install link.tgz")

    cmd_list = [
        'cd /OSM/conf; mv node_config.%s.xml /OSM/bin/node_config.xml' % get_cna_type(),
        'chown -h :64000 /OSM/bin/node_config.xml',
        'cp -af %s/manifest.yml %s/conf' % (TOP_DIR, RUN_DIR),
    ]

    ret_code = execute_cmd(cmd_list)
    if ret_code != 0:
        return 1

    ret_code = process_remove_root()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Process remove root failed.")
        return 1

    ret_code = setup_relation_dirs()
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: Setup relation dir failed.")
        return 1

    ret_code = execute_cmd(CHMOD_CERT_INI_DIR_CMD)
    logging.info("APP_DATA_UPGRADE: clear cert ini ret %d.", ret_code)

    print_time_by_type("install rpm", "end")
    return 0

def exec_shell_cmd(cmd):
    r = os.popen(cmd)
    res_str = r.read().strip()
    r.close()
    return res_str

def install_shmem_huge():
    NUMA_NODES = exec_shell_cmd("lscpu|grep \"NUMA node(s):\"|awk -F ':' '{ print strtonum($2) }'")
    NR_N0_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N1_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N2_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node2/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N3_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node3/hugepages/hugepages-2048kB/nr_hugepages")
    PRODUCT_MODEL = exec_shell_cmd("cat /proc/osp/bsp|grep \"Model of products is:\"|awk -F ':' '{ print $2 }'")
    shmem_cmd_list = [
        'mount -t tmpfs -o huge=always tmpfs /OSM/bin/huge/', 
        'echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan', 
        'echo 8640000 > /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs', 
        'echo never >/sys/kernel/mm/transparent_hugepage/defrag', 
    ]
    logging.info("APP_DATA HUGEPAGE before n0: %s , n1: %s , n2: %s n3 %s", NR_N0_HUGEPAGES, NR_N1_HUGEPAGES, NR_N2_HUGEPAGES, NR_N3_HUGEPAGES)
    #计算型存储因内存不够，不预留大页
    if "_XPU" not in PRODUCT_MODEL:
        if NUMA_NODES == "1":
            if str.isdigit(NR_N0_HUGEPAGES):
                n0_huge_page_num = int(NR_N0_HUGEPAGES)
                shmem_cmd_list = [
                    'sed -i "/vm.nr_hugepages/d" /etc/sysctl.conf',
                    'echo %d >/sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages'%(100+n0_huge_page_num),
                ]
        elif NUMA_NODES == "2" or NUMA_NODES == "3":
            if str.isdigit(NR_N0_HUGEPAGES) and str.isdigit(NR_N1_HUGEPAGES):
                n0_huge_page_num = int(NR_N0_HUGEPAGES)
                n1_huge_page_num = int(NR_N1_HUGEPAGES)
                shmem_cmd_list = [
                    'sed -i "/vm.nr_hugepages/d" /etc/sysctl.conf',
                    'echo %d >/sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages'%(100+n0_huge_page_num),
                    'echo %d >/sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages'%(100+n1_huge_page_num),
                ]
        elif NUMA_NODES == "4":
            if str.isdigit(NR_N0_HUGEPAGES) and str.isdigit(NR_N1_HUGEPAGES) and str.isdigit(NR_N2_HUGEPAGES) and str.isdigit(NR_N3_HUGEPAGES):
                n0_huge_page_num = int(NR_N0_HUGEPAGES)
                n1_huge_page_num = int(NR_N1_HUGEPAGES)
                n2_huge_page_num = int(NR_N2_HUGEPAGES)
                n3_huge_page_num = int(NR_N3_HUGEPAGES)
                shmem_cmd_list = [
                    'sed -i "/vm.nr_hugepages/d" /etc/sysctl.conf',
                    'echo %d >/sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages'%(100+n0_huge_page_num),
                    'echo %d >/sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages'%(100+n1_huge_page_num),
                    'echo %d >/sys/devices/system/node/node2/hugepages/hugepages-2048kB/nr_hugepages'%(100+n2_huge_page_num),
                    'echo %d >/sys/devices/system/node/node3/hugepages/hugepages-2048kB/nr_hugepages'%(100+n3_huge_page_num),
                ]
        tmpfs_cmd_list = [
            'mount -t tmpfs -o huge=always tmpfs /OSM/bin/huge/', 
            'echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan', 
            'echo 8640000 > /sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs', 
            'echo never >/sys/kernel/mm/transparent_hugepage/defrag', 
        ]
        shmem_cmd_list.extend(tmpfs_cmd_list)

    mmap_cmd_list = [
        'echo "26-27" > /proc/sys/vm/mmap_rnd_mask',
        'echo 20 > /proc/sys/vm/mmap_rnd_bits',
    ]

    ret_code = os.system("mkdir -p /OSM/bin/huge")
    if ret_code:
        logging.error("APP_DATA_UPGRADE: mkdir -p /OSM/bin/huge failed.")
        return 1

    if os.path.exists("/OSM/conf/appdata_shmem.conf"):
        ret_code = execute_cmd(shmem_cmd_list)
        logging.info("APP_DATA_UPGRADE: mount ret %d.", ret_code)
    if os.path.exists("/OSM/conf/appdata_mmap_rnd.conf"):
        ret_code = execute_cmd(mmap_cmd_list)
        logging.info("APP_DATA_UPGRADE: mmap ret %d.", ret_code)
    NR_N0_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node0/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N1_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node1/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N2_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node2/hugepages/hugepages-2048kB/nr_hugepages")
    NR_N3_HUGEPAGES = exec_shell_cmd("cat /sys/devices/system/node/node3/hugepages/hugepages-2048kB/nr_hugepages")
    logging.info("APP_DATA HUGEPAGE before n0: %s , n1: %s , n2: %s n3 %s", NR_N0_HUGEPAGES, NR_N1_HUGEPAGES, NR_N2_HUGEPAGES, NR_N3_HUGEPAGES)
    return 0

# R11切换, 编译环境是python3.7m  运行环境是3.9m  需要创建一个3.7m的软连接指向3.9m
def make_python_soft_link():
    # ARM环境且环境上不存在py3.7m.so.1.0, 存在py3.9m.so.1.0(滚动到R11或者快速升级后复位)
    # 616 ARM_Release包, 升级到R11(编译环境是py3.7 运行环境是py3.9) 
    if ARM and not os.path.exists("/usr/lib64/libpython3.7m.so.1.0"):
        if os.path.exists("/usr/lib64/libpython3.9.so.1.0"):
            try:
                os.symlink("/usr/lib64/libpython3.9.so.1.0", "/usr/lib64/libpython3.7m.so.1.0")
            except BaseException as e:
                logging.error("APP_DATA_UPGRADE: make soft link(/usr/lib64/libpython3.7m.so.1.0 -> /usr/lib64/libpython3.9.so.1.0) failed(%s)", e)
                return 1
    return 0

def install():
    print_time_by_type("install", "start")
    ret_code = install_shmem_huge()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: install shmem huge failed.")
        return 1

    ret_code = install_rpm()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: install rpm pkg failed.")
        return 1

    ret_code = execute_cmd(INSTALL_EXEC_CMD)
    if ret_code:
        return 1

    ret_code = make_python_soft_link()
    if ret_code:
        return 1

    if os.path.islink('{0}/.flow_trace_mem'.format(get_tmp_path())):
        logging.error(".fow_trace_mem is softlink. chmod 660 .flow_trace_mem failed.")
    else:
        os.chmod('{0}/.flow_trace_mem'.format(get_tmp_path()), 0o660)

    if not os.access(ROOT_FLAG_FILE_PATH, os.F_OK):
        cmd = "mkdir -p /tmp/sys; chmod 770 /tmp/sys"
    else:
        cmd = "mkdir -p /OSM/tmp/sys; chmod 1777 /OSM/tmp/sys"
    ret_code = os.system(cmd)
    if ret_code:
        logging.error("APP_DATA_UPGRADE: exec cmd (%s) failed(%d).", cmd, ret_code)
        return 1

    print_time_by_type("install", "end")
    return 0


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

    print_time_by_type("uninstall", "end")
    return 0


def check_status():
    return 0


def check_upgrade():
    old_cfg_path = os.path.join(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("APP_DATA_UPGRADE: 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("APP_DATA_UPGRADE: The old version is same(%s) with new version, no need upgrade.", old_ver)
        return 1
    logging.info("APP_DATA_UPGRADE: The old version(%s) is different with new version(%s), need upgrade.", old_ver, new_ver)
    return 2

def pre_upgrade_python():

    SRVSTATEPATH = "/OSM/component/app_data/lib/"
    if SRVSTATEPATH not in sys.path:
        sys.path.append(SRVSTATEPATH)

    global lib_sys_python_client
    try:
        import libsys_python_client as lib_sys_python_client
    except ImportError as e:
        logging.error('APP_DATA_UPGRADE: import python lib failed, info: %s, try CDLL', e)
        lib_sys_python_client = CDLL("libsys_python_client.so")
        pass
    retry_time  = 0
    while True:
        if retry_time >= 10:
            return 1
        result = lib_sys_python_client.EbsUpgradePreCheck()
        if result == 0:
            logging.info("APP_DATA_UPGRADE: Pre check result %d ", result)
            return 0
        logging.info("APP_DATA_UPGRADE: Pre check result %d, retry time(%d)"%(result, retry_time))
        retry_time = retry_time + 1
        time.sleep(0.5)

def  proc_data_offline_service():

    print_time_by_type("proc data offline", "start")

    SRVSTATEPATH = "/OSM/component/app_data/lib/"
    if SRVSTATEPATH not in sys.path:
        sys.path.append(SRVSTATEPATH)

    global lib_sys_python_client
    try:
        import libsys_python_client as lib_sys_python_client
    except ImportError as e:
        logging.error('APP_DATA_UPGRADE: import python lib failed, info: %s, try CDLL', e)
        lib_sys_python_client = CDLL("libsys_python_client.so")
        pass
    result = lib_sys_python_client.ProcDataOfflineService()
    logging.info("APP_DATA_UPGRADE: Proc data offline service result %d ", result)
    print_time_by_type("proc data offline", "end")
    return result

def pre_upgrade():
    print_time_by_type("pre check", "start")
    if check_process_num() != 0:
        return 1
    exec_cmd = "mv -f /OSM/bin/huge/app_data /OSM/bin/app_data"
    ret_code = os.system(exec_cmd)
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: exec cmd (%s) failed(%d).", exec_cmd, ret_code)

    cmd = '%s python %s/action/appctl get_pre_upgrade_result' % (LD_LIBRARY_PATH, TOP_DIR)
    ret_code = os.system(cmd)
    if ret_code:
        logging.error('APP_DATA_UPGRADE: os.system failed(%d)', ret_code)
        return 1

    cmd = 'LD_LIBRARY_PATH=/OSM/component/app_data/lib:/lib64:/usr/lib64:/usr/local/lib python ' \
          '%s/action/appctl pre_upgrade_python' % TOP_DIR
    ret_code = os.system(cmd)
    if ret_code:
        logging.error('APP_DATA_UPGRADE: os.system failed(%d)', ret_code)
        return 1
    ret_code = execute_cmd(CLEAR_CERT_INI_EXEC_CMD)
    logging.info("APP_DATA_UPGRADE: clear cert ini ret %d.", ret_code)

    print_time_by_type("pre check", "end")
    return 0

def get_pre_upgrade_result():
    if check_proc_state('app_data', PROCESS_ID):
        logging.error('APP_DATA_UPGRADE: app_data state not normal')
        return 1
    return 0

def copy_patch_file():
    if os.path.exists(CO_PATCH_RUNNING_PATH):
        shutil.rmtree(CO_PATCH_RUNNING_PATH)

    if not os.path.isdir(CO_PATCH_PATH):
        logging.info("APP_DATA_UPGRADE: No hotpatch to copy.")
        return True

    logging.info("APP_DATA_UPGRADE: Copy patch in %s.", CO_PATCH_PATH)
    shutil.copytree(CO_PATCH_PATH, CO_PATCH_RUNNING_PATH)
    logging.info("APP_DATA_UPGRADE: Copy patch successed.")
    return True


#  解决升级场景下的权限问题, 文件不存在导致的失败，忽略错误
def change_upgeade_permission():

    if os.path.islink('{0}/.flow_trace_mem'.format(get_tmp_path())):
        return False

    cmd_list = [
        'chmod 660 {0}/.flow_trace_mem'.format(get_tmp_path())
    ]
    ret_code = execute_cmd(cmd_list)
    if ret_code:
        return False
    return True

def app_data_upgrade():

    # 检查进程数量
    if check_process_num() != 0:
        return 1
    process_id = get_process_id()
    get_process_state(process_id)
    os.system(GEN_PRODUCT_INI_AUTO)
    logging.info("APP_DATA_UPGRADE: Gen product ini end.")
    # 等到指定时间和其它节点一起停I/0,杀进程
    time_sync_consistency()
    if os.path.exists(NOT_STOP_SERVER_FILE_FLAG) == False:
        cmd = 'LD_LIBRARY_PATH=/OSM/component/app_data/lib:/lib64:/usr/lib64:/usr/local/lib python ' \
          '%s/action/appctl proc_data_offline_service' % TOP_DIR
        os.system(cmd)
    ret_code = kill_app_data(process_id)
    if ret_code == 0:
        return 0
    return 1


def execute_upgrade():

    print_time_by_type("execute upgrade", "start")
    retry_time = 0
    while True:
        if retry_time >= 10:
            logging.error("APP_DATA_UPGRADE: Upgrade app_data failed")
            return 1

        ret_code = app_data_upgrade()
        if ret_code:
            logging.error("APP_DATA_UPGRADE: Upgrade app data failed, retry time(%d).", retry_time)
            retry_time = retry_time + 1
            time.sleep(0.5)
            continue
        print_time_by_type("execute upgrade", "end")
        return 0

def mkdir_huge_dir():
    if os.path.exists("/OSM/bin/huge"):
        return 0

    exec_cmd = "mkdir -p /OSM/bin/huge"
    ret_code = os.system(exec_cmd)
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: exec cmd (%s) failed(%d).", exec_cmd, ret_code)
        return 1
    return 0


def is_euleros_r10():
    os_release = platform.release()
    if "v2r10" in os_release:
            return True
    return False


def update_secure_boot_digest():
    if os.path.exists('/sys/kernel/security/ima/policy') and not is_release_crack_package():
        if is_euleros_r10():
            cmd_list = [
                'mv /etc/ima/digest_lists/0-metadata_list-compact-app_data.r10 /etc/ima/digest_lists/0-metadata_list-compact-app_data',
                'setfattr -n security.ima -v 0x$(xxd -p -c 1000 /etc/ima/digest_lists.sig/0-metadata_list-compact-app_data.sig) /etc/ima/digest_lists/0-metadata_list-compact-app_data',
                'echo /etc/ima/digest_lists/0-metadata_list-compact-app_data > /sys/kernel/security/ima/digest_list_data',
                'chcon -t osm_exec_t /OSM/bin/huge/app_data'
            ]
        else:
            cmd_list = [
                'mv /etc/ima/digest_lists/0-metadata_list-compact-app_data.r11 /etc/ima/digest_lists/0-metadata_list-compact-app_data',
                'echo /etc/ima/digest_lists/0-metadata_list-compact-app_data > /sys/kernel/security/ima/digest_list_data',
                'chcon -t osm_exec_t /OSM/bin/huge/app_data'
            ]
        ret_code = execute_cmd(cmd_list)
        if ret_code != 0:
            return 1
    return 0

def access_bin_and_so():
    if not is_release_package():
        logging.info("APP_DATA_UPGRADE: NOT release package")
        return 1
    print_time_by_type("access app_data so", "start")
    # 访问一次so，触发hash计算，缩短后期计算hash的时延
    SO_DIR = '/OSM/component/app_data/lib/'
    if os.path.exists(SO_DIR):
        for file in os.listdir(SO_DIR):
            try:
                with open(os.path.join(SO_DIR, file), 'rb') as f:
                    f.read(1)
                    logging.info("APP_DATA_UPGRADE: access %s so success" % file)
            except:
                logging.info("APP_DATA_UPGRADE: access %s" % file)
                continue
    else:
        logging.error("APP_DATA_UPGRADE: dir %s does NOT exist" % SO_DIR)
        return 1

    print_time_by_type("access app_data bin", "start")
    # 访问一次app_data二进制，触发hash计算，缩短后期计算hash的时延
    BIN_PATH = '/OSM/bin/huge/app_data'
    if os.path.exists(BIN_PATH):
        with open(BIN_PATH, 'rb') as f:
            f.read(1)
            logging.info("APP_DATA_UPGRADE: access %s bin success" % BIN_PATH)
    else:
        logging.error("APP_DATA_UPGRADE: %s does NOT exist" % BIN_PATH)
        return 1

    print_time_by_type("access app_data so and bin", "end")
    return 0

def upgrade():
    print_time_by_type("APP_DATA_UPGRADE:", "start")
    ret_code = check_process_num()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: Check process failed")
        return 1
    ret_code = mkdir_huge_dir()
    if ret_code:
        return 1
    ret_code = install_rpm()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: install rpm pkg failed.")
        return 1
    ret_code = compile_app_data()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: compile app_data failed.")
        return 1
    ret_code = copy_patch_file()
    if not ret_code:
        logging.error("APP_DATA_UPGRADE: Copy patch file failed.")
        return 1
    change_upgeade_permission()
    # 若开启了安全启动 更新签名文件; 在kill的前一步去更新, 否则在这中间app_data故障了老的app_data会拉不起来
    if update_secure_boot_digest() != 0:
        return 1
    # 安装rpm包之后，拉起进程之前，访问二进制和so，触发hash计算，缩短后期校验时间，从而缩短app_data拉起时间
    ret_code = access_bin_and_so()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: access app_data bin and so failed in upgrade.")

    return execute_upgrade()

def pre_rollback():
    exec_cmd = "mv -f /OSM/bin/huge/app_data /OSM/bin/app_data"
    ret_code = os.system(exec_cmd)
    if ret_code != 0:
        logging.error("APP_DATA_UPGRADE: exec cmd (%s) failed(%d).", exec_cmd, ret_code)
    ret_code = execute_cmd(CLEAR_CERT_INI_EXEC_CMD)
    logging.info("APP_DATA_UPGRADE: clear cert ini ret %d.", ret_code)

    return 0


def app_data_upgrade_rollback():

    process_id = get_process_id()
    ret_code = kill_app_data(process_id)
    if ret_code == 0:
        return 0
    return 1

def rollback():
    print_time_by_type("roll back", "start")
    ret_code = install_rpm()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: install rpm pkg failed.")
        return 1
    ret_code = compile_app_data()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: Compile app_data failed.")
        return 1
    ret_code = copy_patch_file()
    if not ret_code:
        logging.error("APP_DATA_UPGRADE: Copy patch file failed.")
        return 1

    # 若开启了安全启动 更新签名文件
    if update_secure_boot_digest() != 0:
        return 1
    # 安装rpm包之后，拉起进程之前，访问二进制和so，触发hash计算，缩短后期校验时间，从而缩短app_data拉起时间
    ret_code = access_bin_and_so()
    if ret_code:
        logging.error("APP_DATA_UPGRADE: access app_data bin and so failed in rollback.")

    retry_time = 0
    while True:
        if retry_time >= 10:
            logging.error("APP_DATA_UPGRADE: rollback app_data failed")
            return 1
        ret_code = app_data_upgrade_rollback()
        if ret_code:
            logging.error("APP_DATA_UPGRADE: rollback app_data failed，retry time(%d)" % retry_time)
            retry_time = retry_time + 1
            time.sleep(0.5)
            continue
        print_time_by_type("roll back", "end")
        return 0


def exec_build(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 {0}".format(e))
        return 1
    if func is None:
        return 1
    return func()


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