#!/bin/env bash
##Description: stop primary replicate  

function f_syntax
{
    [ ! -z "$1" ] && echo ">>[$(date "+%Y%m%d.%H%M%S")] $1"
    echo " "
    echo "bash ${ShellName}  [-primaryhostname <hostname>]"
    echo "-standbyhostname <hostname_standby> "
    echo " "
    echo " #sample:"
    echo " #sh ${ShellName} -hostname node1 -standbyhostname sa-172-16-4-81" 
    echo " "
    exit 2
}

function f_PrintLog
{
    _LogFlag=$(echo "$1"|tr "a-z" "A-Z")
    _LogMessage=$2
    [ $(echo "$1"|wc -L) -eq 4 ] && _LogFlag=$(echo "${_LogFlag}_")
    echo "$(date "+%Y%m%d.%H%M%S"):[${_LogFlag}]:${UserName}@${HostName}:${2}";
}

###############################################################################

function f_checkconnection
{
    _ip=$1

    f_PrintLog "Debug" "Start to run f_checkconnection."
    # f_PrintLog "Info" "Check process privileges, current user:${tidbuser}!"
    # mysql -u${user} -p${passwd} -h${ip} -P${port} -vvv -e "show grants for ${tidbuser}"
    ignoreusers=${adminusers}
    f_PrintLog "Info" "Start to check applications, ignoreusers:${ignoreusers}!!"
    mysql -u${user} -p${tidbPasswd:-123} -h${_ip} -P${tidbPort} -vvv -e "select user,count(1) from information_schema.cluster_processlist group by user"
    if [ $? -ne 0 ]
    then
        f_PrintLog "Error" "failed to run processlist query, pls check!!!"
        exit 1
    fi
    mysql -u${user} -p${tidbPasswd:-123} -h${_ip} -P${tidbPort} -NBe "select user,count(1) from information_schema.cluster_processlist group by user;" | while read username appcount
    do
        if [ -z "$(echo ${ignoreusers} | grep -w ${username})" ]
        then
            f_PrintLog "Error" "find connections in database, user:${username} appcount:${appcount}, pls stop or kill these connections!!!"
            echo "${username} ${appcount}" >> ${tmp_file}
        else
            f_PrintLog "Info" "find connections in database, user:${username} appcount:${appcount}, ignore these connections!"
        fi
    done
}

function f_runsqlfile
{
    _sqlfile=$1
    _ip=$2
    _port=$3

    f_PrintLog "Info" "START TO RUN SQL:${_sqlfile}, DBinfo ${_ip}:${_port}"
    echo "*************************************************************"
    cat ${_sqlfile}
    echo "*************************************************************"
    f_PrintLog "Info" "PLS CHECK ALL COMMANDS IN FILE, SLEEP 3......"
    sleep 3
    echo "mysql -h${_ip} -P${_port} -u${user} -p -vvv < ${_sqlfile}"
    mysql -h${_ip} -P${_port} -u${user} -p${tidbPasswd:-123} -vvv < ${_sqlfile}
    return $?
}


function f_tiupcmd
{
    f_PrintLog "Info" "Start to run f_tiupcmd."
    cls_name="$1"
    component_name="$2"
    action="$3"
    
    f_PrintLog "Info" "Start to ${action} ${component_name} on cluster ${cls_name} ..."
    sleep 3
    #clsinfo 
    tiup cluster ${action} ${cls_name} -N${component_name} --yes --wait-timeout 60
    rt_code=$?
    if [ ${rt_code} -eq 0 ]
    then
        f_PrintLog "Info" "${action} ${component_name} on cluster ${cls_name} successfully!"
        return ${rt_code}
    else
        f_PrintLog "Error" "${action} ${component_name} on cluster ${cls_name} failed!!!"
        return ${rt_code}
    fi
}


