#!/bin/bash
# Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
# Description: dataturbo upgrade script
# Create: 2022/01/22
#

CUR_DIR="$( cd "$( dirname "$0"  )" && pwd )"
source $CUR_DIR/common.sh
readonly DATATURBO_SERVICE="dataturbo.service"
DATATURBO_VERSION="1.1.0"
DATATURBO_PATH=/opt/oceanstor/dataturbo
DATATURBO_CONF_PATH=/opt/oceanstor/dataturbo/conf
RAND_NUM=${RANDOM}
TEMP_BAK_DIR=/tmp/oceanstor_dataturbo_bak_${RAND_NUM}_$$
UPGRADE_RESULT=${DATATURBO_PATH}/script/upgrade_res
readonly LOCK_FILE="/var/log/upgrade.shell.lock"

# 顺序停服务,后续扩展直接往列表后面添加服务即可。
readonly stop_srv_array=(dataturbo.service)

readonly LOG_FILE="/var/log/dataturbo/dataturbo_script/dataturbo.action.log"

readonly USER_CONF_LIST=(cache_product_config_release.xml
                         cache_product_config.xml
                         dpc.conf
                         client.cfg
                         shim_config.xml
                         dpc_user_config.xml
                         dpc_config.xml
                         whitelist
                         dpc_mount_tab
                         version.xml
                         config/node_config.xml
                         ../bin/dpc_username_config.ini)

readonly USER_CONF_REMOCE_LIST=(dpc_alarm.xml
                                )

function usage()
{
    echo "usage: $0 [-h|-f]"
    echo "  -h                  display help."
    echo "  -f                  force upgrade dataturbo client (Force upgrade will stop the service, and the upgrade rollback may fail)."
}

function lock_shell()
{
    local thepid=""
    local ret=""

    if [ "$1" = "" ]; then
        log_warn_print "need lock file param"
        exit 1
    fi

    if [ -f "${1}" ]; then
        thepid=$(cat "${1}")
        if [ "${thepid}" != "" ]; then
            ret=$(ps ax |awk '{print $1}'| grep -c "^[[:blank:]]*${thepid}[[:blank:]]*$")
            if [ ${ret} -eq 1 ]; then
                log_warn_print "This shell task is already running, exit."
                exit 1
            fi
        fi
    fi

    echo $$ > "${1}"
    chmod 600 "${1}"
    return 0
}

function unlock_shell()
{
    if [ "$1" = "" ]; then
        return 1
    fi

    if [ -f "${1}" ]; then
        rm -f "${1}"
    fi

    return 0
}

function abnormal_exit()
{
    unlock_shell "${LOCK_FILE}"
    exit 1
}

function backup_file()
{
    mkdir -m 700 ${TEMP_BAK_DIR}
    if [ $? -ne 0 ]; then
        log_error_print "Create backup dir failed."
        return 1
    fi

    #backup
    cp -rp ${DATATURBO_PATH}/packages ${TEMP_BAK_DIR}/
    cp -rp ${DATATURBO_PATH}/conf ${TEMP_BAK_DIR}/
    mkdir -m 700 ${TEMP_BAK_DIR}/bin
    cp -p ${DATATURBO_PATH}/bin/dpc_username_config.ini ${TEMP_BAK_DIR}/bin/
    return 0;
}

