#!/usr/bin/env bash

#
# Register all Hâpy Cluster Nodes
#

. /usr/lib/eole/ihm.sh

#
# NAME: copy_ssh_id
# AIM: Copy the ssh key on the host
# PARAM: the hostname of the node
#
function copy_ssh_id()
{
    local host=${1}

    CLEF=$(cat ~oneadmin/.ssh/eole.pub)
    PKEY=$(cat ~oneadmin/.ssh/eole)
    ssh ${host} bash <<EOF
if ! grep -qs "$CLEF" ~oneadmin/.ssh/authorized_keys; then
    echo "$CLEF" >> ~oneadmin/.ssh/authorized_keys
    echo "${CLEF}" > ~oneadmin/.ssh/eole.pub
    echo "${PKEY}" > ~oneadmin/.ssh/eole
    chown oneadmin:oneadmin ~oneadmin/.ssh/authorized_keys
    chown oneadmin:oneadmin ~oneadmin/.ssh/eole.pub
    chown oneadmin:oneadmin ~oneadmin/.ssh/eole
    su - oneadmin -c "ssh-keyscan $host"
fi
EOF
    [[ ${?} -ne 0 ]] && EchoRouge "Erreur lors de l'échange de clés SSH avec le noeud ${host}"
    REMOTEKEY=$(su - oneadmin -c "ssh ${host} 'cat ~oneadmin/.ssh/eole.pub'")
    if ! grep -qs "$REMOTEKEY" ~oneadmin/.ssh/authorized_keys; then
        su - oneadmin -c "echo ${REMOTEKEY} >> ~oneadmin/.ssh/authorized_keys"
        su - oneadmin -c "ssh-keyscan $host"
    fi
}

function copy_files()
{
    local host=${1}
    su - oneadmin -c "ssh ${host} '[[ ! -d .one ]] && mkdir -p .one'"
    su - oneadmin -c "scp ~oneadmin/.one/* ${host}:.one/"
}

#
# NAME: sync_db
# AIM: Sync onedb in HA and sqlite mode
# PARAM: the hostname of the node
#
function sync_db()
{
    local ip=${1}
    local user="oneadmin"
    local DBFILE="/var/lib/one/one.db"
    local DBBCK="/tmp/one.db.bck"
    local ret=0

    if [[ ! -f ${DBBCK} ]]
    then
        cmd="onedb backup --sqlite ${DBFILE} ${DBBCK}"
        ret=$(su - ${user} -c -- "${cmd}")
    fi

    cmd2="scp ${DBBCK} ${ip}:${DBFILE}.leader"
    ret=$(su - ${user} -c -- "${cmd2}")
    return ${?}
}

#
# NAME: sync_nodes
# AIM: sync nodes
# PARAM: none
#
function sync_nodes()
{
    cmd2="onehost sync"
    ret2=$(su - ${ONEUSER} -c -- "${cmd2}")
    return ${?}
}

#
# NAME: stop_opennebula
# AIM: stop service opennebula on node
# PARAM: the node name
#
function stop_opennebula()
{
    local host=${1}
    su - oneadmin -c "ssh ${host} 'sudo systemctl stop opennebula'"
}

#
# NAME: restart_onenode
# AIM: stop service opennebula on node
# PARAM: the node name
#
function restart_onenode()
{
    local host=${1}
    su - oneadmin -c "ssh ${host} 'sudo systemctl restart onenode'"
}

#
# NAME: restart_opennebula
# AIM: restart service opennebula on node
# PARAM: the node name
#
function restart_opennebula()
{
    local host=${1}
    su - oneadmin -c "ssh ${host} 'sudo systemctl restart opennebula'"
}

#
# NAME: restart_onenodestart
# AIM: restart service onenodestart on node
# PARAM: the node name
#
function restart_onenodestart()
{
    local host=${1}
    su - oneadmin -c "ssh ${host} 'sudo systemctl restart onenodestart'"
}

#
# NAME: wait_for_oned
# AIM: block execution until oned api is accessible
# PARAM: The node name
#
function wait_for_oned()
{
    local host=${1}
    while ! su - oneadmin -c "ssh ${host} '(: </dev/tcp/127.0.0.1/2633) &>/dev/null'"
    do
        sleep 1
    done
    sleep 10
}

#
# NAME: register_node
# AIM: register the node in OpenNebula master
# PARAM: the node hostname
#
function register_node()
{
    cmd="onehost create -i kvm -v kvm -c 0 ${1}"

    ret=$(su - ${ONEUSER} -c -- "${cmd}")
    if [[ ${?} -ne 0  ]]
    then
        EchoRouge "Hosts register failed $res"
    else
        EchoVert "Hosts register OK"
    fi
}