function f_checkdrinittso
{
    f_PrintLog "Info" "Start to run f_checkdrinittso."

    #cluster edit-config
    metaFile="${tidbHome}/.tiup/storage/cluster/clusters/${clsName}/meta.yaml"
    drainerDeployDir=$(tiup cluster display ${clsName}  2>/dev/null |awk -v dip="${tidbDrainerId}" '{
        if($1==dip && $2=="drainer"){printf "%s",$NF;exit}
        }')
    drainerDeployConfig="${drainerDeployDir}/conf/drainer.toml"
    
    if [ ! -f "${metaFile}" ]
    then
        f_PrintLog "Error" "tiup config meta yaml not exists,${metaFile}, exit!!!"
        exit 1
    fi

    drinittsOk0=$(cat ${metaFile} | awk -F: '{if($1~"initial-commit-ts" && $2!~"-1"){printf "%s",$2}}')
    # echo "tiup cluster exec ${clsName} -N${tidbIp_s} --command grep initial-commit-ts ${drainerDeployConfig}"
    # stdout=$(tiup cluster exec ${clsName} -N${tidbIp_s} --command "grep initial-commit-ts ${drainerDeployConfig}")
    # drinitts=$(echo ${stdout}| awk -F"stdout:" '{print $2}')
    # drinittsOk1=$(echo ${drinitts}| awk -F= '{if($1~"initial-commit-ts" && $2!~"-1"){printf "%s",$2}}')
    
    if [ -n "${drinittsOk0}" -o -n "${drinittsOk1}" ]
    then
        f_PrintLog "Error" "drainer config initial-commit-ts != -1, meta config:${drinittsOk0}, drainer config:${drinittsOk1}, exit!!!"
        exit 1
    else
        f_PrintLog "Info" "drainer config initial-commit-ts = -1, check pass!"
    fi
    
}



function f_stopdrainer
{
    #check tidb
    tiup cluster display ${clsName} 
    
    action="stop"
    f_tiupcmd "${clsName}" "${tidbDrainerId}" "${action}"
    rt_code=$?
    
    tiup cluster display ${clsName} 
    drok=$(tiup cluster display ${clsName}  2>/dev/null |awk -v dip="${tidbDrainerId}" '{
        if($1==dip && $2=="drainer" && $6=="Down"){printf "%s\n",$1;exit}
        }')
    
    if [ ${rt_code} -eq 0 -a -n "${drok}" ]
    then
        f_PrintLog "Info" "cluster ${clsName} node ${tidbDrainerId}, role:${drok}, ${action} finish!"    
    else
        f_PrintLog "Error" "cluster ${clsName} node ${tidbDrainerId}, role:${drok}, ${action} failed !!!"
        exit 1
    fi
}

function f_CheckDumplingCount
{
    f_PrintLog "Info" "Start to run f_CheckDumplingCount."
    MaxRunningCount=0
    ##Check ShellCount##
    for i in 1 2 3
    do
        ShellCount=$(ps -ef|grep "dumpling"|grep -vE "$$|cat|more|vi|view|grep|tail|tee"|awk 'END{print NR}')
        if [ ${ShellCount} -le ${MaxRunningCount:-0} ]
        then
            break
        fi

        ps -ef|grep "dumpling"|grep -vE "$$|cat|more|vi|view|grep|tail|tee"
        f_PrintLog "Warn" "dumpling is running, running count:${ShellCount}, greater then ${MaxRunningCount:-0}, continue ${i}";
        sleep 3
    done

    if [ ${ShellCount} -gt ${MaxRunningCount:-0} ]
    then
        ps -ef|grep "dumpling"
        f_PrintLog "Error" "Dumpling is current running, current pid is $$, exit!!!";
        exit 1
    fi
}

