#!/bin/bash

#LABEL_FOR_BUILD


##################################global define##################################
g_util_name=p2v-cloud-tool
g_util_desc="P2V Cloud Tool"
g_util_version=1.5.0

# log
g_cur_dir=$(dirname $(readlink -f $0))
g_log_dir="$g_cur_dir"/Logs
mkdir -p "$g_log_dir"
g_log_file=${g_log_dir}/p2vs_tool.log
g_log_err_file=${g_log_dir}/p2vs_tool_err.log
g_mount_file=${g_log_dir}/temp_mount_file.log
g_os_name=""
g_disk_index=0
g_part_index=0

function global::print_version() {
    cat <<EOF
$g_util_desc $g_util_version.
EOF
}


##################################common util##################################

#common error code
CODE_ERROR_COMMON_ERROR=1
CODE_ERROR_COMMON_NOT_FOUND=2
CODE_ERROR_COMMON_EMPTY_VAR=3
CODE_ERROR_COMMON_INVALID_PARAM=4

function common::get_datetime() {
    date +"%Y-%m-%d %H:%M:%S.%3N"
}

function common::get_datetime_string() {
    date +"%Y%m%d%H%M%S%3N"
}

function common::log() {
    local log_msg=$1
    local log_level=$2
    local log_function=$3
    if [ "$log_level" == "" ]; then
        log_level="Debug"
    fi
    if [ "$log_function" == "" ]; then
        log_function=${FUNCNAME[1]}
    fi
    log_info="[$log_level]\t[$log_function] $log_msg"
    log_info_time="$(common::get_datetime) $log_info"
    echo -e "$log_info_time" >>"$g_log_file"
}

function common::log_function() {
    local log_msg="Function: ${FUNCNAME[1]} $*"
    common::log "$log_msg" "Debug" "${FUNCNAME[1]}"
}

function common::log_var_exit() {
    if [ -z "$2" ]; then
        common::log "var $1 is empty" "Error" "${FUNCNAME[1]}"
        exit $((CODE_ERROR_COMMON_EMPTY_VAR))
    else
        common::log "var $1 is $2" "Debug" "${FUNCNAME[1]}"
    fi
}

function common::log_var() {
    if [ -z "$2" ]; then
        common::log "var $1 is empty" "Debug" "${FUNCNAME[1]}"
    else
        common::log "var $1 is $2" "Debug" "${FUNCNAME[1]}"
    fi
}

