#!/bin/bash

# helper to talk to the PDU using expect
#
# supported actions are
#   list on off status
# the actual action is computed from $0
#   create symlinks toward this file
#   e.g. pdu-list is a symlink to pdu and means, well, to run the list command
#
# each command has its own usage, run with -help to see it
#
# return code
# 255: something went wrong
# 0: all went well
# 1: (for status only): the node is turned OFF
#    in that case 0 means the node is ON

COMMAND=$(basename $0)
TMP=$(mktemp /tmp/pdu.XXXXXX) || { echo cannot find a temporary filename; exit 255; }

function myexit() {
    local exitcode="$1"
    rm -f $TMP
    exit $exitcode
}

#echo using PDU_PASSWORD=${PDU_PASSWORD} PDU_USERNAME=${PDU_USERNAME} PDU_IP=${PDU_IP}

[[ -z "$PDU_IP" ]] && { echo variable PDU_IP is not configured - exiting; myexit 255; }
[[ -z "$PDU_USERNAME" ]] && { echo variable PDU_USERNAME is not configured - exiting; myexit 255; }
[[ -z "$PDU_PASSWORD" ]] && { echo variable PDU_PASSWORD is not configured - exiting; myexit 255; }

SSH="sshpass -p${PDU_PASSWORD} ssh -oLogLevel=ERROR -oUserKnownHostsFile=/dev/null -oStrictHostKeyChecking=no -oKexAlgorithms=+diffie-hellman-group1-sha1 -l${PDU_USERNAME} -i /dev/null ${PDU_IP}"
# with this setting -i /dev/null we get garbled errors at the top of the ssh session
SSH_IGNORE_LINES=7

# ignore that many lines when picking in the ssh output file
function relevant-line() {
    local line="$1"
    echo $((${SSH_IGNORE_LINES} + $line))
}


function pdu-probe() {
    function help() {
        echo "$COMMAND: Retrieve the status and label of all R2lab rack outlets"
        echo "Usage: $COMMAND"
        myexit 255
    }

    [[ "$#" == 0 ]] || help

    # this shows up at the end, to not really helpful is it
    #echo "Be patient..."

    #Expect script starts here
    /usr/bin/expect <<EOF >$TMP.txt

    set timeout 10

    spawn -noecho ${SSH}
    expect "pdu#0>"

    for {set pdu 0} {\$pdu <= 1} {incr pdu 1} {

        send -- "pdu \$pdu\r"
        expect ">"

        for {set outlet 1} {\$outlet <= 8} {incr outlet 1} {
            send -- "get PDU.OutletSystem.Outlet\[\$outlet\].PresentStatus.SwitchOnOff\r"
            expect ">"

            send -- "get PDU.OutletSystem.Outlet\[\$outlet\].iName\r"
            expect ">"
        }
    }
    send -- "quit"
EOF

    # the interesting stuff starts at line 3
    local line=$(relevant-line 3)
    for pdu in 0 1; do
        echo "PDU#: $pdu"
        for outlet in {1..8}; do
            local res=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9]//g")
            line=$((line + 2))
            local name=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9a-zA-Z ]//g" | sed -e 's|Outlet ||')
            line=$((line + 2))
            #echo "** res is $res and name is $name"
            case "$res" in
                0)
                    echo -e "\toutlet-$outlet@chain-$pdu ($name): OFF" ;;
                1)
                    echo -e "\toutlet-$outlet@chain-$pdu ($name): ON"  ;;
                *)
                    echo "Could not retrieve outlet-$outlet@chain-$pdu ($name) status, returned $res"
                    myexit 255
            esac
        done
        line=$((line + 1))
    done
    myexit 0

}


function pdu-status() {

    function help() {
        echo "$COMMAND: Retrieve the status of a specific R2lab rack outlet"
        echo "Usage: $COMMAND outlet# pdu#"
        echo -e "\twith outlet# in [1:8] and pdu# in [0:1]"
        myexit 255
    }

    [[ "$#" == 2 ]] || help

    local outlet="$1"; shift
    local pdu="$1"; shift

    [[ "$outlet" -lt 1 || "$outlet" -gt 8 || "$pdu" -lt 0 || "$pdu" -gt 1 ]] && help

    #Expect script starts here
    /usr/bin/expect <<EOF >$TMP.txt

    set timeout 10

    spawn -noecho ${SSH}
    expect "pdu#0>"

    send -- "pdu $pdu\r"
    expect ">"

    send -- "get PDU.OutletSystem.Outlet\[$outlet\].PresentStatus.SwitchOnOff\r"
    expect ">"

    send -- "get PDU.OutletSystem.Outlet\[$outlet\].iName\r"
    expect ">"

    send -- "quit"
EOF

    # on line 5
    local line=$(relevant-line 5)
    local name=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9a-zA-Z ]//g" | sed -e 's|Outlet ||')
    # on line 3
    line=$(relevant-line 3)
    local res=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9]//g")

    case "$res" in
        0)
            echo "outlet-$outlet@chain-$pdu ($name): OFF"
            myexit 1
            ;;
        1)
            echo "outlet-$outlet@chain-$pdu ($name): ON"
            myexit 0
            ;;
        *)
            echo "Could not retrieve outlet-$outlet@chain-$pdu ($name) status, returned $res"
            myexit 255
    esac
}

# factorized on and off

function -pdu-on-off() {

    local mode="$1"; shift    # 'on' or 'off'

    function help() {
        echo "$COMMAND: switch $mode a specific R2lab rack outlet"
        echo "Usage: $COMMAND outlet# pdu#"
        echo -e "\twith outlet# in [1:8] and pdu# in [0:1]"
        myexit 255
    }

    [[ "$#" == 2 ]] || help

    local outlet="$1"; shift
    local pdu="$1"; shift

    # factorizing on and off
    local setting=

    case $mode in
        on)
            setting=DelayBeforeStartup; expected=0 ;;
        off)
            setting=DelayBeforeShutdown; expected=-1 ;;
    esac


    #Expect script starts here
    /usr/bin/expect <<EOF >$TMP.txt

    set timeout 10

    spawn -noecho ${SSH}

    expect "pdu#0>"

    send -- "pdu $pdu\r"
    expect ">"

    send -- "set PDU.OutletSystem.Outlet\[$outlet\].${setting} 0\r"
    expect ">"

    send -- "get PDU.OutletSystem.Outlet\[$outlet\].iName\r"
    expect ">"

    send -- "quit"

EOF

    local line=$(relevant-line 5)
    local name=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9a-zA-Z ]//g" | sed -e 's|Outlet ||')
    line=$(relevant-line 3)
    local res=$(sed -n "${line}p" $TMP.txt | sed "s/[^0-9-]//g")

    if [ "$res" -eq "$expected" ]; then
        echo "outlet-$outlet@chain-$pdu ($name): ${mode}"
        myexit 0
    else
        echo "Error could not switch ${mode} outlet-$outlet@chain-$pdu ($name), returns $res"
        myexit 255
    fi
}

function pdu-on() {
    -pdu-on-off on "$@"
}
function pdu-off() {
    -pdu-on-off off "$@"
}

# so that we can call either
# pdu-status 0 0
# or
# pdu status 0 0
[[ "$COMMAND" == "pdu" ]] && {
    subcommand="$1"; shift
    COMMAND="pdu-${subcommand}"
}

$COMMAND "$@"
rm -f $TMP