function f_backupsizecheck
{
    _clsname=$1

    f_PrintLog "Info" "Start to run f_backupsizecheck."
    
    pdNode=$(tiup cluster display ${_clsname}  2>/dev/null |awk  '{
        if($2=="pd" && $6~"Up"){printf "%s\n",$1;exit}
        }')
    f_PrintLog "Info" "Get cluster size pd-ctl ${pdNode}"
    storeSize=$(pd-ctl --pd="http://${pdNode}" store | awk '{if($1~"used_size"){gsub(",","",$2);gsub("\"","",$2);printf "%s\t",$2}}')
    sizeMB=0
    for _size in ${storeSize}
    do
        _sizemb=$(echo ${_size}|awk '
            /.*KiB/{gsub("KiB","",$1);sz=$1/1000}
            /.*MiB/{gsub("MiB","",$1);sz=$1}
            /.*GiB/{gsub("GiB","",$1);sz=$1*1000}
            /.*TiB/{gsub("TiB","",$1);sz=$1*1000000}
            END{printf "%d\n",sz}
            ')
        sizeMB=$((sizeMB+_sizemb))
    done
    f_PrintLog "Info" "Cluster ${clsName} size is ${sizeMB}MiB"
    
    df -h
    diskFree=$(df -P -m ${backupdir}|awk '!/Filesystem/{printf "%d",$4;exit}')
    if [ ${diskFree:-1} -gt ${sizeMB:-1} ]
    then
        f_PrintLog "Info" "backup dir ${backupdir} free space is ${diskFree}MB, greater then dbsize ${sizeMB}MB"
    else
        f_PrintLog "Error" "backup dir ${backupdir} free space is ${diskFree}MB, less then dbsize ${sizeMB}MB, quit!!!"
        exit 1
    fi
}


function f_rundumpling
{
    _ip=$1

    f_PrintLog "Info" "Start to run f_rundumpling."
    
    #dumpling config
    numProc=$(awk '/^processor[ |\t]*:/{c++}END{print c}' /proc/cpuinfo 2>/dev/null)
    if [ ${numProc} -ge 40 ]
    then
        thread=12
    elif [ ${numProc} -ge 20 ]
    then
        thread=6
    else
        thread=4
    fi

    enginetype='tidb_isolation_read_engines=tikv'
    _snapshot=$(date "+%Y-%m-%d %H:%M:%S")
    _backupdir=${backupdir}/${RunTs}
    _baklogfile=${backupdir}/logs/dumpling_${_ip}_${RunTs}.log

    if [ ! -d ${backupdir} ]
    then
        f_PrintLog "Error" "backup dir ${backupdir} not exists, exit!!!"
        exit 1
    fi

    mkdir -p ${_backupdir}
    mkdir -p ${backupdir}/logs
    
    # [ -n "${dbs}" ] && db_option="-B ${dbs}"
    conn_option="-h ${_ip} -P ${tidbPort} -u ${user} "
    dir_option="-o ${_backupdir}"
    par_option="--filetype sql --params ${enginetype} -t ${thread} -r 200000 -F 256MiB"

    dumpling -V
    f_PrintLog "Info" "start to run dumpling backup, log file:${_logfile} "
    f_PrintLog "Info" "parameter: ${conn_option} ${dir_option} ${db_option} ${par_option} -p "

    echo "dumpling ${conn_option} ${dir_option} ${db_option} ${par_option} -p 123 2>&1 >> ${_baklogfile} "
    dumpling ${conn_option} ${dir_option} ${db_option} ${par_option} -p ${tidbPasswd} 2>&1  >> ${_baklogfile}  &
    f_PrintLog "Info" "run tiup dumpling finished, retrun ${_rt_code}, backup dir is ${_backupdir} "
    
    sleep 10
    ps -ef|grep "dumpling"
    dumplingCount=$(ps -ef|grep "dumpling"|grep -vE "grep|tail|tee" |awk 'END{print NR}')
    dumplingSucc=$(tail ${_baklogfile}|grep "dump data successfully"|wc -l)

    if [ ${dumplingCount:-0} -ge 1 ]
    then
        f_PrintLog "Info" "Dumpling is running background, pls monitor it and check the log file ${_baklogfile}"
    elif [ ${dumplingSucc} -eq 1 ]
    then
        f_PrintLog "Info" "Dumpling already finished, pls check the log file ${_baklogfile}"
    else
        f_PrintLog "Error" "Dumpling was not start running, pls check the log file ${_baklogfile}"
        exit 1
    fi

}


###############################################################################
##Init##
export LANG=en_US
ShellName="$(echo $0|awk -F / '{print $NF}')";ShellOption="$@";
WorkDir="$(echo $0|sed s/${ShellName}//g)";[ -z "${WorkDir}" ] && WorkDir=${PWD};cd ${WorkDir};WorkDir=${PWD}
LogDir="${WorkDir%/}/logs";mkdir -p ${LogDir};
RunTs=$(date "+%Y%m%d.%H%M%S")
LogFile="${LogDir}/${ShellName}.${RunTs}.log";
Shellcksum=$(cksum ${WorkDir%/}/${ShellName} 2>/dev/null|awk '{printf "%d",$1}')
declare -i UserID=$(id -u);declare UserName=$(whoami);declare HostName=$(hostname);
###############################################################################
##TiDB Var##
port="4000";user="timon";passwd=""
adminusers="root,tidb,timon,repl"
checkpointschema="tidb_binlog"
isswitchback="NO"
p_port=${port};s_port="4100"
###############################################################################
###############################################################################
##Get inopt
[ -z "${ShellOption}" ] && f_syntax;
for inopt in ${ShellOption}
do
    case $(echo $inopt|tr a-z A-Z) in
    -PRIMARYHOSTNAME|-HOSTNAME) CurOpt="-HOSTNAME_P";continue;;
    -STANDBYHOSTNAME) CurOpt="-HOSTNAME_S";continue;;
    -HELP|--HELP|-H) f_syntax;;
    -*) CurOpt="";continue;;
    esac
    case "${CurOpt}" in
    -HOSTNAME_P) declare HostName_P="${inopt}";continue;;
    -HOSTNAME_S) declare HostName_S="${inopt}";continue;;
    esac