function common::backup_file() {
    common::log_function "$*"
    local file_path=$1
    common::log_var "file_path" "$file_path"
    if [ -z "$file_path" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi

    if [ ! -e "$file_path" ]; then
        common::log "$file_path not exist" "Error"
        return $((CODE_ERROR_COMMON_NOT_FOUND))
    fi

    local dest_file_path=${file_path}_p2v_bak_$(common::get_datetime_string)
    cp "$file_path" "$dest_file_path"
    local ret=$?
    if [[ $ret -ne 0 ]]; then
        common::log "backup $file_path to $dest_file_path failed" "Error"
    fi
    return $((ret))
}

function common::temp_mount() {
    common::log_function "$*"
    local mount_dev=$1
    local mount_path=$2
    common::log_var "mount_dev" "$mount_dev"
    common::log_var "mount_path" "$mount_path"
    if [ -z "$mount_dev" ] || [ -z "$mount_path" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi

    mount $mount_options "$mount_dev" "$mount_path" 2>$g_log_err_file
    local ret=$?
    if [[ $ret -ne 0 ]]; then
        dmesg | tail >>$g_log_err_file
        common::log "mount $mount_dev to $mount_path failed return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        return $((ret))
    fi

    # save to mount file by reserve
    if [ ! -e "$g_mount_file" ]; then
        common::log "create mount file: $g_mount_file"
        echo " " >"$g_mount_file"
    fi
    sed -i "1 i\ ${mount_dev}:${mount_path}" "$g_mount_file"
    return 0
}

function common::force_umount() {
    common::log_function "$*"
    local path=$1
    common::log_var_exit "path" "$path"
    if mount 2>$g_log_err_file | grep "$path"; then
        #try umount path
        for ((; a<20; a++)) do
            if umount "$path" 2>$g_log_err_file; then
                common::log "$path umounted"
                return 0
            fi
            sleep 3s
        done
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::temp_umount_all() {
    common::log_function "$*"
    # load from mount file
    if [ -e "$g_mount_file" ]; then
        while read line ; do
            if [ -z "$line" ]; then
                continue
            fi
            local mount_dev=""
            local mount_path=""
            mount_dev=$(echo "$line" | awk -F':' '{printf $1}')
            mount_path=$(echo "$line" | awk -F':' '{printf $2}')
            if [ -z "$mount_dev" ] && [ -z "$mount_path" ]; then
                continue
            fi
            common::log "try umount $mount_dev($mount_path)..."
            if mount 2>$g_log_err_file | grep "$mount_dev"; then
                umount "$mount_dev"
            fi
            if mount 2>$g_log_err_file | grep "$mount_path"; then
                umount "$mount_path" 2>$g_log_err_file
            fi
        done <"$g_mount_file"
        common::log "clear mount file"
        # clear mount file
        echo "" >"$g_mount_file"
    fi
}

function common::is_centos6() {
    common::log_function
    if uname -r 2>$g_log_err_file | grep "2\.6\.32"; then
        return 0
    else
        return 1
    fi
}

function common::check_whoami() {
    common::log_function
    if [ "$(id -u)" == "0" ]; then
        return 0
    else
        common::log "please use root or sudo" "Error"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_architecure() {
    local arch=""
    arch=$(uname -m)
    if [ -z "$arch" ]; then
        arch=$(uname -p)
    fi
    common::log "arch: $arch"
    echo "$arch"
}

function common::check_architecure() {
    common::log_function
    local arch=""
    arch=$(common::get_architecure)
    if echo "$arch" | grep -E 'x86_64|i[36]86|amd64|aarch64'; then
        return 0
    else
        common::log "not supported current architecture" "Error"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_os_issue() {
    common::log_function "$*"
    local root_dir=$1
    common::log_var_exit "root_dir" "$root_dir"
    local os_issue=""
    if [ -e "$root_dir"/etc/redhat-release ]; then
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/redhat-release)"
        common::log "read os_issue from redhat-release: $os_issue"
    elif [ -e "$root_dir"/etc/centos-release ]; then
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/centos-release)"
        common::log "read os_issue from centos-release: $os_issue"
    elif [ -e "$root_dir"/etc/os-release ]; then
        os_issue=$(grep "PRETTY_NAME" < ${root_dir}/etc/os-release | awk -F\" '{print $2}')
        common::log "read os_issue from os-release: $os_issue"
    else
        read -r os_issue <<< "$(sed '/^[[:blank:]]*$/d' ${root_dir}/etc/issue)"
        common::log "read os_issue from issue: $os_issue"
    fi
    echo "$os_issue"
}

function common::get_disk_part_fstype() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local part_fstype=""
    part_fstype=$(blkid -s TYPE "$disk_part" | awk -F"\"" '{print $2}')
    common::log "part_fstype: $part_fstype"
    echo "$part_fstype"
}

function common::get_disk_part_uuid() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local part_uuid=""
    part_uuid=$(blkid -s UUID "$disk_part" | awk -F"\"" '{print $2}')
    common::log "part_uuid: $part_uuid"
    echo "$part_uuid"
}

function common::gen_disk_part_fstab_line() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local mount_path=$2
    common::log_var_exit "mount_path" "$mount_path"
    local mount_arg1=$3
    common::log_var_exit "mount_arg1" "$mount_arg1"
    local mount_arg2=$4
    common::log_var_exit "mount_arg2" "$mount_arg2"
    local mount_arg3=$5
    common::log_var_exit "mount_arg3" "$mount_arg3"

    local part_uuid=""
    part_uuid=$(common::get_disk_part_uuid "$disk_part")
    if [ -z "$part_uuid" ]; then
        echo ""
        return
    fi

    local part_fstype=""
    part_fstype=$(common::get_disk_part_fstype "$disk_part")
    if [ -z "$part_fstype" ]; then
        echo ""
        return
    fi

    local fstab_line="UUID=$part_uuid    $mount_path    $part_fstype    $mount_arg1    $mount_arg2 $mount_arg3"
    common::log "fstab_line: $fstab_line"
    echo "$fstab_line"
}

function common::get_data_disk_devs() {
    common::log_function "$*"
    local root_disk_dev=$1
    local trans_disk_dev=$(common::get_disk_dev 1)
    local all_devs=$(export LC_ALL=C; fdisk -l 2>$g_log_err_file | grep -v mapper | grep -oE "Disk /dev/(nvme[0-9]+n[0-9]+|[a-z]+)" | awk '{printf ""$2" "}')
    common::log "all_devs: $all_devs"

    for dev in $all_devs
    do
        if [[ "$dev" != "$root_disk_dev" ]] && [[ "$dev" != "$trans_disk_dev" ]]; then
            data_disk_devs+="$dev "
        fi
    done

    common::log "data_disk_devs: $data_disk_devs"
    if [ -z "$data_disk_devs" ]; then
        echo ""
        return
    fi
    echo "$data_disk_devs" | awk '{$1=$1};1'
}

function common::get_disk_dev() {
    common::log_function "$*"
    local disk_num=$1
    local disk_dev=""

    disk_dev=$(export LC_ALL=C; fdisk -l 2>$g_log_err_file | grep -v mapper | grep -oE "Disk /dev/(nvme[0-9]+n[0-9]+|[a-z]+)" | sort | head -"$disk_num" | tail -1 | awk '{printf $2}')
    common::log "disk_dev: $disk_dev"
    if [ -z "$disk_dev" ]; then
        echo ""
        return
    fi
    echo "$disk_dev"
}

function common::get_disk_parts() {
    common::log_function "$*"
    local disk_dev=$1
    common::log_var_exit "disk_dev" "$disk_dev"
    local disk_name=${disk_dev##*/}
    common::log "disk_name: $disk_name"

    local disk_parts=""
    disk_parts=$(lsblk -n -no NAME -r /dev/${disk_name} 2>$g_log_err_file | grep -E "${disk_name}p?[0-9]+" | awk '{printf "/dev/"$1" "}')
    common::log "disk_parts: $disk_parts"
    echo "$disk_parts"
}

# check if disk part mount dir contain a sub path
# $1: disk part mount dir
# $2: sub path
function common::check_part_sub_path() {
    common::log_function "$*"
    local part_dir=$1
    local sub_path=$2
    local file_path="$part_dir/$sub_path"

    if [ -e "$file_path" ]; then
        common::log "found $file_path"
        return 0
    else
        return $((CODE_ERROR_COMMON_NOT_FOUND))
    fi
}

function common::mk_temp_dir() {
    common::log_function "$*"
    local name=$1
    local temp_dir=""
    temp_dir="/tmp/${name}$(common::get_datetime_string)"
    if ! mkdir -p "$temp_dir" 2>$g_log_err_file; then
        common::log "mkdir $temp_dir failed"
        temp_dir=""
    fi
    echo "$temp_dir"
}

# check if disk part contain sub paths
# $1: disk part
# $2: sub path
function common::check_part_sub_paths() {
    common::log_function "$*"
    local temp_dir=$1
    common::log_var "temp_dir" "$temp_dir"
    if [ -z "$temp_dir" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    local sub_path1=$2
    common::log_var "sub_path1" "$sub_path1"
    if [ -z "$sub_path1" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    local sub_path2=$3

    local ret=1
    local while_param=0
    while [ $while_param == 0 ]; do
        while_param=1
        if common::check_part_sub_path "$temp_dir" "$sub_path1"; then
            if [ -n "$sub_path2" ]; then
                if common::check_part_sub_path "$temp_dir" "$sub_path2"; then
                    ret=0
                fi
            else
                ret=0
            fi
        fi
    done

    if [[ $ret -eq 1 ]]; then
        return 1
    else
        return 0
    fi
}

function common::check_root_part() {
    common::log_function "$*"
    if common::check_part_sub_paths "$1" boot etc; then
        return 0
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_efi_part() {
    common::log_function "$*"
    local temp_dir=$1
    if common::check_part_sub_path "$temp_dir" EFI; then
        local efi_temp_dir="$temp_dir"/EFI
        if find "$efi_temp_dir" -name "*.efi" 2>$g_log_err_file; then
            return 0
        fi
    fi
        return $((CODE_ERROR_COMMON_ERROR))
}

function common::check_boot_part() {
    common::log_function "$*"
    local boot_dir=$1
    common::log_var "boot_dir" "$boot_dir"
    if [ -z "$boot_dir" ]; then
        return $((CODE_ERROR_COMMON_INVALID_PARAM))
    fi
    if ls $boot_dir/initr* 2>$g_log_err_file; then
        if ls $boot_dir/vmlinu* 2>$g_log_err_file; then
            return 0
        else
            return $((CODE_ERROR_COMMON_ERROR))
        fi
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_lib_part() {
    common::log_function "$*"
    local lib_dir=$1
    local ls_output=""
    ls_output=$(ls -l "$lib_dir" 2>/dev/null)
    common::log "ls -l $lib_dir: $ls_output"
    ls_output=$(ls -l "$lib_dir/" 2>/dev/null)
    common::log "ls -l $lib_dir/: $ls_output"
    if test -d "$lib_dir"/modules; then
        common::log "g_lib_path=/lib"
        return 0
    elif test -d "$lib_dir"/lib/modules; then
        common::log "g_lib_path=/usr"
        return 0
    else
        common::log "g_lib_path=none"
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::check_hidden_part(){
    common::log_function "$*"
    local disk_dev=$1
    local part_num=$2
    local part_flag=$(parted -m -s "$disk_dev" print 2>$g_log_err_file | grep "^$part_num:" | cut -d: -f7)
    common::log "The flag of part $part_num is $part_flag" "Info"
    if [[ "$part_flag" == *"bios_grub"* ]] || [[ "$part_flag" == *"boot"* ]]; then
        return 0
    else
        return $((CODE_ERROR_COMMON_ERROR))
    fi
}

function common::get_disk_part_dev() {
    common::log_function "$*"
    local disk_dev=$1
    local path_num=$2
    local disk_part_dev=""
    if [[ "$disk_dev" == "/dev/nvme"* ]]; then
        disk_part_dev="${disk_dev}p${path_num}"
    else
        disk_part_dev="${disk_dev}${path_num}"
    fi
    common::log "disk_part_dev: $disk_part_dev"
    echo "$disk_part_dev"
}

function common::get_part_off() {
    common::log_function "$*"
    local disk_dev=$1
    local part_off=0
    local tmp_part_nums=$(parted -s "$disk_dev" print  2>$g_log_err_file | awk '/^ / {print $1}')
    for tmp_part_num in $tmp_part_nums; do
        if common::check_hidden_part $disk_dev $tmp_part_num; then
            ((part_off++))
        fi
    done
    echo "$part_off"
}

function common::flush_disks() {
    common::log_function
    common::log "do disk data sync..."
    sync
}

function common::xfs_repair_part() {
    common::log_function "$*"
    local device=$1
    local mem_total=""
    mem_total=$(free -m | awk '/Mem:/ {print $2}')
    local code=$?
    if [[ $code -eq 0 ]]; then
        common::log "get total memory: ${mem_total} MB"
    else
        mem_total=1024
        common::log "get total memory failed, use default value: ${mem_total} MB"
    fi

    local ag_mem=0
    if [ "$mem_total" -ge 8192 ]; then
        ag_mem=1024
    elif [ "$mem_total" -ge 4096 ]; then
        ag_mem=512
    elif [ "$mem_total" -ge 2048 ]; then
        ag_mem=256
    else
        ag_mem=128
    fi
    common::log "set ag_mem: ${ag_mem} MB"

    common::log "do xfs_repair -L -m ${ag_mem} ${device}"
    xfs_repair -L -m "${ag_mem}" "${device}" >/dev/null 2>$g_log_err_file
    code=$?
    return $((code))
}

function common::fsck_part() {
    common::log_function "$*"
    local part_dev=$1
    common::log_var_exit "part_dev" "$part_dev"
    local part_fstype=$2
    common::log_var_exit "part_fstype" "$part_fstype"
    local fsck_flag=$3
    common::log_var_exit "fsck_flag" "$fsck_flag"

    # do disk data sync
    common::flush_disks

    #try umount part dev
    common::force_umount "$part_dev"
    local code=0
    if [ "$part_fstype" == "ext2" ] || [ "$part_fstype" == "ext3" ] ||
        [ "$part_fstype" == "ext4" ]; then
        local tune2fs_exec="$g_cur_dir"/bin/tune2fs
        chmod +x "$tune2fs_exec"
        local has_journal=0
        common::log "get and check dev $part_dev filesystem features..." "Info"
        if [ "$fsck_flag" == "1" ] && dumpe2fs -h "$part_dev" 2>/dev/null | grep 'Filesystem features' | grep 'has_journal'; then
            has_journal=1
            # force to disable ext journal function
            common::log "found filesystem features has_journal, try to turn off it..." "Info"
            "$tune2fs_exec" -ff -O ^has_journal "$part_dev"
        fi
        # do ext fsck
        common::log "do fsck.$part_fstype dev $part_dev..." "Info"
        fsck."$part_fstype" -fy "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "fsck return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
        fi
        if [ "$fsck_flag" == "1" ] && [[ $has_journal -eq 1 ]]; then
            # enable journal function again
            common::log "turn on filesystem features has_journal..." "Info"
            "$tune2fs_exec" -O has_journal "$part_dev"
            # do ext fsck again
            common::log "do fsck.$part_fstype dev $part_dev..." "Info"
            fsck."$part_fstype" -fy "$part_dev" >/dev/null 2>$g_log_err_file
            code=$?
        fi
    elif [ "$part_fstype" == "btrfs" ]; then
        common::log "do btrfsck dev $part_dev..." "Info"
        btrfsck --repair "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
    elif [ "$part_fstype" == "xfs" ]; then
        common::xfs_repair_part "$part_dev"
        code=$?
    elif [ "$part_fstype" == "ntfs" ]; then
        #ntfsfix -n "$part_dev" >/dev/null
        common::log "do nothing for ntfs $part_dev..." "Info"
    else
        common::log "try to do fsck(unknown fstype) dev $part_dev..." "Info"
        fsck -fy "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
    fi
    if [[ $code -ne 0 ]]; then
        common::log "fsck return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
    fi
}

function common::get_disk_part_mount_path() {
    common::log_function "$*"
    local disk_part=$1
    common::log_var_exit "disk_part" "$disk_part"
    local mount_path=""
    mount_path=$(df -P | grep "$disk_part" | awk '{print $6}')
    common::log "mount_path: $mount_path"
    echo "$mount_path"
}

function common::to_little_endian_hex() {
    local num=$1
    local count=$2
    local hex_str=$(printf "%0${count}x\n" $num)
    local len=${#hex_str}
    local result=""
    for ((i=$((len-2)); i>=0; i=i-2)); do
        result="${result}${hex_str:$i:2}"
    done

    echo "$result"
}

# sed with '[^[:space:]]+' will clear quotation which is in last position, such as:
#   sed -i -r -e "/console=[^[:space:]]+/s;console=[^[:space:]]+;;g" "/etc/default/grub"
#   input:  /etc/default/grub: GRUB_CMD_LINE="xxx=xx yyy=yy console=tty0"
#   output: /etc/default/grub: GRUB_CMD_LINE="xxx=xx yyy=yy
# the lack of quotation will cause grub mkconfig issues.
function common::add_sed_space_protect() {
    # insert 2 blankspaces to " before fixing
    sed -i -r -e "s/\"/  \"/g" "$1"
}

function common::remove_sed_space_protect() {
    # remove 2 blankspaces from " after fixing to recovery
    sed -i -r -e "s/  \"/\"/g" "$1"
}


export LC_ALL=C
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:/usr/local/bin
export LVM_SUPPRESS_FD_WARNINGS=1

function tool::print_usage() {
    global::print_version
    cat <<EOF
Usage: $(basename "$g_util_name") [OPTION...]
Examples:
  $g_util_name --help                           # show usage
  $g_util_name --version                        # show version
EOF
}

#SRC TYPE CODE
SRC_TYPE_WIN=9999

#error code
CODE_ERROR_CR_EXEC=20
CODE_ERROR_CR_PREPARE=21
CODE_ERROR_CR_BUILD=21
CODE_ERROR_CR_RAM=25
CODE_ERROR_CR_DOCKER_LOGIN=26
CODE_ERROR_CR_DOCKER_TAG=27
CODE_ERROR_CR_DOCKER_PUSH=28
CODE_ERROR_MOUNT_CR_WORK=33
CODE_ERROR_MOUNT_CR_DOCKER=34
CODE_ERROR_MOUNT_CR_SRC=35
CODE_ERROR_MOUNT_PRERSYNC=36
CODE_ERROR_INIT_DELETE_PART=101
CODE_ERROR_INIT_MAKE_LABEL=102
CODE_ERROR_INIT_MAKE_EXTEND=103
CODE_ERROR_INIT_MAKE_LOGICAL=104
CODE_ERROR_INIT_MAKE_PRIMARY=105
CODE_ERROR_INIT_MAKE_FS=106
CODE_ERROR_INIT_PART_NOT_EXIST=107
CODE_ERROR_INIT_FIX_GPT_TABLE=108
CODE_ERROR_INIT_VG_NOT_EXIST=110
CODE_ERROR_INIT_DELETE_LVM=111
CODE_ERROR_INIT_CREATE_LVM=112
CODE_ERROR_INIT_CREATE_PV=113
CODE_ERROR_INIT_CREATE_VG=114
CODE_ERROR_INIT_LVM_NOT_EXIST=115
CODE_ERROR_INIT_LVS_PV_CREATE=151
CODE_ERROR_INIT_LVS_VG_RESTORE=152
CODE_ERROR_INIT_LVS_VG_CHANGE=153

#global vars
g_src_part_num=""
g_is_win=0

# acr vars
_cr_progress_log=""
_a2c_pkg_name="app2container"


function tool::check_disk_part_exist() {
    common::log_function "$*"
    local disk_dev=$1
    local part_num=$2
    local get_part_num=""
    get_part_num=$(parted "$disk_dev" -s Unit MB print |
        grep -E "[0-9]+MB" | grep -v "$disk_dev" | grep -w "$part_num " | head -1 | awk '{printf $1}')
    common::log "get_part_num: $get_part_num"
    if [ -n "$get_part_num" ] && [ "$get_part_num" == "$part_num" ]; then
        return 0
    else
        return 1
    fi
}

function tool::check_lvm_exist() {
    common::log_function "$*"
    local vg_name=$1
    local lv_name=$2
    lvdisplay=$(lvdisplay $vg_name | grep "LV Name" | grep $lv_name 2>$g_log_err_file)
    if [ -n "$lvdisplay" ]; then
        return 0
    else
        return 1
    fi

}

function tool::check_vg_exist() {
    common::log_function "$*"
    local vg_name=$1
    vgdisplay $vg_name 2>$g_log_err_file
    local code=$?
    if [[ $code -ne 0 ]]; then
        return 1
    else
        return 0
    fi

}

function tool::prepare_rsync_disk_part() {
    common::log_function "$*"
    local part_dev=$1
    local disk_index=$2
    local part_index=$3
    local origin_disk_index=$4

    common::force_umount "$part_dev"
    if [[ -n $g_part_off ]]; then
        ((part_index=part_index-g_part_off))
    fi
    local new_mount_path=""
    new_mount_path="/mnt/edata${origin_disk_index}_${part_index}"
    if test -d "$new_mount_path"; then
        common::force_umount "$new_mount_path"
    else
        mkdir -p "$new_mount_path"
    fi

    common::log "mount $part_dev to rsync target path $new_mount_path..."
    if ! mount "$part_dev" "$new_mount_path" 2>$g_log_err_file; then
        common::log "mount rsync target path" "Error"
        return $((CODE_ERROR_MOUNT_PRERSYNC))
    fi
    return 0
}

function tool::get_extfs_infos() {
    local dev=$1
    local extfs_features=""
    local block_size=""
    extfs_features=$(dumpe2fs -h "$part_dev" 2>/dev/null | grep 'Filesystem features' |
        cut -d ':' -f2 | tr -s ' ' ',' | sed 's/^,//' | sed 's/,$//')
    block_size=$(dumpe2fs -h "$part_dev" 2>/dev/null | grep "Block size" |
        cut -d ':' -f2 | awk '{print $1}')
    # use + to split
    echo "$block_size+$extfs_features"
}

function tool::get_xfs_infos() {
    local dev=$1
    local select_keys=$2
    local xfs_info=""
    local block_size=""
    xfs_info=$(xfs_info "$dev" 2>/dev/null | sed 's/ blks//g' | sed 's/,//g')
    block_size=$(xfs_info "$dev" 2>/dev/null | grep -i '^data' | grep -Eo "bsize=[0-9]+" | awk -F'=' '{print $2}')
    if [ -z "$xfs_info" ] || [ -z "$block_size" ]; then
        return
    fi

    local output="bsize=$block_size"
    local keys_pattern=""
    keys_pattern=${select_keys//,/|}
    echo "$xfs_info" |
        awk -v pattern="^($keys_pattern)=" -v output="$output" '{
            for (i = 1; i <= NF; i++) {
                if ($i ~ pattern) {
                    if (length(output) > 0) {
                        output = output ","
                    }
                    output = output $i
                }
            }
        }
        END { print output }'
}

function tool::get_fs_options() {
    local fs_type=$1
    local mock_dev_file=/tmp/mock_dev_file
    local part_dev=""
    local loop_dev=""
    local ret=0
    local fs_options=""
    while true; do
        #mock part dev with loop
        if ! which losetup >/dev/null 2>&1; then
            ret=2
            break
        fi
        if ! dd if=/dev/zero of=$mock_dev_file bs=1M count=100 >/dev/null 2>&1; then
            ret=3
            break
        fi
        loop_dev=$(losetup -f)
        if [ -z "$loop_dev" ]; then
            ret=4
            break
        fi
        if ! losetup -f $mock_dev_file >/dev/null 2>&1; then
            ret=5
            break
        fi
        if ! mkfs.$fs_type "$loop_dev" >/dev/null 2>&1; then
            ret=6
            break
        fi
        part_dev=$loop_dev
        if [ "$fs_type" == "ext2" ] || [ "$fs_type" == "ext3" ] || [ "$fs_type" == "ext4" ]; then
            fs_options=$(tool::get_extfs_infos "$part_dev")
        elif [ "$fs_type" == "xfs" ]; then
            fs_options=$(tool::get_xfs_infos "$part_dev" \
"isize,attr,projid32bit,crc,finobt,sparse,rmapbt,reflink,bigtime,inobtcount,\
ascii-ci,ftype,\
version,lazy-count")
        fi
        if [ -z "$fs_options" ]; then
            ret=7
            break
        fi
        break
    done
    if [ -n "$loop_dev" ]; then
        losetup -d "$loop_dev" 2>/dev/null
    fi
    if [ -f "$mock_dev_file" ]; then
        \rm -f "$mock_dev_file" 2>/dev/null
    fi
    echo "$fs_options"
    return $((ret))
}

function tool::get_xfs_option_key_to_cmd() {
    local option_key=$1
    local option_value=$2
    local xfs_options=""
    case "$option_key" in
        bsize)
            if [ -z "$option_value" ]; then option_value=4096; fi
            xfs_options+=" -b size=$option_value"
            ;;
        isize)
            if [ -z "$option_value" ]; then option_value=256; fi
            xfs_options+=" -i size=$option_value"
            ;;
        attr)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -i attr=$option_value"
            ;;
        projid32bit)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -i projid32bit=$option_value"
            ;;
        crc)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m crc=$option_value"
            ;;
        finobt)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m finobt=$option_value"
            ;;
        sparse)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -i sparse=$option_value"
            ;;
        rmapbt)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m rmapbt=$option_value"
            ;;
        reflink)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m reflink=$option_value"
            ;;
        bigtime)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m bigtime=$option_value"
            ;;
        inobtcount)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -m inobtcount=$option_value"
            ;;
        ascii-ci)
            if [ -z "$option_value" ]; then option_value=2; fi
            if [ "$option_value" == "1" ]; then
                xfs_options+=" -n version=ci"
            elif [ "$option_value" == "0" ]; then
                xfs_options+=" -n version=2"
            fi
            ;;
        ftype)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -n ftype=$option_value"
            ;;
        version)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -l version=$option_value"
            ;;
        lazy-count)
            if [ -z "$option_value" ]; then option_value=0; fi
            xfs_options+=" -l lazy-count=$option_value"
            ;;
    esac
    echo "$xfs_options"
}