#
# NAME: wait_node_ok
# AIM: Wait until the node is OK or ERROR
# PARAM: the node name
#
function wait_node_ok()
{
    local cmd="onehost show ${1} | awk '/^STATE/ {print \$3}'"
    local spinstr='|/-\'
    local delay=0.75
    local cnt=0

    while [ 1 ]
    do
        st=$(su - ${ONEUSER} -c "${cmd}")
        [[ ${st} == "MONITORED" ]] && break
        [[ ${st} == '' ]] && break
        if [[ ${st} == "ERROR" ]]
        then
            if [ $cnt -gt 160 ]; then
                EchoRouge "Erreur lors de l'enregistrement du noeud ${host} !"
                break
            fi
        fi

        local temp=${spinstr#?}
        printf " [%c]  " "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b\b\b\b\b\b"
        cnt=$((cnt+1))
    done
    printf "    \b\b\b\b"
}

#
# NAME: apply_leader_db
# AIM: Restart follower service with leader DB
# PARAM: The node name
#
function apply_leader_db {
    local host=$1
    EchoVert " ** Synchronisation de la base de données"
    stop_opennebula ${host} 
    su - oneadmin -c "ssh ${host} '/usr/share/eole/preservice/31-one-ha'"
    restart_opennebula ${host}
    wait_for_oned ${host}
}

function init_ha_leader() {
    # server with index 1 exists if already instanciate
    onezone show 0 | grep -A 3 ^"HA & FEDERATION SYNC STATUS" | tail -n 1 | grep -q ^" 1 "
    if [ ! $? = 0 ]; then
        FOLLOWER=($(CreoleGet one_nodes))
        echo
        EchoVert " * Déclaration du premier leader"
        onezone server-add 0 --name ${FOLLOWER} --rpc http://$FOLLOWER/RPC2
        for follower in ${FOLLOWER[@]:1}; do
            echo
            EchoVert " * Déclaration du follower ${follower}"
            apply_leader_db ${follower}
            until [[ "$(onezone show 0 -j | jq -r '.ZONE.SERVER_POOL.SERVER | if type == "array" then . else [.] end | .[].NAME')" =~ "$follower" ]]
            do
                EchoVert " ** Déclaration du follower"
                i=0
                spinstr='-\|/'
                message="En attente d'un leader [/]"
                reset=$(printf '\b%.0s' $(seq 1 ${#message}))
                until [[ "$(onezone show 0 -j | jq -r '.ZONE.SERVER_POOL.SERVER | if type == "array" then . else [.] end | .[].STATE')" =~ "3" ]]
                do
                    printf "$message"
                    i=$(( (i+1) %4 ))
                    printf "\b\b\b[${spinstr:$i:1}]"
                    sleep 1
                    printf "$reset"
                done
                onezone server-add 0 --name $follower --rpc http://$follower/RPC2 2>/dev/null
            done
        done
    fi
}

#
# MAIN
#
HAPY_ACTIF=$(echo $(CreoleGet activer_oned))
if [[ $HAPY_ACTIF == "non" ]]
then
    EchoRouge "Le serveur de virtualisation n'est pas activé dans l'interface de configuration du module"
    exit 1
fi

HAPY_NODE_SUPPORT=$(echo $(CreoleGet activer_multinode))
if [[ $HAPY_NODE_SUPPORT == "non" ]]
then
    EchoRouge "Le mode multi-noeuds n'est pas activé dans l'interface de configuration du module"
    exit 1
fi

HAPY_HA=$(CreoleGet activer_one_ha "non")

master=1
if [[ ${HAPY_HA} == "oui" ]]; then
    idx=$(CreoleGet one_ha_server_index)
    if [[ ${idx} = "0" ]]; then
        master=0
    else
        master=2
    fi
fi

ONEUSER=$(CreoleGet virt_user)
HAPY_SLV="$(CreoleGet one_nodes)"
DBMODE=$(CreoleGet one_database_type "none")

echo -e "\n"
EchoBleu "Vous allez inscrire un noeud dans une grappe Hâpy"
EchoBleu "Pour ce faire vous devez vous munir du mot de passe de l'utilisateur 'root' de chacun des noeuds"
Question_ouinon  "Voulez-vous commencer ?" 'True' "oui"
if [[ $? -ne 0 ]]
then
    EchoOrange "Abandon de l'enregistrement"
    exit 1
fi


for host in ${HAPY_SLV}; do
    echo -e "\n"
    EchoOrange "Traitement du noeud ${host}"
    echo
    EchoVert " * Gestion des clés SSH"
    echo
    copy_ssh_id ${host}
    if [ $master = 2 ]; then
        continue
    fi
#    if [ $master = 0 ]; then
    # Copy .one files
    copy_files ${host}
#   fi
    echo
    EchoVert " * Enregistrement du noeud"
    echo
    register_node ${host}
    if [[ ${HAPY_HA} == "oui" ]]
    then
        if [[ $DBMODE == "sqlite" ]]
        then
            if [[ $(CreoleGet one_ha_server_index) == "0" ]]
            then
                sync_db ${host}
            fi
        fi
    fi
    if [[ ${HAPY_HA} != "oui" ]]
    then
	# cf https://dev-eole.ac-dijon.fr/issues/36746
	sleep 5
        sync_nodes
        wait_node_ok ${host}
        if [[ ${?} -ne 0 ]]
        then
            EchoRouge "Erreur lors de l'enregistrement du noeud ${host} !"
        fi
        echo
        EchoVert " * Redémarrage du service onenodestart"
        restart_onenodestart ${host}
    else
        echo
        EchoVert " * Redémarrage des services onenode et opennebula"
        restart_onenode ${host}
        restart_opennebula ${host}
        wait_for_oned ${host}
    fi
done

if [ $master = 0 ]; then
    echo -e "\n"
    EchoOrange "Mise en place de la Haute Disponibilité de l’interface"
    init_ha_leader
fi
echo -e "\n"
EchoVert "Enregistrement des noeuds terminé"
