#!/bin/bash

#
# Cluster init !
#

#function ProgressBar {
#    # Process data
#    let _progress=(${1}*100/${2}*100)/100
#    let _done=(${_progress}*4)/10
#    let _left=40-$_done
#    # Build progressbar string lengths
#    _fill=$(printf "%${_done}s")
#    _empty=$(printf "%${_left}s")
#
#    # 1.2 Build progressbar strings and print the ProgressBar line
#    # 1.2.1 Output example:
#    # 1.2.1.1 Progress : [########################################] 100%
#    printf "\rProgress : [${_fill// /#}${_empty// /-}] ${_progress}%%"
#}

function redisRun()
{
	cmd="redis-cli"
    host=$1
    shift
    port=$1
    shift
    #pass=$1
    #shift

	#opt="-h ${host} -p ${port} -a ${pass}"
	opt="-h ${host} -p ${port}"
	act=${@}
	${cmd} ${opt} ${act}
	return ${?}
}
#
# Get Node ID with IP Adress and Port Number
# Params :
#  - $1 => Leader IP
#  - $2 => Leader Port
#  - $3 => Cluster Password
#  - $4 => Node IP Adress
#  - $5 => Node Port
#
function getNodeID()
{
    if [[ ${1} == ${3} ]]
    then
        searchCmd="awk '/myself,master/ { print \$1  }'"
    else
        searchCmd="awk '/${3}:${4}@/ { print \$1  }'"
    fi
	result=$(redisRun $1 $2 cluster nodes | eval ${searchCmd})
	rcode=${?}

	if [[ -z ${result} ]]
	then
		searchCmd="awk '/${3}:${4}@/ { print \$1   }'"
		result=$(redisRun $1 $2 cluster nodes | eval ${searchCmd})
		rcode=${?}
	fi
	echo ${result}
	return ${rcode}
}

function redisClusterForgetAll()
{
    local res=0
    for node in $(redisRun ${1} ${2} ${3} cluster nodes | awk '!/myself,master/ {print $1}')
    do
        redisRun ${1} ${2} ${3} "cluster forget ${node}"
        res=$((res+${?}))
    done
    return ${res}
}

#
# Assing redis slots to master Node
# Params :
#  - $1 => Node IP
#  - $2 => Node Port
#  - $3 => Cluster Password
#  - $4 => First slot
#  - $5 => Last slot
#
function redisSlotAssign()
{
    res=0
    for slot in $(seq ${3} ${4})
    do
        OUT=$(redisRun ${1} ${2} "cluster ADDSLOTS $slot")
        res=$((res+${?}))
        if [[ ${OUT} =~ ERR ]]
        then
            OUT=$(redisRun ${1} ${2} "cluster ADDSLOTS $slot")
        fi
    done
    return ${res}
}

function redisAddReplica()
{
    redisRun ${1} ${2} "cluster replicate ${3}"
    return ${?}
}

function redisClusterIsOK()
{
    out=$(redis-cli -h ${1} -p ${2} cluster info | grep "cluster_state:ok")
    return $?
}

CONF="/etc/redis/cluster.conf"

[[ ! -e ${CONF} ]] && exit 0

declare -A IPADDRS
declare -A PORTS
declare -A ROLES
declare -A MASTERS
declare -A NIDS
declare LeaderIP
declare LeaderPort
index=0

while read line
do
	[[ ${line} =~ ^# ]] && continue
	line=${line//::/:none:}
	li=(${line//:/ })

    name=${li[0]}
    ip=${li[1]}
    port=${li[2]}
    role=${li[3]}
    master=${li[4]}
	ntype=${li[5]}

	if [[ ${ntype} == "Leader" ]] 
	then
		LeaderIP=${ip}
		LeaderPort=${port}
	fi
    NAMES+=(${name})
    IPADDRS+=([${name}]=${ip})
	PORTS+=([${name}]=${port})
	ROLES+=([${name}]=${role})
	MASTERS+=([${name}]=${master})
done < ${CONF}

# If the cluster is "ok" don't do anything
st=$(redisClusterIsOK ${LeaderIP} ${LeaderPort})
#[[ ${?} -eq 0 ]] && exit 0

if [[ ${1} == "forget" ]]
then
    redisClusterForgetAll ${LeaderIP} ${LeaderPort}
    exit ${?}
fi

echo " * Organise Cluster Meeting."
for node in ${NAMES[@]}
do
	act="cluster meet ${IPADDRS[${node}]} ${PORTS[${node}]}"
    echo "   - ${node} - ${IPADDRS[${node}]}:${PORTS[${node}]} meeting with :"
    for oth in ${NAMES[@]}
    do
        [[ ${oth} == ${node} ]] && continue
        echo -en "      -> ${oth} - ${IPADDRS[${oth}]} ${PORTS[${oth}]} "
        redisRun ${IPADDRS[${oth}]} ${PORTS[${oth}]} "${act}"
    done
done

echo " * Waiting for all members ."
for node in ${NAMES[@]}
do
    try=3
    scmd="awk '/${IPADDRS[$node]}:${PORTS[${node}]}/ {print \$8}'"
    while [[ $(redisRun ${LeaderIP} ${LeaderPort} "cluster nodes" | eval ${scmd}) != "connected" ]]
    do
        sleep 1
        try=$((try-1))
        [[ ${try} -eq 0 ]]
        break
    done
done

nbMaster=$(grep -c ":master" ${CONF})
totalslots=16384
perNodeSlots=$((totalslots/nbMaster))
startSlot=0

echo " * Assign Slots to the nodes."
for node in ${NAMES[@]}
do
    if [[ ${ROLES[$node]} == "master" ]]
    then
        if [[ ${startSlot} -eq 0 ]]
        then
            lastSlot=$(((perNodeSlots+startSlot)-1))
        else
            lastSlot=$((perNodeSlots+startSlot))
        fi
        [[ ${lastSlot} -ge ${totalslots} ]] && lastSlot=$((totalslots-1))
        echo "    - Slots ${startSlot} to ${lastSlot} => ${node} - ${IPADDRS[${node}]} ${PORTS[${node}]}"
        redisSlotAssign ${IPADDRS[${node}]} ${PORTS[${node}]}  ${startSlot} ${lastSlot}
        startSlot=$((lastSlot+1))
    fi
done

echo " * Configuring replication."
NIDS+=()
for node in ${NAMES[@]}
do
   NIDS+=([${node}]=$(getNodeID ${LeaderIP} ${LeaderPort} ${IPADDRS[${node}]} ${PORTS[${node}]}))
done

for node in ${NAMES[@]}
do
    [[ ${ROLES[$node]} == "master" ]] && continue
    echo -ne "   - Replicate ${MASTERS[${node}]} to ${node}\t\t"
    redisAddReplica ${IPADDRS[${node}]} ${PORTS[${node}]} ${NIDS[${MASTERS[${node}]}]}
done

sleep 5

echo
echo "Cluster status :"
echo
redisRun ${LeaderIP} ${LeaderPort} cluster info
echo


exit 0