function tool::get_mkfs_base_options() {
    local fs_type=$1
    local inode_num=$2
    local base_options=""
    if [ "$fs_type" == "ext4" ] || [ "$fs_type" == "ext3" ] || [ "$fs_type" == "ext2" ]; then
        base_options="-m 1"
        if [ "$fs_type" == "ext4" ]; then
            base_options+=" -F -E lazy_itable_init=1"
        fi
    elif [ "$fs_type" == "xfs" ]; then
        base_options="-f"
    elif [ "$fs_type" == "btrfs" ]; then
        if ! common::is_centos6; then
            base_options="-f"
        fi
    fi
    if [ "$inode_num" != "-" ] && [ "$inode_num" != "0" ]; then
        base_options+=" -N ${inode_num}"
    fi
    echo "$base_options"
}

function tool::get_mkfs_more_options() {
    local fs_type=$1
    local inode_num=$2
    local fs_options_string=$3

    local more_options=""
    if [ "$fs_type" == "ext4" ] || [ "$fs_type" == "ext3" ] || [ "$fs_type" == "ext2" ]; then
        if [ -n "$fs_options_string" ]; then
            local block_size=""
            local features_string=""
            block_size=$(echo "$fs_options_string" | awk -F"+" '{printf $1}')
            features_string=$(echo "$fs_options_string" | awk -F"+" '{printf $2}')

            local e2fs_options=""
            IFS=','
            local default_options_string=""
            default_options_string=$(tool::get_fs_options $fs_type)
            if [ -z "$default_options_string" ]; then
                return
            fi
            local default_block_size=""
            local default_features_string=""
            default_block_size=$(echo "$default_options_string" | awk -F"+" '{printf $1}')
            default_features_string=$(echo "$default_options_string" | awk -F"+" '{printf $2}')

            local default_options=()
            read -ra default_options <<< "$default_features_string"
            local fs_options=()
            read -ra fs_options <<< "$features_string"
            unset IFS

            if [ "$block_size" != "$default_block_size" ]; then
                more_options+=" -b $block_size"
            fi
            # compare transition server's default mkfs options with src fs options
            # then filter the same options and deal with diffient options
            for fs_option in "${fs_options[@]}"; do
                if [ "$fs_option" == "needs_recovery" ] ; then
                    continue
                fi
                local is_default=0
                for default_option in "${default_options[@]}"; do
                    if [ "$fs_option" == "$default_option" ]; then
                        is_default=1
                        break
                    fi
                done
                if [[ $is_default -eq 0 ]]; then
                    if [ -n "$e2fs_options" ]; then
                        e2fs_options+=",$fs_option"
                    else
                        e2fs_options="$fs_option"
                    fi
                fi
            done
            for default_option in "${default_options[@]}"; do
                if [ "$default_option" == "needs_recovery" ]; then
                    continue
                fi
                local need_disable=1
                for fs_option in "${fs_options[@]}"; do
                    if [ "$default_option" == "$fs_option" ]; then
                        need_disable=0
                        break
                    fi
                done
                if [[ $need_disable -eq 1 ]]; then
                    if [ -n "$e2fs_options" ]; then
                        e2fs_options+=",^$default_option"
                    else
                        e2fs_options="^$default_option"
                    fi
                fi
            done
            if [ -n "$e2fs_options" ]; then
                more_options+=" -O $e2fs_options"
            fi
        fi
    elif [ "$fs_type" == "xfs" ]; then
        if [ -n "$fs_options_string" ]; then
            local xfs_options=""
            IFS=','
            local default_options_string=""
            default_options_string=$(tool::get_fs_options $fs_type)
            if [ -z "$default_options_string" ]; then
                return
            fi
            local default_options=()
            read -ra default_options <<< "$default_options_string"
            local fs_options=()
            read -ra fs_options <<< "$fs_options_string"
            unset IFS

            # compare transition server's default mkfs options with src fs options
            # then filter the same options and deal with diffient options
            for fs_option in "${fs_options[@]}"; do
                local is_default=0
                for default_option in "${default_options[@]}"; do
                    if [ "$fs_option" == "$default_option" ]; then
                        is_default=1
                        break
                    fi
                done
                if [[ $is_default -eq 0 ]]; then
                    local option_key=""
                    option_key=$(echo "$fs_option" | awk -F"=" '{printf $1}')
                    local option_value=""
                    option_value=$(echo "$fs_option" | awk -F"=" '{printf $2}')
                    local xfs_option_key_cmd=""
                    xfs_option_key_cmd=$(tool::get_xfs_option_key_to_cmd $option_key $option_value)
                    xfs_options+=$xfs_option_key_cmd
                fi
            done
            for default_option in "${default_options[@]}"; do
                local def_option_key=""
                def_option_key=$(echo "$default_option" | awk -F"=" '{printf $1}')
                local need_disable=1
                for fs_option in "${fs_options[@]}"; do
                    local option_key=""
                    option_key=$(echo "$fs_option" | awk -F"=" '{printf $1}')
                    if [ "$option_key" == "$def_option_key" ]; then
                        need_disable=0
                        break
                    fi
                done
                if [[ $need_disable -eq 1 ]]; then
                    local option_key=""
                    option_key=$(echo "$default_option" | awk -F"=" '{printf $1}')
                    local option_value=""
                    option_value=$(echo "$default_option" | awk -F"=" '{printf $2}')
                    local xfs_option_key_cmd=""
                    xfs_option_key_cmd=$(tool::get_xfs_option_key_to_cmd $option_key)
                    xfs_options+=$xfs_option_key_cmd
                fi
            done
            if [ -n "$xfs_options" ]; then
                more_options+=" $xfs_options"
            fi
        fi
    fi
    echo "$more_options"
}

