#!/bin/bash

INSTALL_LOG='/tmp/hardware_check.log'

_color_red=$(TERM=xterm-256color tput setaf 1)
_color_normal=$(TERM=xterm-256color tput sgr0)
_color_green=$(TERM=xterm-256color tput setaf 2)
_color_yellow=$(TERM=xterm-256color tput setaf 3)

function log_info() {
    local ts=$(date -Iseconds)
    # to log file
    echo -e "$ts [INFO] $@" >>$INSTALL_LOG
    # to console (stderr)
    echo >&2 -e "$ts [INFO] $@"
    return 0
}

function log_error() {
    local ts=$(date -Iseconds)
    # to log file
    echo -e "$ts [ERROR] $@" >>$INSTALL_LOG
    # to console (stderr)
    echo >&2 -e "${_color_red}$ts [ERROR] $@${_color_normal}"
    return 0
}

function log_success() {
    local ts=$(date -Iseconds)
    # to log file
    echo -e "$ts [SUCCESS] $@" >>$INSTALL_LOG
    # to console (stderr)
    echo >&2 -e "${_color_green}$ts [SUCCESS] $@${_color_normal}"
    return 0
}

function string_check() {
    if [ -z "$2" ]; then
        log_error "check failed, please config $1 values"
        print_usage
        exit 1
    fi
}

function install_fio() {
    if [ $(command -v fio &>/dev/null || echo "notinstall")x == "notinstall"x ]; then
        log_info "没有安装依赖fio，尝试进行下载.."
        wget http://download.edoc2.com:5999/requirements/fio-3.2-1.el7.x86_64.rpm
        if [ $? -ne 0 ]; then
            log_error "不能进行fio下载, 请自行下载安装，url: http://download.edoc2.com:5999/requirements/fio-3.2-1.el7.x86_64.rpm"
            exit
        fi
        log_info "install package fio."
        rpm -Uvh fio-3.2-1.el7.x86_64.rpm
    fi
}

function print_usage() {
    echo
    cat <<EOF
Usage:
    bash hardware_perfcheck.sh --help
    bash hardware_perfcheck.sh -e test -d -m
    bash hardware_perfcheck.sh -e test -d /home/docker -m /home/middleware
    bash hardware_perfcheck.sh -e test -d -n /home/nas

Options:
    -h, --help                   show usage guide
    -e, --env                    测试的安装环境，test|prod，必须参数
    -d, --docker-root            需要检测的docker安装目录, 默认路径 /var/lib/docker， 必须选项
    -m, --middleware-root        需要检测的中间件的安装目录的iops, 默认路径 /home/edoc2/macrowing/edoc2v5/data
    -n, --nas-perm-test          使用nas作为中间件存储时，指定测试nas挂载目录，进行变更权限测试
EOF
}

function cpu_check() {
    local req_cpus=$REQ_CPUS
    local cpus=$(lscpu | awk '/^CPU\(s):/{print $2}')
    if [[ -n ${req_cpus} && -n ${cpus} ]]; then
        if [[ $(awk 'BEGIN{print('"$cpus"'<'"$req_cpus"')?0:1}') -eq 0 ]]; then
            log_error "运行环境：$RUNENV, CPU 核心数要求：$req_cpus core, 实际CPU核心数：$cpus core, 不符合运行要求"
            exit 10
        else
            log_info "运行环境：$RUNENV, CPU 核心数要求：$req_cpus core, 实际CPU核心数：$cpus core"
        fi
    else
        log_error "获取宿主机cpu核心数失败"
    fi
}

function mem_check() {
    local mem_total=$(cat /proc/meminfo | awk '/MemTotal:/{print $2$3}')
    local mem_gb=$(unit_convert $mem_total)
    local req_mem=${REQ_MEM:0:-1}
    if [[ -n ${req_mem} && -n ${mem_gb} ]]; then
        if [[ $(awk 'BEGIN{print('"$mem_gb"'<'"$req_mem"')?0:1}') -eq 0 && $(awk 'BEGIN{print('"$mem_gb"'-'"$req_mem"'<=-1)?0:1}') -eq 0 ]]; then
            log_error "运行环境：$RUNENV, 内存大小要求：${req_mem}gb, 实际内存大小：${mem_gb}gb, 不符合运行要求"
            exit 11
        else
            log_info "运行环境：$RUNENV, 内存大小要求：${req_mem}gb, 实际内存大小：${mem_gb}gb"
        fi
    else
        log_error "获取宿主机内存失败"
    fi
}

