#!/bin/bash

# helper to manage PDUs

# the target PDU is supposed to be specified using the following env. variables

[[ -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; }

#
# supported actions are
#   list on off status
# e.g.
#   pdu list
#   pdu status 1 7
#   pdu on 1 7
#
# 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



# v2 (SNMP): new iteration - status, we use:
# (*) SNMP for reading (probe & status)
# (*) SSH for writing (on and off)
# hopefully this is temporary, but here's what we get
# root@faraday /tmp # snmpget -c public -v 1 192.168.4.107 1.3.6.1.4.1.534.6.6.7.6.6.1.2.1.8
# SNMPv2-SMI::enterprises.534.6.6.7.6.6.1.2.1.8 = INTEGER: 0
# root@faraday /tmp # snmpset -c public -v 1 192.168.4.107 1.3.6.1.4.1.534.6.6.7.6.6.1.2.1.8 i 1
# Error in packet.
# Reason: (noSuchName) There is no such variable name in this MIB.
# Failed object: SNMPv2-SMI::enterprises.534.6.6.7.6.6.1.2.1.8



##### SNMP

# http://www.circitor.fr/Mibs/Html/E/EATON-EPDU-MIB.php

# root@faraday /tmp # snmpwalk -Os -c public -v 1 192.168.4.107 <the-oid>
#
PREFIX_STATUS="enterprises.534.6.6.7.6.6.1.2"
# oid=1.3.6.1.4.1.534.6.6.7.6.6.1.2
# enterprises.534.6.6.7.6.6.1.2.0.1 = INTEGER: 0
# <snip>
# enterprises.534.6.6.7.6.6.1.2.1.8 = INTEGER: 0
#
PREFIX_NAME="enterprises.534.6.6.7.6.1.1.3"
# oid=enterprises.534.6.6.7.6.1.1.3
# SNMPv2-SMI::enterprises.534.6.6.7.6.1.1.3.0.1 = STRING: "Outlet USRP N300"
# <snip>
# SNMPv2-SMI::enterprises.534.6.6.7.6.1.1.3.1.8 = STRING: "Outlet A8"
#
PREFIX_WATTS="enterprises.534.6.6.7.6.5.1.2"
# oid = enterprises.534.6.6.7.6.5.1.2
# SNMPv2-SMI::enterprises.534.6.6.7.6.5.1.2.0.1 = INTEGER: 0
# <snip>
# SNMPv2-SMI::enterprises.534.6.6.7.6.5.1.2.1.8 = INTEGER: 0


### get
#
# root@faraday /tmp # snmpget -c public -v 1 192.168.4.107 1.3.6.1.4.1.534.6.6.7.6.6.1.2.1.8
# SNMPv2-SMI::enterprises.534.6.6.7.6.6.1.2.1.8 = INTEGER: 0

SNMP_OPTIONS=""
SNMP_OPTIONS="$SNMP_OPTIONS -c public"
SNMP_OPTIONS="$SNMP_OPTIONS -v 1"
SNMPWALK="snmpwalk ${SNMP_OPTIONS} ${PDU_IP}"
SNMPGET="snmpget ${SNMP_OPTIONS} ${PDU_IP}"

##### SSH

COMMAND=$(basename $0)
TMP=$(mktemp /tmp/pdu.XXXXXX.txt) || { 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}

SSH_OPTIONS=""
SSH_OPTIONS="$SSH_OPTIONS -oLogLevel=ERROR"
SSH_OPTIONS="$SSH_OPTIONS -oUserKnownHostsFile=/dev/null"
SSH_OPTIONS="$SSH_OPTIONS -oStrictHostKeyChecking=no"
SSH_OPTIONS="$SSH_OPTIONS -oKexAlgorithms=+diffie-hellman-group1-sha1"
SSH_OPTIONS="$SSH_OPTIONS -oPreferredAuthentications=password"

SSH="sshpass -p${PDU_PASSWORD} ssh ${SSH_OPTIONS} -l${PDU_USERNAME} ${PDU_IP}"

# with used to use -i /dev/null instead of  -oPreferredAuthentications=password
# which we were really meaning in the first place
# regardless, with that setting involving /dev/null the output
# was garbled with 7 lines of warning messages
# this is no longer useful, but let's keep it in place
SSH_IGNORE_LINES=0

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


# extract the last integer in line
function snmp-extract-integer() {
    sed -e 's|.*INTEGER: \([0-9]*\)|\1|'
}
# extract the pdu.outlet address in line
function snmp-extract-address() {
    sed -e 's|.*\([0-9][0-9]*\.[0-9][0-9]*\) =.*|\1|'
}
# extract the last string in line
function snmp-extract-string() {
    sed -e 's|.*STRING: "\(.*\)"|\1|'
}
function remove-outlet() {
    sed -e 's|Outlet ||'
}

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

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

    ${SNMPWALK} ${PREFIX_NAME} | while read line; do
        local address=$(snmp-extract-address <<< $line)
        local name=$(snmp-extract-string <<< $line | remove-outlet)
        local status_integer=$(${SNMPGET} ${PREFIX_STATUS}.${address} | snmp-extract-integer)
        local watts=$(${SNMPGET} ${PREFIX_WATTS}.${address} | snmp-extract-integer)
        local status="???"
        local power=""

        case "${status_integer}" in
            0) status="OFF" ;;
            1) status="ON"; power=" (${watts}W)" ;;
        esac

        echo $address: $name $status$power
    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 pdu="$1"; shift
    local outlet="$1"; shift

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

    local status_integer=$(${SNMPGET} ${PREFIX_STATUS}.${pdu}.${outlet} | snmp-extract-integer)
    local name=$(${SNMPGET} ${PREFIX_NAME}.${pdu}.${outlet} | snmp-extract-string | remove-outlet)
    local watts=$(${SNMPGET} ${PREFIX_WATTS}.${pdu}.${outlet} | snmp-extract-integer)
    case "$status_integer" in
        0)
            echo "chain-$pdu@outlet-$outlet ($name): OFF"
            myexit 1
            ;;
        1)
            echo "chain-$pdu@outlet-$outlet ($name): ON (${watts}W)"
            myexit 0
            ;;
        *)
            echo "Could not retrieve chain-$pdu@outlet-$outlet ($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 pdu="$1"; shift
    local outlet="$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

    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 | sed "s/[^0-9a-zA-Z ]//g" | sed -e 's|Outlet ||')
    line=$(relevant-line 3)
    local res=$(sed -n "${line}p" $TMP | sed "s/[^0-9-]//g")

    if [ "$res" -eq "$expected" ]; then
        echo "chain-$pdu@outlet-$outlet ($name): ${mode}"
        myexit 0
    else
        echo "Error could not switch ${mode} chain-$pdu@outlet-$outlet ($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