function tool::try_wipe_pv_dev() {
    common::log_function "$*"
    local dev=$1
    common::log_var_exit "dev" "$dev"

    local code=0
    local temp_vg_name=""
    common::log "try get vg_name..."
    temp_vg_name=$(pvdisplay $dev -C --noheadings -o vg_name | awk '{printf $1}')
    common::log_var "temp_vg_name" "$temp_vg_name"
    if [ -n "$temp_vg_name" ]; then
        # try remove old vg
        common::log "try remove old vg: $temp_vg_name" "Info"
        echo y | vgremove -f $temp_vg_name 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "try_wipe_pv_dev vgremove return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
        fi
    fi
    # try wiped out old pv dev
    #The default metadatasize of LVM is 1MB on pv dev
    common::log "try wiped out old pv dev..." "Info"
    dd if=/dev/zero of=$dev bs=512 count=2048
    echo y | pvremove --force --force $dev 2>$g_log_err_file
    code=$?
    if [[ $code -ne 0 ]]; then
        common::log "init lvs pvremove return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
    fi
    return $((code))
}

function tool::init_disk_part() {
    common::log_function "$*"
    local disk_dev=$1
    local part_dev=$2
    local disk_index=$3
    local part_index=$4
    local part_num=$5
    local is_fsb=$6
    local size_sectors=$7
    local is_gpt=$8
    local is_boot=$9
    local more_info=${10}

    local fs_type=""
    fs_type=$(echo "$more_info" | awk -F":" '{printf $1}')
    local inode_num="-"
    inode_num=$(echo "$more_info" | awk -F":" '{printf $2}')
    local disk_offset=""
    disk_offset=$(echo "$more_info" | awk -F":" '{printf $3}')
    if [ "$disk_offset" == "-" ]; then
        disk_offset=""
    elif [[ $disk_offset == 0 ]]; then
        common::log "it is raw mounted disk"
    fi

    local fs_options_string=""
    fs_options_string=$(echo "$more_info" | awk -F":" '{printf $4}')
    if [ "$fs_options_string" == "-" ]; then
        fs_options_string=""
    fi
    local is_lvm=""
    local vg_name=""
    local lv_name=""
    is_lvm=$(echo "$more_info" | awk -F":" '{printf $5}')
    vg_name=$(echo "$more_info" | awk -F":" '{printf $6}')
    if [ "$vg_name" == "-" ]; then
        vg_name=""
    fi
    lv_name=$(echo "$more_info" | awk -F":" '{printf $7}')
    if [ "$lv_name" == "-" ]; then
        lv_name=""
    fi
    if [[ $is_fsb -ne 1 ]]; then
        common::log_var_exit "fs_type" "$fs_type"
        common::log_var_exit "inode_num" "$inode_num"
    fi

    local ret=0
    local code=0
    if [ -n "$g_src_part_num" ]; then
        common::force_umount "$part_dev"
    elif [ "$is_lvm" == "1" ]; then
        if tool::check_vg_exist "$vg_name"; then
            part_dev="/dev/$vg_name/$lv_name"
            if tool::check_lvm_exist "$vg_name" "$lv_name"; then
                # try delete exist lvm
                common::force_umount "$part_dev"
                common::log "delete existed lvm $lv_name on $vg_name..."
                lvremove $part_dev 2>$g_log_err_file
                code=$?
                if [[ $code -ne 0 ]]; then
                    common::log "delete existed lvm $lv_name return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_DELETE_LVM
                    return $((ret))
                fi
            else
                local pe_free=$(vgdisplay $vg_name | grep "Free  PE" | awk '{print $5}')
                local pe_size_bytes=4194304
                local size_bytes=$((size_sectors*512))
                local pe_required=$((size_bytes/pe_size_bytes))
                local remainder=$((size_bytes%pe_size_bytes))
                if [ $remainder -gt 0 ]; then
                    pe_required=$((pe_required+1))
                fi
                if [ $pe_required -gt $pe_free ]; then
                    pe_required=$pe_free
                fi
                common::log "create lvm $lv_name on vg $vg_name, allocated pe: $pe_required..."
                lvcreate --yes -n $lv_name -l $pe_required $vg_name 2>$g_log_err_file
                code=$?
                if [[ $code -ne 0 ]]; then
                    common::log "create lvm $lv_name return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_CREATE_LVM
                    return $((ret))
                fi
            fi
        else
            ret=$CODE_ERROR_INIT_VG_NOT_EXIST
            return $((ret))
        fi
    else
        # try umount pre part in case current mkpart fails
        local pre_part_num=0
        if [[ $part_num -eq 5 ]] && [[ $is_gpt -eq 0 ]]; then
            pre_part_num=3
        else
            ((pre_part_num=part_num-1))
        fi
        common::log_var "pre_part_num" "$pre_part_num"

        if [[ $pre_part_num -eq 0 ]] || ([[ -n $g_part_off ]] && [[ $part_num -eq $((g_part_off + 1)) ]]); then
            common::log "try umount all disk parts before init disk" "Info"
            local disk_parts=""
            disk_parts=$(common::get_disk_parts "$disk_dev")
            if [ -n "$disk_parts" ]; then
                # travel disk parts
                for disk_part in $disk_parts
                do
                    if [ -z "$disk_part" ]; then
                        continue
                    fi
                    common::force_umount "$disk_part"
                done
            fi
            # try to erase all parts when first init part in a disk
            common::log "try to erase all parts when first init part in a disk: $disk_dev" "Info"
            if [[ $is_gpt -eq 0 ]]; then
                #erase mbr part table
                dd if=/dev/zero of=$disk_dev bs=1 count=64 seek=446 2>$g_log_err_file
                code=$?
            else
                #erase all parts except hidden parts
                local tmp_part_nums=$(parted -s "$disk_dev" print | awk '/^ / {print $1}')
                for tmp_part_num in $tmp_part_nums; do
                    if ! common::check_hidden_part $disk_dev $tmp_part_num; then
                        parted -s "$disk_dev" rm "$tmp_part_num" >>"$g_log_file" 2>$g_log_err_file
                    fi
                done
            fi
            if [[ $code -ne 0 ]]; then
                common::log "erase all parts failed: $code, error_msg: $(cat $g_log_err_file)" "Error"
                ret=$CODE_ERROR_INIT_DELETE_PART
                return $((ret))
            fi
            partprobe -s $disk_dev
        elif [[ $pre_part_num -ge 1 ]]; then
            local pre_part_dev=""
            pre_part_dev=$(common::get_disk_part_dev $disk_dev $pre_part_num)
            if [ -e "$pre_part_dev" ]; then
                common::log "try umount pre_part $pre_part_dev" "Info"
                common::force_umount "$pre_part_dev"
            fi
        fi

        # check size_sectors
        if [[ $size_sectors -lt 0 ]]; then
            common::log "size_sectors < 0" "Error"
            return $((CODE_ERROR_COMMON_INVALID_PARAM))
        elif [[ $size_sectors -eq 0 ]]; then
            if [ "$is_fsb" != "1" ]; then
                local size_b=0
                size_b=$(fdisk "$disk_dev" -l | grep -w "$disk_dev" | grep bytes | awk '{printf $5}')
                local size_gb=0
                ((size_gb=size_b/1024/1024/1024))
                common::log "size_gb: $size_gb"
                if [[ $size_gb -ge 2048 ]]; then
                    is_gpt=1
                    common::log "set is_gpt=1"
                fi
            else
                common::log "size_sectors = 0" "Error"
                return $((CODE_ERROR_COMMON_INVALID_PARAM))
            fi
        fi

        # 1.make part
        local start_sector=0
        local end_sector=0
        local fs_flag=""
        local boot_flag=""

        if [[ $g_is_win -eq 1 ]]; then
            fs_flag="ntfs"
        fi

        # 1.1.check and make disk mklabel type msdos/gpt
        # make mbr/gpt label before make first partition
        if [[ $part_num -eq 1 ]]; then
            local mklabel_type=""
            local print_result=""
            print_result=$(parted "$disk_dev" -s print 2>$g_log_err_file)
            print_result_err=$(cat $g_log_err_file)
            if echo "$print_result" | grep -q "unrecognised disk label" ||
                echo "$print_result_err" | grep -q "unrecognised disk label"; then
                common::log "current mklabel_type: unrecognised disk label"
                #need to fix unrecognised disk label by making label
                if [[ $is_gpt -eq 0 ]]; then
                    mklabel_type="msdos"
                else
                    mklabel_type="gpt"
                fi
            elif echo "$print_result" | grep "Partition Table" | grep -q "gpt"; then
                common::log "current mklabel_type: gpt"
                if [[ $is_gpt -eq 0 ]]; then
                    mklabel_type="msdos"
                fi
            elif echo "$print_result" | grep "Partition Table" | grep -q "msdos"; then
                common::log "current mklabel_type: msdos"
                if [[ $is_gpt -eq 1 ]]; then
                    mklabel_type="gpt"
                fi
            else
                common::log "current mklabel_type: unsupport disk label"
                if [[ $is_gpt -eq 0 ]]; then
                    mklabel_type="msdos"
                else
                    mklabel_type="gpt"
                fi
            fi
            if [ -n "$mklabel_type" ]; then
                common::log "try make disk label type: $mklabel_type on dev $disk_dev..."
                parted "$disk_dev" -s mklabel "$mklabel_type" 2>$g_log_err_file
                code=$?
                if [[ $code -ne 0 ]]; then
                    if grep -qi "have been written" $g_log_err_file 2>/dev/null; then
                        common::log "make disk label success but return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
                    else
                        common::log "make disk label return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                        ret=$CODE_ERROR_INIT_MAKE_LABEL
                        return $((ret))
                    fi
                fi
                common::log "partprobe on $disk_dev..."
                partprobe -s "$disk_dev"
            fi

            if [ -n "$disk_offset" ]; then
                start_sector=$disk_offset
            else
                start_sector=2048
            fi
        else
            local last_end_sector=0
            last_end_sector=$(parted "$disk_dev" -s Unit S print | grep -E "[0-9]+s" |
                grep -w "$pre_part_num" | tail -1 | awk '{printf $3}' | grep -oE "[0-9]+")
            common::log "last_end_sector: $last_end_sector"
            start_sector=$((last_end_sector + 1))
            common::log "start_sector: $start_sector"
        fi

        end_sector=$((start_sector + size_sectors - 1))
        common::log "end_sector: $end_sector"

        # 1.2.check and delete existed part
        if tool::check_disk_part_exist "$disk_dev" "$part_num"; then
            common::force_umount "$part_dev"
            common::log "delete existed part $part_num on $disk_dev..."
            parted "$disk_dev" -s rm "$part_num" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                common::log "delete existed part return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                ret=$CODE_ERROR_INIT_DELETE_PART
                return $((ret))
            fi
            # also delete extend part when it is the first logical part
            if [[ $part_num -eq 5 ]] && [[ $is_gpt -eq 0 ]]; then
                common::log "delete existed extend part 4 on $disk_dev..."
                parted "$disk_dev" -s rm 4 2>$g_log_err_file
                code=$?
                if [[ $code -ne 0 ]]; then
                    common::log "delete extend part return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_DELETE_PART
                    return $((ret))
                fi
            fi
            common::log "partprobe on $disk_dev..."
            partprobe -s "$disk_dev"
        fi

        # 1.3.make part
        local start_param=""
        local end_param=""
        start_param=${start_sector}s
        if [[ $size_sectors -gt 0 ]]; then
            end_param=${end_sector}s
        else
            end_param="100%"
        fi
        common::log "start_param: $start_param"
        common::log "end_param: $end_param"
        # set boot flag
        # make boot partition
        if [[ $is_boot -eq 1 ]]; then
            boot_flag="set $part_num boot on"
            common::log "boot_flag: $boot_flag"
        fi

        if [[ $part_num -eq 5 ]] && [[ $is_gpt -eq 0 ]]; then
            # make extend part(only for mbr) if need, 
            common::log "make extend part on dev $disk_dev, size: $start_param-100%"
            parted "$disk_dev" -s mkpart extended "$start_param" 100% 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                if grep -qi "have been written" $g_log_err_file 2>/dev/null; then
                    common::log "make extended part success but return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
                else
                    common::log "make extended part return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_MAKE_EXTEND
                    return $((ret))
                fi
            fi
            sleep 1s
            common::log "partprobe on $disk_dev..."
            partprobe -s "$disk_dev"
        fi

        if [[ $part_num -ge 5 ]] && [[ $is_gpt -eq 0 ]]; then
            # (only for mbr)
            # prepare first logical start/end param, because it takes extra 2048 sector.
            ((start_sector=start_sector+2048))
            start_param=${start_sector}s
            ((end_sector=end_sector+2048))
            end_param=${end_sector}s
            # make logic part
            common::log "make first logical part on $disk_dev, start-end: $start_param-$end_param..."
            parted "$disk_dev" -s mkpart logical "$start_param" "$end_param" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                if grep -qi "have been written" $g_log_err_file 2>/dev/null; then
                    common::log "make logical part success but return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
                else
                    common::log "make logical part return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_MAKE_LOGICAL
                    return $((ret))
                fi
            fi
        else
            # make primary part
            common::log "make primary part on $disk_dev, start-end: $start_param-$end_param boot_flag: $boot_flag..."
            parted "$disk_dev" -s mkpart primary "$fs_flag" "$start_param" "$end_param" "$boot_flag" 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                if grep -qi "have been written" $g_log_err_file 2>/dev/null; then
                    common::log "make primary part success but return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
                else
                    common::log "make primary part return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                    ret=$CODE_ERROR_INIT_MAKE_PRIMARY
                    return $((ret))
                fi
            fi
        fi

        sleep 3s
        common::log "partprobe on disk $disk_dev..."
        partprobe -s "$disk_dev"
        common::log "partprobe on part $part_dev..."
        partprobe -s "$part_dev"

    fi

    # 1.4.check part
    if [[ $is_lvm -eq 1 ]]; then
        if ! tool::check_lvm_exist "$vg_name" "$lv_name"; then
            common::log "make lvm not exist" "Error"
            ret=$CODE_ERROR_INIT_LVM_NOT_EXIST
            return $((ret))
        fi
    elif [[ $part_num -gt 0 ]]; then
        if ! tool::check_disk_part_exist "$disk_dev" "$part_num"; then
            common::log "make part not exist" "Error"
            ret=$CODE_ERROR_INIT_PART_NOT_EXIST
            return $((ret))
        fi
    fi

    if [ "$is_fsb" == "1" ]; then
        common::log "fsb no need to do mkfs"
        return 0
    fi

    # 2.make fs
    local base_fs_options=""
    base_fs_options=$(tool::get_mkfs_base_options "$fs_type" "$inode_num")
    common::log_var "base_fs_options" "$base_fs_options"
    local more_fs_options=""
    more_fs_options=$(tool::get_mkfs_more_options "$fs_type" "$inode_num" "$fs_options_string")
    common::log_var "more_fs_options" "$more_fs_options"

    #try to erase old fs info by wiping out first 1MB on part dev
    common::log "try wiped out old fs info for $part_dev" "Info"
    dd if=/dev/zero of=$part_dev bs=512 count=2048
    common::log "make fs type $fs_type on $part_dev, with base/more options=$base_fs_options $more_fs_options..."
    if [ "$fs_type" == "lvm2_member" ]; then
        # try pvcreate
        common::log "try pvcreate on part $part_dev..." "Info"
        # try wiped out old pv dev
        tool::try_wipe_pv_dev "$part_dev"
        echo y | pvcreate $part_dev 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "pvcreate return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            ret=$CODE_ERROR_INIT_LVS_PV_CREATE
            return $((ret))
        fi
        sleep 1s
        # try vgcreate
        common::log "try vgcreate on vg $vg_name..." "Info"
        echo y | vgcreate -s 4M $vg_name $part_dev 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "vgcreate return: $code, error_msg: $(cat $g_log_err_file)" "Error"
            ret=$CODE_ERROR_INIT_LVS_VG_CREATE
            return $((ret))
        fi
        sleep 1s
    else
        # WARN: no double queto mkfs_options
        mkfs."$fs_type" ${base_fs_options} ${more_fs_options} "$part_dev" >/dev/null 2>$g_log_err_file
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "make fs return: $code, error_msg: $(cat $g_log_err_file)" "Warn"
            # try again
            partprobe -s "$part_dev"
            sleep 5s
            common::log "try again to make fs type $fs_type on $part_dev, with base/more options=$base_fs_options $more_fs_options..."
            mkfs."$fs_type" ${base_fs_options} ${more_fs_options} "$part_dev" >/dev/null 2>$g_log_err_file
            code=$?
            if [[ $code -ne 0 ]]; then
                local error_msg=""
                error_msg=$(cat $g_log_err_file)
                common::log "make fs return: $code, error_msg: $error_msg" "Error"
                #try mkfs with only base options
                #if echo "$error_msg" | grep -q "Invalid filesystem option set"; then
                common::log "try again to make fs type $fs_type on $part_dev, with only base options=$base_fs_options..."
                mkfs."$fs_type" ${base_fs_options} "$part_dev" >/dev/null 2>$g_log_err_file
                code=$?
                #fi
                if [[ $code -ne 0 ]]; then
                    error_msg=$(cat $g_log_err_file)
                    common::log "make fs return: $code, error_msg: $error_msg" "Error"
                    ret=$CODE_ERROR_INIT_MAKE_FS
                    return $((ret))
                fi
            fi
        fi

        local mount_rootfs=""
        mount_rootfs=/mnt/edata"$disk_index"_"$part_index"
        if ! test -d "$mount_rootfs"; then
            common::log "make rootfs dir: $mount_rootfs" "Info"
            mkdir -p "$mount_rootfs"
        fi
    fi
    return 0
}