function disk_iops_test() {
    local disk=$1
    local iops=$(fio -filename=${disk}/fio.txt -direct=1 -iodepth 6 -thread -rw=write -ioengine=psync -bs=4k -size=5G -numjobs=8 -runtime=30 -group_reporting -name=mytest | grep IOPS | awk -F'IOPS=|,' '{print $2}' && rm -f ${disk}/fio.txt)
    local iops=$(iops_unit_convert $iops)
    local req_mid_disk_iops=${REQ_MID_DISK_IOPS:0:-1}
    if [[ -n ${req_mid_disk_iops} && -n ${iops} ]]; then
        if [[ $(awk 'BEGIN{print('"$iops"'<'"$req_mid_disk_iops"')?0:1}') -eq 0 ]]; then
            log_error "运行环境：$RUNENV, 磁盘iops最低要求：${req_mid_disk_iops}k, 实际测试磁盘iops：${iops}k, 不符合运行要求"
            exit 12
        else
            log_info "运行环境：$RUNENV, 磁盘iops最低要求：${req_mid_disk_iops}k, 实际测试磁盘iops：${iops}k"
        fi
    else
        log_error "测试主机磁盘 $disk iops 失败"
    fi
}

function iops_unit_convert() {
    typeset -l size
    size=$1
    to_k=$(echo $size | awk '{
            if($1 ~ /k$/){
                sub(/k$/,"");
                print $1
            } else {
                print $1/1000
            }
        }')
    echo $to_k
}