done

f_PrintLog "Info" "Start to run Script ${ShellName}, the Shellcksum is ${Shellcksum}"
f_PrintLog "Debug" "Script Options:${ShellOption}"

if [ "${HostName_P}" != "${HostName}" -a "${HostName_S}" != "${HostName}" ] 
then
    f_PrintLog "Error" "InputHost are ${HostName_P} and ${HostName_S}, but now i am ${HostName}, exit"
    # exit 1
fi

if [ "${HostName_P}" == "${HostName}" ] 
then
    HostRole="PRIMARY"
elif [ "${HostName_S}" == "${HostName}" ]
then
    HostRole="STANDBY"
else
    HostRole="UNKNOWN"
fi

f_PrintLog "Info" "InputHost are ${HostName_P} and ${HostName_S}, now i am ${HostName}, the hostrole:${HostRole}"


function f_getclusterinfo
{
    f_PrintLog "Info" "Start to run f_getclusterinfo."
    # clsname,connip,connport,dbsize,dbs,userlist,drainernode,backupdir,repluser
    clsName=$(tiup cluster list 2>/dev/null|awk '{if($3~"v" && $4~"tiup"){printf "%s\n",$1;exit}}') 
    if [ -z "${clsName}" ]
    then
        f_PrintLog "Error" "Can not find valid cluster name using:tiup cluster list"
        exit 1
    fi
    f_PrintLog "Info" "find valid cluster name is ${clsName}"
    tiup cluster display ${clsName} 

    tidbclsinfo=$(tiup cluster display ${clsName} 2>/dev/null)
    IPList=$(ip a |awk '/^[1-9]/{gsub(":","",$2);en=$2}
     /^[ |\t]*inet /{gsub(".*:","",$2);gsub("/.*","",$2);if(en!~"^lo")N[$2]=en}
     END{for(ip in N){printf "%s\n",ip}}')
    tidbProc=$(ps -ef | grep "tidb-server -P 4000"| grep -v grep)
    tidbPort=$(echo ${tidbProc}|awk -F"-P" '{print $2}'|awk '{printf "%d",$1}')
    tidbIp=$(echo ${tidbProc}|awk -F"--advertise-address=" '{print $2}'|awk '{printf "%s",$1}')

    tidbNodeId=$(tiup cluster display ${clsName} 2>/dev/null|awk -v id="${tidbIp}:${tidbPort}" '{
     if($1==id && $2=="tidb" && $6=="Up" ){printf "%s",$1;exit}
     }')

    [ -z "${tidbNodeId}" ] && f_PrintLog "Error" "Can not find valid tidb node ${tidbIp}:${tidbPort}, the cluster name is ${clsName}"

    tidbDrainerId=$(tiup cluster display ${clsName} 2>/dev/null|awk -v id="${tidbIp%.*}" '{
     if($1!~id && $2=="drainer"){printf "%s",$1;exit}
     }')

    if [ -z "${tidbDrainerId}" ] 
    then
        f_PrintLog "Error" "Can not find valid drainer node, the cluster name is ${clsName}"
    else
        tidbIp_s=${tidbDrainerId%:*}
    fi

    f_PrintLog "Info" "Get ${HostRole} cluster info finished. clsname:${clsName}, Ip:${tidbIp}, Port:${tidbPort}, drainerId:${tidbDrainerId}, standbyIp:${tidbIp_s}"
}

function f_gettidbinfo
{
    f_PrintLog "Info" "Start to run f_gettidbinfo."
    tidbHome=$(awk -F: -v un="tidb" '{if($1==un){print $6;exit}}' /etc/passwd 2>/dev/null)
    adminusers_sql=$(echo "${adminusers}"|sed "s/,/','/g")
    _execsql="select group_concat(concat(user,'@',host) SEPARATOR ',') from  mysql.user where user not in ('${adminusers_sql}')"
    tidbPasswd=$(echo "${passwd}="|base64 -d)
    f_PrintLog "Info" "START TO RUN SQL:${_execsql}, DBinfo ${tidbIp}:${tidbPort}"
    appUsers=$(mysql -h${tidbIp} -P${tidbPort} -u${user} -p${tidbPasswd:-123} -NBe "${_execsql}")
    if [ -z "${appUsers}" ]
    then
        f_PrintLog "Error" "get app users failed!!!"
        exit 1
    fi

    f_PrintLog "Info" "Get tidb ${tidbIp_s}:${tidbPort} info finished. appUser:${appUsers}, replUser:repl, tidbHome:${tidbHome}"
}

function f_stopreplmain
{
    #grant sql files
    grantsqldir="${tidbHome}/switchover/grantsql"
    # mkdir -p ${grantsqldir}
    app_bakfile=${grantsqldir}"/usergrant_app_${tidbIp}_$(date +%Y%m%d).sql"
    app_rvkfile=${grantsqldir}"/userrevoke_app_${tidbIp}_$(date +%Y%m%d).sql"
    repl_bakfile=${grantsqldir}"/usergrant_repl_${tidbIp_s}_$(date +%Y%m%d).sql"
    repl_rvkfile=${grantsqldir}"/userrevoke_repl_${tidbIp_s}_$(date +%Y%m%d).sql"

    #user
    # p_passwd=$(echo "${passwd}="|base64 -d)
    # s_passwd=${p_passwd}


    #revoke app user,primary
    f_runsqlfile "${app_rvkfile}" "${tidbIp}" "${tidbPort}"
    _rt_code=$?
    if [ ${_rt_code} -eq 0 ]
    then
        f_PrintLog "Info" "revoke app user privileges finish!"
    else
        f_PrintLog "Error" "revoke app user privileges failed!!!"
        exit 1
    fi
    
    #check connection
    tmp_file="./logs/tmp_apps.txt"
    echo "" > ${tmp_file}
    f_checkconnection "${tidbIp}"
    app_counts=$(wc ${tmp_file}|awk '{printf "%d",$1+$2}')
    if [ ${app_counts} -gt 1 ]
    then
        f_PrintLog "Error" "some applications still use database, check failed, pls stop applications and kill all connections!!!"
        exit 1
    else
        f_PrintLog "Info" "all connections are closed, check pass!"
    fi


    f_stopdrainer "${clsName}"

    # revoke repl user standby
    f_runsqlfile "${repl_rvkfile}" "${tidbIp_s}" "${tidbPort}"
    _rt_code1=$?

    # grant repl user primary
    f_runsqlfile "${repl_bakfile}" "${tidbIp}" "${tidbPort}"
    _rt_code2=$?
    _rt_code=$((_rt_code1+_rt_code2))
    if [ ${_rt_code} -eq 0 ]
    then
        f_PrintLog "Info" "revoke and grant repl user privileges finish!"
    else
        f_PrintLog "Error" "revoke and grant repl user privileges failed!!!"
        exit 1
    fi
    
}

function f_dumplingmain
{
    f_PrintLog "Info" "Start to run f_dumplingmain."

    backupdir="/data1/tidbbackup"
    if [ ! -d "${backupdir}" ]
    then
        f_PrintLog "Error" "backupdir ${backupdir} not exists, exit!!!"
        exit 1
    fi

    f_CheckDumplingCount
    f_backupsizecheck "${clsName}"
    f_rundumpling "${tidbIp}"
}

###############################################################################
###################
####    Main   ####
###################


function primary_switch_main
{
    #获取脚本执行需要的集群信息
    f_getclusterinfo  

    #获取脚本执行需要的数据库连接信息
    f_gettidbinfo 
    f_PrintLog "Info" "Get ${HostRole} cluster info finished. clsname:${clsName}, clsname_s:${clsName_s}, Ip:${tidbIp}, standbyIp:${tidbIp_s}, drainerId:${tidbDrainerId}"

    f_checkdrinittso

    #执行主集群的复制暂停
    f_stopreplmain

    #执行dumpling
    f_dumplingmain
}

primary_switch_main | tee -a ${LogFile}
exit ${PIPESTATUS[0]} 