function tool::update_disk_part() {
    common::log_function "$*"
    local part_dev=$1
    local is_fsb=$2
    local more_info=$3
    if [ "$is_fsb" != "1" ]; then
        if mount 2>$g_log_err_file | grep "$part_dev"; then
            common::log "check $part_dev is mounted, umount..."
            umount "$part_dev"
        fi

        local src_perm=""
        src_perm=$(echo "$more_info" | awk -F":" '{printf $1}')
        common::log_var "src_pem" "$src_perm"
        if [ -z "$src_pem" ] || [ "$src_pem" == "-" ]; then
            src_pem="755"
        fi
        common::log "need to do chmod perm for $part_dev..."
        #src_pem means need to do chmod perm to target path which is file-level-synced
        # try mount part
        local temp_part_dir=""
        temp_part_dir=$(common::mk_temp_dir "part")
        common::log_var "temp_part_dir" "$temp_part_dir"
        if [ -n "$temp_part_dir" ]; then
            if mount 2>$g_log_err_file "$part_dev" "$temp_part_dir" 2>/dev/null; then
                chmod $src_perm $temp_part_dir && common::log "chmod $src_perm $temp_part_dir done."
                umount $temp_part_dir
            fi
            rmdir $temp_part_dir
        fi
    else
        local fs_type=""
        fs_type=$(echo "$more_info" | awk -F":" '{printf $1}')
        common::log_var_exit "fs_type" "$fs_type"
        local fsck_flag=""
        fsck_flag=$(echo "$more_info" | awk -F":" '{printf $2}')
        common::log_var "fsck_flag" "$fsck_flag"

        common::fsck_part "$part_dev" "$fs_type" "$fsck_flag"
    fi
    return 0
}