function disk_partation_size_check() {
    local check_partation=$1
    local req_size=$2
    local mounted=
    local mounted_avail=

    for (( ; ; )); do
        mounted=$(mounted_match $check_partation)
        if [ $? -ne 0 ]; then
            check_partation=${check_partation%/*}
            if [ "$check_partation"x == "x" ]; then
                check_partation="/"
            fi
            continue
        fi
        break
    done
    log_info "检测到目录 $1, 使用的挂载分区为: $mounted"

    mounted="${mounted//\//\\/}"
    mounted_avail=$(df -h | awk '$NF ~ /^'"$mounted"'$/{print $(NF-2)}')
    mounted_avail=$(unit_convert $mounted_avail)
    req_size=${req_size:0:-1}
    mounted_avail=${mounted_avail:0:-1}
    if [[ -n ${req_size} && -n ${mounted_avail} ]]; then
        if [[ $(awk 'BEGIN{print('"$mounted_avail"'<'"$req_size"')?0:1}') -eq 0 ]]; then
            log_error "运行环境：$RUNENV, 查看目录空间：$check_partation, 磁盘空间要求：${req_size}g, 实际可用空间：${mounted_avail}g, 不符合运行要求"
            exit 13
        else
            log_info "运行环境：$RUNENV, 查看目录空间：$check_partation, 磁盘空间要求：${req_size}g, 实际可用空间：${mounted_avail}g"
        fi
    else
        log_error "获取主机磁盘空间失败"
    fi
}

function mounted_match() {
    local check_dir=
    local match_dir=

    if [ "${1}"x != "/x" ]; then
        check_dir=$(echo $1 | sed 's/\/$//')
    else
        check_dir="/"
    fi
    check_dir="${check_dir//\//\\/}"
    match_dir=$(df -h | awk '$NF ~ /^\//{print $NF}' | awk '$1 ~ /^'"$check_dir"'$/')
    if [ -z $match_dir ]; then
        return 1
    fi
    echo $match_dir
    return 0
}

function unit_convert() {
    typeset -l size
    size=$1
    to_gb=$(echo $size | awk '{
            if ($1 ~ /pb$/ || $1 ~ /p$/){
                print $1 * 1024 * 1024
            } else if ($1 ~ /tb$/ || $1 ~ /t$/){
                print $1 * 1024
            } else if ($1 ~ /gb$/ || $1 ~ /g$/){
                print $1
            } else if ($1 ~ /mb$/ || $1 ~ /m$/){
                print $1/1024
            } else if ($1 ~ /kb$/ || $1 ~ /k$/){
                print $1/1024/1024
            } else if ($1 ~ /b$/){
                print $1/1024/1024/1024
            }
        }')
    echo $to_gb
}

function hardware_perfcheck() {
    ## 设置内存和磁盘的单位注意是g(或G)，不是gb(GB)，不要转换为m之类的，这地方没做校验
    if [ "${RUNENV}"x == "test"x ]; then
        REQ_CPUS=8
        REQ_MEM=16g
        REQ_MID_DISK_IOPS=1k
    elif [ "${RUNENV}"x == "prod"x ]; then
        REQ_CPUS=16
        REQ_MEM=32g
        REQ_MID_DISK_IOPS=6k
    else
        log_error "RUNENV 只能设置test 或 prod"
        print_usage
        exit
    fi
    REQ_SYS_ROOT=25g
    REQ_DOCKER_ROOT=80g

    string_check RUNENV $RUNENV
    string_check DOCKER_DATA_ROOT $DOCKER_DATA_ROOT

    log_info "start hardware perf check..."
    cpu_check
    mem_check
    log_info "检测宿主机根目录/ 存储空间大小..."
    disk_partation_size_check "/" $REQ_SYS_ROOT
    log_info "检测docker root目录$DOCKER_DATA_ROOT存储空间大小..."
    disk_partation_size_check $DOCKER_DATA_ROOT $REQ_DOCKER_ROOT

    if [ ! -z $MIDDLEWARE_DATA_PATH ]; then
        install_fio
        [ -d "$MIDDLEWARE_DATA_PATH" ] || mkdir -p $MIDDLEWARE_DATA_PATH
        log_info "测试中间件存储路径：$MIDDLEWARE_DATA_PATH 的iops"
        disk_iops_test $MIDDLEWARE_DATA_PATH
    fi

    if [ ! -z $NAS_DIR ]; then
        log_info "测试NAS是否满足安装权限"
        [ -d "$NAS_DIR" ] && touch $NAS_DIR/nas_perm.test
        if [ $? -ne 0 ]; then
            log_error "不能在$NAS_DIR创建测试文件，请确认是否挂载nas或目录是否存在"
            exit
        fi
        chown 999.999 $NAS_DIR/nas_perm.test
        if [ $(ls -nd $NAS_DIR/nas_perm.test | awk '$3==999&&$4==999{print "ok"}')x == "okx" ]; then
            log_info "nas 存储权限测试成功，满足中间件安装需求"
            rm -f $NAS_DIR/nas_perm.test
        else
            log_error "nas 存储权限测试失败，无法进行文件权限更改，不满足中间件安装"
            rm -f $NAS_DIR/nas_perm.test
            exit
        fi
    fi

    log_success "测试成功，满足安装要求"
}

if [ "$1"x == x ]; then
    log_error "没有获取参数"
    print_usage
    exit
fi

while true; do
    case "$1" in
    -h | --help)
        case "$2" in
        "" | -*)
            print_usage
            exit
            ;;
        esac
        ;;
    -e | --env)
        case "$2" in
        "" | -*)
            log_error "-e|--env must config a value"
            print_usage
            exit
            ;;
        esac
        RUNENV=$2
        shift 2
        ;;
    -d | --docker-root)
        case "$2" in
        "" | -*)
            DOCKER_DATA_ROOT=/var/lib/docker
            shift 1
            ;;
        *)
            DOCKER_DATA_ROOT=$2
            shift 2
            ;;
        esac
        ;;
    -m | --middleware-root)
        case "$2" in
        "" | -*)
            MIDDLEWARE_DATA_PATH=/home/edoc2/macrowing/edoc2v5/data
            shift 1
            ;;
        *)
            MIDDLEWARE_DATA_PATH=$2
            shift 2
            ;;
        esac
        if [[ $MIDDLEWARE_DATA_PATH =~ ^/dev || $MIDDLEWARE_DATA_PATH =~ ^/boot ]];then
            log_error "不能设置-m 中间件存储路径在 /dev或/boot下"
            exit
        fi
        ;;
    -n | --nas-perm-test)
        case "$2" in
        "" | -*)
            log_error "没有指定 nas测试目录"
            print_usage
            exit
            ;;
        *)
            NAS_DIR=$2
            shift 2
            ;;
        esac
        ;;
    *)
        break
        ;;
    esac
done

hardware_perfcheck