function upgrade_rollback()
{
    local package_tail=""
    local os_name=$(cat /etc/os-release |grep ^NAME | awk -F "=" '{print $2}' | sed 's/\"//g')
    if [ "${os_name}" == "Ubuntu" ]; then
        package_tail="deb"
        old_package=$(ls ${TEMP_BAK_DIR}/packages/ | grep dataturbo | grep ${package_tail})
        if [ "${old_package}" == "" ]; then
            log_warn_print "Cat not find the old package"
            log_error_print "Rollback failed."
            return 1
        fi
        log_warn_print "Upgrade failed, start rollback to ${old_package}"
        
        # 卸载环境上的dataturbo deb包信息
        rm -rf /var/lib/dpkg/info/dataturbo.* 2>/dev/null
        dpkg --remove --force-remove-reinstreq dataturbo
        rm -rf ${DATATURBO_PATH} 2>/dev/null

        # 安装deb老包
        user_name=$(cat ${TEMP_BAK_DIR}/bin/dpc_username_config.ini | grep "username" | cut  -f2  -d "=")
        CUSTOM_USER=${user_name} dpkg -i ${TEMP_BAK_DIR}/packages/oceanstor_dataturbo*.${package_tail}
        if [ $? -ne 0 ]; then
            log_error_print "Install dataturbo deb failed."
            log_error_print "Rollback failed."
            return 1
        fi
    else
        package_tail="x86_64.rpm"
        if [ "$(uname -p)" == "aarch64" ]; then
            package_tail="aarch64.rpm"
        fi
        old_package=$(ls ${TEMP_BAK_DIR}/packages/ | grep dataturbo | grep ${package_tail})
        if [ "${old_package}" == "" ]; then
            log_warn_print "Cat not find the old package"
            log_error_print "Rollback failed."
            return 1
        fi
        log_warn_print "Upgrade failed, start rollback to ${old_package}"
    
        # 卸载环境上的dataturbo rpm包
        cur_env_packages=$(rpm -qa|grep dataturbo-*)
        for package in ${cur_env_packages}; do
            rpm -e ${package} --noscripts --nodeps 2>/dev/null
        done
        rm -rf ${DATATURBO_PATH} 2>/dev/null
        
        # 安装rpm老包
        user_name=$(cat ${TEMP_BAK_DIR}/dpc_username_config.ini | grep "username" | cut  -f2  -d "=")
        CUSTOM_USER=${user_name} rpm -ivh --nodeps ${TEMP_BAK_DIR}/packages/oceanstor_dataturbo*.${package_tail}
        if [ $? -ne 0 ]; then
            log_error_print "Install dataturbo rpm failed."
            log_error_print "Rollback failed."
            return 1
        fi
    fi

    cp -af ${TEMP_BAK_DIR}/packages/oceanstor_dataturbo*.${package_tail} ${DATATURBO_PATH}/packages/

    # 恢复所有配置文件
    rm -rf ${DATATURBO_PATH}/conf
    cp -rp ${TEMP_BAK_DIR}/conf ${DATATURBO_PATH}/
    rm -f ${DATATURBO_PATH}/bin/dpc_username_config.ini
    cp -p ${TEMP_BAK_DIR}/bin/dpc_username_config.ini ${DATATURBO_PATH}/bin/

    log_info_print "Rollback complete."
    return 0
}

function recover_user_config()
{
    for file in ${USER_CONF_LIST[*]}; do
        rm -f ${DATATURBO_CONF_PATH}/${file}
        log_info "file=${file}"
        log_info "copy ${TEMP_BAK_DIR}/conf/${file} to ${DATATURBO_CONF_PATH}/${file}"
        cp -p ${TEMP_BAK_DIR}/conf/${file} ${DATATURBO_CONF_PATH}/${file}
    done
    # DTS2023040701187 配置文件shim_config.xml中OpenFileNum的值1.1.0版本之前为1024，升级后修改为50000
    sed -i 's/<OpenFileNum min="8" max="4096">1024<\/OpenFileNum>/<OpenFileNum min="8" max="100000">50000<\/OpenFileNum>/g' ${DATATURBO_CONF_PATH}/shim_config.xml
}

function remove_old_config()
{
    for file in ${USER_CONF_REMOCE_LIST[*]}; do
        rm -f ${DATATURBO_CONF_PATH}/${file}
        log_info "file=${file} remove ret:$?"
    done
}