function tool::log_cr_progress() {
    local msg=$1
    local level=$2
    if [[ -z "$level" ]]; then
        level="info"
    fi
    local cur_time=""
    cur_time=$(date '+%Y-%m-%d %H:%M:%S')
    echo -e "{\"level\":\"$level\",\"msg\":\"$msg\",\"time\":\"$cur_time\"}" >> "$_cr_progress_log"
}

function tool::do_cr_image() {
    common::log_function "$*"
    local ram_role=$1
    common::log_var_exit "ram_role" "$ram_role"
    local cr_repo=$2
    common::log_var_exit "cr_repo" "$cr_repo"
    local cr_namespace=$3
    common::log_var_exit "cr_namespace" "$cr_namespace"
    local cr_tag=$4
    common::log_var_exit "cr_tag" "$cr_tag"
    local cr_domain=$5
    common::log_var_exit "cr_domain" "$cr_domain"

    local ret=0
    local code=0
    # 1. do init cr workspace disk part
    local work_disk_index=2
    local work_part_index=0
    local work_disk_num=0
    ((work_disk_num=work_disk_index+1))
    local work_part_num=0
    ((work_part_num=work_part_index+1))
    local work_disk_dev=""
    work_disk_dev=$(common::get_disk_dev "$work_disk_num")
    common::log_var_exit "work_disk_dev" "$work_disk_dev"
    local work_part_dev=""
    work_part_dev=$(common::get_disk_part_dev $work_disk_dev $work_part_num)
    common::log_var "work_part_dev" "$work_part_dev"
    if [ ! -e "$work_part_dev" ]; then
        common::log "init cr workspace disk part..." "Info"
        tool::init_disk_part "$work_disk_dev" "$work_part_dev" $work_disk_index $work_part_index $work_part_num 0 0 0 0 "ext4:0"
        ret=$?
        if [[ $ret -ne 0 ]]; then
            common::log "init cr workspace disk part" "Error"
            return $((ret))
        fi
    fi

    # 2. mount workspace dir
    local work_dir=/smc/app2container
    mkdir -p "$work_dir"
    if ! mount 2>$g_log_err_file | grep -w "$work_part_dev" | grep -w "$work_dir"; then
        common::log "mount workspace dir $work_dir to dev $work_part_dev..." "Info"
        if ! mount "$work_part_dev" "$work_dir" 2>$g_log_err_file; then
            common::log "mount workspace dir" "Error"
            return $((CODE_ERROR_MOUNT_CR_WORK))
        fi
    fi

    local is_centos6=0
    common::is_centos6 && is_centos6=1

    local work_docker_dir="$work_dir"/docker
    local var_docker_dir="/var/lib/docker"
    local work_tmp_dir="$work_dir"/tmp
    local var_tmp_dir="/var/tmp"
    local src_dir="/smc/cr_src"
    local var_docker_dir_mounted=0
    local var_tmp_dir_mounted=0
    local src_dir_mounted=0
    local docker_started=0
    while [ $ret -eq 0 ]
    do
        # 3. mount workspace docker dir and restart docker
        mkdir -p "$work_docker_dir"
        mkdir -p "$var_docker_dir"
        if ! mount 2>$g_log_err_file | grep -w "$var_docker_dir" | grep -w "$work_docker_dir"; then
            common::log "mount bind workspace docker dir $work_docker_dir to $var_docker_dir..." "Info"
            if ! mount --bind "$work_docker_dir" "$var_docker_dir" 2>$g_log_err_file; then
                common::log "mount bind workspace docker dir" "Error"
                ret=$((CODE_ERROR_MOUNT_CR_DOCKER))
                break
            fi
        fi
        var_docker_dir_mounted=1

        mkdir -p "$work_tmp_dir"
        mkdir -p "$var_tmp_dir"
        if ! mount 2>$g_log_err_file | grep -w "$var_tmp_dir" | grep -w "$work_tmp_dir"; then
            common::log "mount bind workspace tmp dir $work_tmp_dir to $var_tmp_dir..." "Info"
            if ! mount --bind "$work_tmp_dir" "$var_tmp_dir" 2>$g_log_err_file; then
                common::log "mount bind workspace tmp dir" "Error"
                ret=$((CODE_ERROR_MOUNT_CR_DOCKER))
                break
            fi
        fi
        export TMPDIR=$work_tmp_dir
        var_tmp_dir_mounted=1

        # 4. mount source data dir
        mkdir -p "$src_dir"
        local src_disk_num=2
        local src_part_num=1
        local src_disk_dev=""
        src_disk_dev=$(common::get_disk_dev "$src_disk_num")
        common::log_var_exit "src_disk_dev" "$src_disk_dev"
        local src_part_dev=""
        src_part_dev=$(common::get_disk_part_dev $src_disk_dev $src_part_num)
        common::log_var "src_part_dev" "$src_part_dev"
        if [ ! -e "$src_part_dev" ]; then
            common::log "cr src disk part not exist" "Error"
            ret=$((CODE_ERROR_MOUNT_CR_WORK))
            break
        fi
        if ! mount 2>$g_log_err_file | grep -w "$src_part_dev" | grep -w "$src_dir"; then
            common::log "mount src dir $src_dir to dev $src_part_dev..." "Info"
            if ! mount "$src_part_dev" "$src_dir" 2>$g_log_err_file; then
                common::log "mount src dir" "Error"
                ret=$((CODE_ERROR_MOUNT_CR_SRC))
                break
            fi
        fi
        src_dir_mounted=1

        local a2c_single_app_mode=0
        local a2c_app_id=""
        local a2c_p2vs_app_id_path="$src_dir/A2C_APP_ID"
        local cr_progress_stage=""
        if test -e "$a2c_p2vs_app_id_path"; then
            cr_progress_stage="single app containerize prepare"
            tool::log_cr_progress "Prepare to do single app containerize"
            a2c_app_id=$(cat $a2c_p2vs_app_id_path)
            if [[ -z "$a2c_app_id" ]]; then
                common::log "a2c migration prepare error: failed to get app id" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: get app id" "error"
                ret=$((CODE_ERROR_CR_PREPARE))
                break
            fi

            a2c_single_app_mode=1
            common::log "a2c migration: copy docker conf..."
            local a2c_p2vs_dest_docker_conf="/etc/docker/daemon.json"
            if ! test -f "$a2c_p2vs_dest_docker_conf"; then
                mkdir -p "/etc/docker"
                local a2c_p2vs_src_docker_conf="$src_dir/docker-daemon.json"
                common::log "a2c migration: copy docker conf..."
                \cp -f "$a2c_p2vs_src_docker_conf" "$a2c_p2vs_dest_docker_conf" || return 1
            fi
        fi
        common::log "start docker service..." "Info"
        if [[ $is_centos6 -ne 1 ]]; then
            systemctl restart docker && docker_started=1
        else
            service docker restart && docker_started=1
        fi

        if [[ $a2c_single_app_mode -eq 1 ]]; then
            # do new a2c
            # install app2container
            if ! which $_a2c_pkg_name >>"$g_log_file" 2>&1; then
                local a2c_p2vs_zip_path="$src_dir/AWSApp2Container-installer-linux.tar.gz"
                common::log "a2c migration prepare: unzip $_a2c_pkg_name..."
                local a2c_p2vs_unzip_dir="$src_dir"
                tar xf "$a2c_p2vs_zip_path" -C "$a2c_p2vs_unzip_dir" >>"$g_log_file" 2>$g_log_err_file
                common::log "a2c migration prepare: install $_a2c_pkg_name..."
                echo y | bash "$a2c_p2vs_unzip_dir/install.sh" >>"$g_log_file" 2>$g_log_err_file
                local code=$?
                if ! which $_a2c_pkg_name 2>$g_log_err_file; then
                    common::log "a2c migration prepare error: failed to install: $code" "Error"
                    tool::log_cr_progress "$cr_progress_stage failed: install $_a2c_pkg_name" "error"
                    ret=$((CODE_ERROR_CR_PREPARE))
                    break
                fi
            fi

            # app2container init
            # FIXME: In nodejs module, env vars such as HOME is empty,
            # it will cause app2conrainer cannot find app2container.conf
            # so we need to export HOME directly
            export HOME=/root
            local a2c_p2vs_init_conf_dir="/root/.app2container-config"
            local a2c_p2vs_init_conf_path="$a2c_p2vs_init_conf_dir/app2container.conf"
            if ! test -f "$a2c_p2vs_init_conf_path"; then
                mkdir -p "$a2c_p2vs_init_conf_dir"
                common::log "a2c migration prepare: $_a2c_pkg_name init..."
                local a2c_src_init_conf_path="$src_dir/app2container.conf"
                mkdir -p "$a2c_p2vs_init_conf_dir"
                \cp -f "$a2c_src_init_conf_path" "$a2c_p2vs_init_conf_path"
            fi

            # check app extraction
            local a2c_app_extraction_path="$src_dir/$a2c_app_id-extraction.tar"
            if ! test -f "$a2c_app_extraction_path"; then
                common::log "a2c migration prepare error: app extraction file not exist" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: app extraction file not exist" "error"
                ret=$((CODE_ERROR_CR_PREPARE))
                break
            fi
            tool::log_cr_progress "Step1: single app containerize prepare done"

            # app2container containerize
            cr_progress_stage="single app containerize"
            echo "HOME=$HOME" && $_a2c_pkg_name containerize --no-validate --input-archive "$a2c_app_extraction_path" >>"$g_log_file" 2>"$g_log_err_file"
            local a2c_log="$work_dir/log/app2container.log"
            test -f "$a2c_log" && \cp "$a2c_log" "$g_log_dir/app2container.log"
            code=$?
            if [[ $code -ne 0 ]] ; then
                common::log "a2c containerize return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: a2c containerize" "error"
                ret=$((CODE_ERROR_CR_BUILD))
                break
            fi
            local local_image_id=""
            local_image_id=$(docker image list 2>/dev/null | grep "$a2c_app_id" | tail -1 | awk '{printf $3}')
            if [[ -z "$local_image_id" ]]; then
                common::log "a2c get local image failed" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: a2c get local image" "error"
                ret=$((CODE_ERROR_CR_BUILD))
                break
            fi
            tool::log_cr_progress "Step2: single app containerize done"

            # docker login/push to acr
            # copy docker files to preparing docker login/push
            local a2c_p2vs_dest_pip_conf="/root/.pip/pip.conf"
            if ! test -f "$a2c_p2vs_dest_pip_conf"; then
                mkdir -p "/root/.pip"
                local a2c_p2vs_src_pip_conf="$src_dir/pip.conf"
                common::log "a2c migration: copy pip conf..."
                \cp -f "$a2c_p2vs_src_pip_conf" "$a2c_p2vs_dest_pip_conf" || return 1
            fi

            common::log "a2c migration: pip install aliyun-python-sdk-core..."
            pip3 install aliyun-python-sdk-core >>"$g_log_file" 2>"$g_log_err_file"
            code=$?
            if [[ $code -ne 0 ]] ; then
                common::log "a2c install aliyun-python-sdk-core return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: a2c install aliyun-python-sdk-core" "error"
                ret=$((CODE_ERROR_CR_PREPARE))
                break
            fi

            common::log "a2c migration: get acr temp login..."
            local a2c_p2vs_py_file="$src_dir/acr_temp_login.py"
            if ! test -f "$a2c_p2vs_py_file"; then
                common::log "get acr temp login failed: tool not exist" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: get acr temp login failed: tool not exist" "error"
                ret=$((CODE_ERROR_CR_DOCKER_LOGIN))
                break
            fi
            local temp_infos=""
            temp_infos=$(python3 "$a2c_p2vs_py_file" "$ram_role")
            echo "$temp_infos"
            if [[ $temp_infos == ERROR* ]]; then
                common::log "get acr temp login failed: $temp_infos" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: get acr temp login failed: $temp_infos" "error"
                if echo "$temp_infos" | grep -q "AUTHENTICATION_FAILED"; then
                    ret=$((CODE_ERROR_CR_RAM))
                else
                    ret=$((CODE_ERROR_CR_DOCKER_LOGIN))
                fi
                break
            fi

            common::log "a2c migration: docker login..."
            local temp_user=""
            temp_user=$(echo "$temp_infos" | awk -F',' '{printf $1}')
            local temp_pwd=""
            temp_pwd=$(echo "$temp_infos" | awk -F',' '{printf $2}')
            echo "$temp_pwd" | docker login --username="$temp_user" --password-stdin "$cr_domain" >>"$g_log_file" 2>"$g_log_err_file"
            code=$?
            if [[ $code -ne 0 ]] ; then
                common::log "a2c docker login return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: docker login" "error"
                ret=$((CODE_ERROR_CR_DOCKER_LOGIN))
                break
            fi
            tool::log_cr_progress "Step3: Docker user login done"

            common::log "a2c migration: docker tag..."
            local remote_image_tag="$cr_domain/$cr_namespace/$cr_repo:$cr_tag"
            docker tag "$local_image_id" "$remote_image_tag" >>"$g_log_file" 2>"$g_log_err_file"
            code=$?
            if [[ $code -ne 0 ]] ; then
                common::log "a2c docker tag return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: docker tag" "error"
                ret=$((CODE_ERROR_CR_DOCKER_TAG))
                break
            fi

            tool::log_cr_progress "Step4: Docker image push begin"
            common::log "a2c migration: docker push..."
            docker push "$remote_image_tag" >>"$g_log_file" 2>"$g_log_err_file"
            code=$?
            if [[ $code -ne 0 ]] ; then
                common::log "a2c docker push return: $code, error_msg: $(cat $g_log_err_file)" "Error"
                tool::log_cr_progress "$cr_progress_stage failed: docker push" "error"
                ret=$((CODE_ERROR_CR_DOCKER_PUSH))
                break
            fi

            common::log "a2c migration: docker logout..."
            docker logout "$cr_domain" >>"$g_log_file" 2>"$g_log_err_file"
            tool::log_cr_progress "Step5: Docker image [$local_image_id] push done"
        else
            # do legacy vm to acr
            cr_progress_stage="whole vm containerize"
            local cr_exec="$g_cur_dir"/bin/ackv2c
            local cr_config_file="$g_cur_dir"/bin/topology.json
            chmod +x "$cr_exec"
            echo "" > "$_cr_progress_log"
            common::log "do v2c $src_dir on work dir $work_dir with $cr_config_file..." "Info"
            "$cr_exec" image create --configfile "$cr_config_file" --workspace "$work_dir" \
                --sourcedir "$src_dir" --ramrole "$ram_role" --logfile "$_cr_progress_log"
            code=$?
            local error_msg=""
            error_msg=$(grep "level" "$_cr_progress_log" | tail -1 | grep "level\":\"error")
            if [ -n "$error_msg" ]; then
                tool::log_cr_progress "$cr_progress_stage failed" "error"
                if grep -q "AUTHENTICATION_FAILED" "$_cr_progress_log"; then
                    tool::log_cr_progress "$cr_progress_stage failed: please check ram role" "error"
                    ret=$((CODE_ERROR_CR_RAM))
                else
                    ret=$((CODE_ERROR_CR_EXEC))
                fi
                break
            fi
        fi
        break
    done

    # stop docke and umount dirs
    if [[ $docker_started -eq 1 ]]; then
        common::log "stop docker service..." "Info"
        if [[ $is_centos6 -ne 1 ]]; then
            systemctl stop docker
        else
            service docker stop
        fi
    fi
    [[ $var_docker_dir_mounted -eq 1 ]] && umount "$var_docker_dir"
    [[ $var_tmp_dir_mounted -eq 1 ]] && umount "$var_tmp_dir"
    umount "$work_dir"
    [[ $src_dir_mounted -eq 1 ]] && umount "$src_dir"

    return $((ret))
}

function tool::init_lvs() {
    common::log_function "$*"
    local dev=$1
    common::log_var_exit "dev" "$dev"
    local pv_uuid=$2
    common::log_var_exit "pv_uuid" "$pv_uuid"
    local vg_name=$3
    common::log_var_exit "vg_name" "$vg_name"
    local cfg_path=$4
    common::log_var_exit "cfg_path" "$cfg_path"

    local code=0

    # try pvcreate
    tool::try_wipe_pv_dev "$dev"
    common::log "try pvcreate $pv_uuid on dev $dev..." "Info"
    echo y | pvcreate --uuid $pv_uuid --norestorefile $dev 2>$g_log_err_file
    code=$?
    if [[ $code -ne 0 ]]; then
        common::log "init lvs pvcreate return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        ret=$CODE_ERROR_INIT_LVS_PV_CREATE
        return $((ret))
    fi
    sleep 1s
    # try vgcfgrestore
    common::log "try vgcfgrestore $cfg_path on vg $vg_name..." "Info"
    echo y | vgcfgrestore --force -f $cfg_path $vg_name 2>$g_log_err_file
    code=$?
    if [[ $code -ne 0 ]]; then
        common::log "init lvs vgcfgrestore return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        ret=$CODE_ERROR_INIT_LVS_VG_RESTORE
        return $((ret))
    fi
    sleep 1s

    # try vgchange to active
    common::log "try vgchange vg $vg_name..." "Info"
    echo y | vgchange -ay $vg_name 2>$g_log_err_file
    code=$?
    if [[ $code -ne 0 ]]; then
        common::log "init lvs vgchange return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        ret=$CODE_ERROR_INIT_LVS_VG_CHANGE
        return $((ret))
    fi
    sleep 1s

    return 0
}