function upgrade_pkg()
{
    local package_tail=""
    local os_name=$(cat /etc/os-release |grep ^NAME | awk -F "=" '{print $2}' | sed 's/\"//g')
    if [ "${os_name}" == "Ubuntu" ]; then
        # 升级deb包: 先卸载后安装，传入环境变量DPKG_STAGE="upgrade"
        package_tail="deb"
        DPKG_STAGE="upgrade" dpkg --remove --force-remove-reinstreq dataturbo
        user_name=$(cat ${TEMP_BAK_DIR}/bin/dpc_username_config.ini | grep "username" | cut  -f2  -d "=")
        CUSTOM_USER=${user_name} DPKG_STAGE="upgrade" dpkg -i ${CUR_DIR}/packages/oceanstor_dataturbo*.${package_tail}
        ret=$?
    else
        # 升级rpm包
        package_tail="x86_64.rpm"
        if [ "$(uname -p)" == "aarch64" ]; then
            package_tail="aarch64.rpm"
        fi
        rpm -Uvh --nodeps ${CUR_DIR}/packages/oceanstor_dataturbo*.${package_tail}
        ret=$?
    fi
    if [ $ret -ne 0 ]; then
        upgrade_rollback
        return 1
    fi

    upgrade_ret=$(cat ${UPGRADE_RESULT} 2>/dev/null)
    if [ "${upgrade_ret}" != "" ] && [ "${upgrade_ret}" == "1" ]; then
        upgrade_rollback
        return 1
    fi

    if [ ! -f "${TEMP_BAK_DIR}/conf/dpc_user_config.xml" ]; then
        dpc_level_val=$(cat ${SHIM_CONF} | grep DpcLevelVal)
        dpc_level_val=${dpc_level_val#*>}
        dpc_level_val=${dpc_level_val%%<*}
        set_cgroup_limit_conf ${dpc_level_val}
    fi

    # 恢复用户升级前配置
    recover_user_config

    # 更新备份包
    rm -f ${DATATURBO_PATH}/packages/oceanstor_dataturbo*
    cp -af ${CUR_DIR}/packages/oceanstor_dataturbo*.${package_tail} ${DATATURBO_PATH}/packages/
    log_info_print "Upgrade dataturbo successfully."
    return 0
}

function stop_srv_array()
{
    local result=0

    for srv in "${stop_srv_array[@]}";
    do
        systemctl stop "${srv}" > /dev/null 2>&1
        if [ $? -ne 0 ]; then
            result=1
            log_error_print "Stop ${srv} failed. See \"systemctl status ${srv}\" for details."
        fi

        if [ "${result}" != "1" ]; then
            log_info_print "Stop ${srv} service successfully."
        fi
    done
    return $result
}

function stop_service()
{
    local result=0

    systemctl stop "${DATATURBO_SERVICE}" > /dev/null 2>&1
    if [ $? -ne 0 ]; then
        result=1
        log_error_print "Stop ${DATATURBO_SERVICE} failed. See \"systemctl status ${DATATURBO_SERVICE}\" for details."
    fi

    if [ "${result}" != "1" ]; then
        log_info_print "Stop dataturbo's service successfully."
        return ${result}
    fi
    return ${result}
}

function stop_dpc_process()
{
    for i in {0..3} # When getting PID fails, it will be retried 3 times
    do
        DPC_PID=$(pidof "dpc")
        if [ "${DPC_PID}" ];then
            kill -9 ${DPC_PID} 2>/dev/null
            return 0
        fi
        sleep 0.5
    done
    log_warn "Get pid of dpc process failed, process not exist."
    return 1
}

# 大于
function version_gt()
{
    test "$(echo "$@" | tr " " "\n" | sort -V | head -n 1)" != "$1";
}

#小于
function version_lt() 
{ 
    test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" != "$1"; 
}

# 大于等于
function version_ge()
{
    test "$(echo "$@" | tr " " "\n" | sort -rV | head -n 1)" == "$1";
}

# 升级前检查版本号
function check_dataturbo_version()
{
    local cur_pkg_version_name=""
    local cur_pkg_version=""
    local cur_pkg_release=""
    local package_tail=""
    local os_name=$(cat /etc/os-release |grep ^NAME | awk -F "=" '{print $2}' | sed 's/\"//g')
    if [ "${os_name}" == "Ubuntu" ]; then
        package_tail="deb"
        cur_pkg_version_name=$(dpkg -l | grep dataturbo | awk '{print $3}')
        if [ "${cur_pkg_version_name}" == "" ]; then
            log_warn_print "DataTurbo has not been installed yet."
            return 1
        fi
        cur_pkg_version=$(echo ${cur_pkg_version_name} | awk -F '-' '{print $1}')
        cur_pkg_release=$(echo ${cur_pkg_version_name} | awk -F '-' '{print $2}')
    else
        package_tail="x86_64.rpm"
        if [ "$(uname -p)" == "aarch64" ]; then
            package_tail="aarch64.rpm"
        fi
        cur_pkg_version_name=$(rpm -qa dataturbo 2>/dev/null)
        if [ "${cur_pkg_version_name}" == "" ]; then
            log_warn_print "DataTurbo has not been installed yet."
            return 1
        fi
        cur_pkg_version=$(echo ${cur_pkg_version_name} | awk -F '-' '{print $2}')
        cur_pkg_release=$(echo ${cur_pkg_version_name} | awk -F '-' '{print $3}' | awk -F '.' '{print $1}')
    fi

    local pkg_path=${CUR_DIR}/packages/
    dataturbo_pkg=$(ls ${pkg_path} | grep dataturbo | grep ${package_tail})
    DATATURBO_VERSION=${dataturbo_pkg%%-*}
    DATATURBO_VERSION=${DATATURBO_VERSION##*_}
    release=${dataturbo_pkg#*-}
    release=${release%%.*}

    log_info_print "Current package version: ${cur_pkg_version}-${cur_pkg_release}, upgrade package version: ${DATATURBO_VERSION}-${release}"
    # 当前版本小于升级版本直接升级
    if  version_lt ${cur_pkg_version} ${DATATURBO_VERSION}; then
        return 0
    # 如果当前版本大于升级版本返回1 
    elif version_gt ${cur_pkg_version} ${DATATURBO_VERSION}; then
        log_error_print "Current version is greater than or equal to upgrade version."
        return 1
    # 如果当前版本等于升级版本则比较release号(时间戳)
    else 
        if version_ge ${cur_pkg_release} ${release}; then
            log_error_print "Current release version ${cur_pkg_release} is greater than or equal to upgrade release version ${release}."
            return 1
        fi
    fi
    return 0
}

function check_old_package()
{
    package_name=$(ls ${DATATURBO_PATH}/packages/oceanstor_dataturbo* 2>/dev/null)
    if [ ! ${package_name} ]; then
        log_warn_print "Can not find oceanstor dataturbo backup rpm or deb package."
        return 1
    fi
    return 0
}

# 检查用户配置文件是否存在，存在 升级后才可继承配置
function check_config_file()
{
    cont=0   
    for file in ${USER_CONF_LIST[*]}; do
        ls ${DATATURBO_CONF_PATH}/${file} 1>/dev/null 2>/dev/null
        if [ $? -ne 0 ]; then
            log_warn_print "${DATATURBO_CONF_PATH}/${file} not exist"
            let cont++;
        fi
    done

    if [ ${cont} -ne 0 ]; then
        return 1;
    fi

    return 0;
}

# 升级前检查。
function upgrade_check()
{
    install_check
    if [ "$?" = "1" ]; then
        return 1
    fi

    # 升级前检查版本号
    check_dataturbo_version
    if [ "$?" == "1" ]; then
        log_error_print "Check dataturbo version failed."
        return 1
    fi

    if [ "$1" != "-f" ]; then
        check_old_package
        if [ "$?" -ne 0 ]; then
            return 1
        fi

        check_config_file
        if [ "$?" -ne 0 ]; then
            log_warn_print "Check config file failed."
            return 1
        fi
    fi

    # 升级场景检查dataturbo服务状态.
    check_srv_status
    ret=$?
    if [ "$ret" == "1" ] && [ "$1" == "-f" ]; then
        # 停止服务
        stop_srv_array
        if [ "$?" == "1" ]; then
            log_error_print "Stop dataturbo.service failed."
            return 1
        fi
    elif [ "$ret" == "1" ] && [ "$1" != "-f" ]; then
        log_warn_print "Check services status error, dataturbo service should be stopped before upgrade."
        return 1
    fi

    # 检查dpc进程
    check_dpc_status
    ret=$?
    if [ "$ret" == "1" ] && [ "$1" == "-f" ]; then
        # 停止服务
        stop_dpc_process
        if [ "$ret" == "1" ]; then
            log_error_print "dpc process stop failed."
            return 1
        fi
    elif [ "$ret" == "1" ] && [ "$1" != "-f" ]; then
        log_warn_print "check process status error, dpc process should be stopped before upgrade."
        return 1
    fi

    return 0
}

function do_upgrade() {
    lock_shell "${LOCK_FILE}"
    log_info "do upgrade with option: [$1]"

    # 升级前检查,参数-f:会自动停止DataTurbo服务
    upgrade_check $1
    if [ $? -ne 0 ]; then
        log_error_print "Upgrade check failed."
        abnormal_exit
    fi

    remove_old_config
    backup_file
    if [ $? -ne 0 ]; then
        log_error_print "Backup file before upgrade failed."
        abnormal_exit
    fi

    upgrade_pkg
    ret=$?

    rm -rf ${TEMP_BAK_DIR}
    unlock_shell "${LOCK_FILE}"
    
    exit $ret
}

if [ $# -gt 1 ]; then
    log_error_print "Wrong input parameter."
    usage
    exit 1
fi

PARA="$1"
if [ -z "${PARA}" ]; then
    do_upgrade
fi

while getopts hf options 2>/dev/null
do
    case $options in
        h)  #显示帮助信息
            usage
            exit 0
        ;;
        f)
            do_upgrade "-f"
        ;;
        *)
            usage
            exit 1
            ;;
    esac
done
usage
exit 1