function tool::exec_sub_action() {
    common::log_function "$*"
    local sub_action=$1
    common::log_var_exit "sub_action" "$sub_action"

    local code=0
    if [ "$sub_action" == "upload_logs" ]; then
        local upload_url=$2
        common::log_var_exit "upload_url" "$upload_url"
        local logs_zip_name=$3
        common::log_var_exit "logs_zip_name" "$logs_zip_name"
        local logs_zip_path=$g_cur_dir/Logs/$logs_zip_name
        common::log "upload_logs cmd: curl --connect-timeout 5 -T \"$logs_zip_path\" \"$upload_url\""
        curl --connect-timeout 5 -T "$logs_zip_path" "$upload_url" >>"$g_log_file" 2>"$g_log_err_file"
        code=$?
        if [[ $code -ne 0 ]]; then
            common::log "upload_logs return: $code, error_msg: $(cat $g_log_err_file)" "Error"
        fi
    else
        echo "Invalid sub action: [$sub_action]"
        code=1
    fi
    return $((code))
}

function tool::main_entry() {
    common::log_function "$*"
    local action=$1
    common::log_var "action" "$action"

    if [ "$action" == "--version" ]; then
        global::print_version
        return 0
    elif [ "$action" == "--help" ]; then
        tool::print_usage
        return 0
    fi

    #clear last server config error log
    if [ -n "$g_log_err_file" ] && test -f "$g_log_err_file"; then
        rm -f "$g_log_err_file"
    fi

    local ret=0
    common::log "====== $g_util_desc $g_util_version =====" "Info"
    common::log "P2V Cloud Tool $action Begin..." "Info"

    if [ "$action" == "--exec" ]; then
        # exec sub action
        tool::exec_sub_action "$2" "$3" "$4" "$5" "$6" "$7"
        ret=$?
    elif [ "$action" == "--docr" ]; then
        # try refresh devs
        common::log "try refresh devs..." "Info"
        fdisk -l
        sleep 1s

        local ram_role=$2
        common::log_var_exit "ram_role" "$ram_role"
        _cr_progress_log=$3
        echo -e "" > "$_cr_progress_log"
        common::log_var_exit "cr_log_file" "$_cr_progress_log"
        
        # do cr image
        tool::do_cr_image "$ram_role" $4 $5 $6 $7
        ret=$?
        if [[ $ret -ne 0 ]]; then
            common::log "do cr image" "Error"
        fi
    elif [ "$action" == "--initlvs" ]; then
        # try refresh devs
        common::log "try refresh devs..." "Info"
        fdisk -l
        sleep 1s

        local dev=$2
        common::log_var "dev" "$dev"
        local pv_uuid=$3
        common::log_var "pv_uuid" "$pv_uuid"
        local vg_name=$4
        common::log_var "vg_name" "$vg_name"
        local cfg_path=$5
        common::log_var "cfg_path" "$cfg_path"

        # do init lvs
        tool::init_lvs "$dev" "$pv_uuid" "$vg_name" "$cfg_path"
        ret=$?
    else
        # try refresh devs
        common::log "try refresh devs..." "Info"
        fdisk -l
        sleep 1s

        # check params
        local disk_index=$2
        common::log_var "disk_index" "$disk_index"
        local disk_num=0
        ((disk_num=disk_index+2))
        common::log_var_exit "disk_num" "$disk_num"

        local disk_dev=""
        disk_dev=$(common::get_disk_dev "$disk_num")
        common::log_var_exit "disk_dev" "$disk_dev"

        local sys_disk_dev=""
        sys_disk_dev=$(fdisk -l 2>/dev/null | grep -oE "Disk /dev/(nvme[0-9]*n[0-9]*|[a-z]*)" | grep -v mapper | sort | head -1 | tail -1 | awk '{printf $2}')
        common::log_var_exit "sys_disk_dev" "$sys_disk_dev"
        if [[ "$disk_dev" == "$sys_disk_dev" ]]; then
            common::log "!![disk device] can't be sys device!!" "Error"
            exit $((CODE_ERROR_COMMON_INVALID_PARAM))
        fi

        local part_index=$3
        common::log_var_exit "part_index" "$part_index"
        g_src_part_num=$4
        common::log_var_exit "g_src_part_num" "$g_src_part_num"
        if [ "$g_src_part_num" == "-" ]; then
            g_src_part_num=""
        fi

        local is_gpt=0
        local is_boot=0
        if [ "$action" == "--init" ]; then
            is_gpt=$8
            is_boot=$9
            common::log_var_exit "is_gpt" "$is_gpt"
            common::log_var_exit "is_boot" "$is_boot"

            # try fix gpt bound
            if [[ $part_index -eq 0 ]] && [[ $is_gpt -eq 1 ]]; then
                # try fix disk gpt boundary, for gpt backup data might be wrong
                common::log "try to fix the GPT to use all of the space: $disk_dev" "Info"
                gdisk "$disk_dev" << EOF
x
e
w
y
EOF
                code=$?
                if [ $? -eq 0 ]; then
                    common::log "fix $disk_dev gpt table to use all of the space successfully!" "Info"
                else
                    common::log "fix $disk_dev gpt table to use all of the space failed: $code" "Error"
                    ret=$CODE_ERROR_INIT_FIX_GPT_TABLE
                    return $((ret))
                fi
            fi
        else
            if parted "$disk_dev" -s print 2>$g_log_err_file | grep -w "gpt"; then
                is_gpt=1
            fi
        fi

        # get part index offset, such as gpt system parts(boot_grub/efi part)
        if [ -z "$g_src_part_num" ] && [[ $is_gpt -eq 1 ]]; then
            g_part_off=$(common::get_part_off $disk_dev)
            common::log_var_exit "g_part_off" "$g_part_off"
            if [[ $g_part_off -eq 0 ]]; then
                g_part_off=""
            else
                ((part_index+=g_part_off))
            fi
        fi

        local part_num=0
        if [ -n "$g_src_part_num" ]; then
            part_num=$g_src_part_num
        else
            # if part index >= 3, mbr need a extend part to place logical parts,
            # but gpt not.
            if [[ $part_index -ge 3 ]] && [[ $is_gpt -eq 0 ]]; then
                ((part_num=part_index+2))
            else
                ((part_num=part_index+1))
            fi
        fi

        local lv_dev=$5
        common::log_var_exit "lv_dev" "$lv_dev"
        if [ "$lv_dev" == "-" ]; then
            lv_dev=""
        fi

        local part_dev=""
        if [ -n "$g_src_part_num" ]; then
            if [[ $g_src_part_num -gt 0 ]]; then
                part_dev=$(common::get_disk_part_dev $disk_dev $g_src_part_num)
            else
                part_dev=${disk_dev}
            fi
        else
            part_dev=$(common::get_disk_part_dev $disk_dev $part_num)
        fi
        if [ -n "$lv_dev" ]; then
            part_dev=$lv_dev
        fi
        common::log_var_exit "part_dev" "$part_dev"

        if [ "$action" == "--prersync" ]; then
            # prepare rsync disk part
            local origin_disk_index=$6
            tool::prepare_rsync_disk_part "$part_dev" "$disk_index" "$part_index" "$origin_disk_index"
            ret=$?
        elif [ "$action" == "--init" ]; then
            # do init disk part
            local is_fsb=$6
            common::log_var_exit "is_fsb" "$is_fsb"
            local size_sectors=$7
            common::log_var_exit "size_sectors" "$size_sectors"
            local is_gpt=$8
            common::log_var_exit "is_gpt" "$is_gpt"
            local is_boot=$9
            common::log_var_exit "is_boot" "$is_boot"
            local src_type=${10}
            common::log_var_exit "src_type" "$src_type"
            if [ "$src_type" == "$SRC_TYPE_WIN" ]; then
                g_is_win=1
                common::log_var "g_is_win" "$g_is_win"
            fi
            local more_info="${11}"
            common::log_var "more_info" "$more_info"
            tool::init_disk_part "$disk_dev" "$part_dev" "$disk_index" "$part_index" "$part_num" "$is_fsb" "$size_sectors" "$is_gpt" "$is_boot" "$more_info"
            ret=$?
        elif [ "$action" == "--update" ]; then
            # do update disk part
            local is_fsb=$6
            common::log_var_exit "is_fsb" "$is_fsb"
            local more_info=$7
            common::log_var "more_info" "$more_info"
            tool::update_disk_part "$part_dev" "$is_fsb" "$more_info"
            ret=$?
        else 
            echo "Invalid option: [$action]"
            tool::print_usage
            exit 1
        fi
    fi

    if [ $ret == 0 ]; then
        common::log "P2V Cloud Tool $action Finished!" "Done"
    else
        common::log "P2V Cloud Tool $action Not Finished!" "Error"
    fi
    exit $((ret))
}

# all exec output to log
exec >>"$g_log_file" 2>&1

tool::main_entry $*
