#!/bin/bash

# helper to manage PDUs

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

# PDU_IP is now passed on the command line
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; }

#
# supported actions are
#   list on off status
# e.g.
#   eaton list ip
#   eaton status ip 1 7
#   eaton on ip 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>
#
# http://www.circitor.fr/Mibs/Html/E/EATON-EPDU-MIB.php#outletControlStatus
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
#
# http://www.circitor.fr/Mibs/Html/E/EATON-EPDU-MIB.php#outletName
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"
#
#
# http://www.circitor.fr/Mibs/Html/E/EATON-EPDU-MIB.php#outletVA - Volt-Ampere
#PREFIX_WATTS="enterprises.534.6.6.7.6.5.1.2"
# http://www.circitor.fr/Mibs/Html/E/EATON-EPDU-MIB.php#outletWatts - better
PREFIX_WATTS="enterprises.534.6.6.7.6.5.1.3"
# 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

function init_globals_from_ip() {
    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_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}"

}

##### SSH

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

# 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 eaton-probe() {
    function help() {
        echo "$COMMAND: Retrieve the status and label of all outlets"
        echo "Usage: $COMMAND probe host_or_ip"
        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 eaton-status() {

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

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

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

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

    local status_integer=$(${SNMPGET} ${PREFIX_STATUS}.${chain}.${outlet} | snmp-extract-integer)
    local name=$(${SNMPGET} ${PREFIX_NAME}.${chain}.${outlet} | snmp-extract-string | remove-outlet)
    local watts=$(${SNMPGET} ${PREFIX_WATTS}.${chain}.${outlet} | snmp-extract-integer)
    case "$status_integer" in
        0)
            echo "chain-$chain@outlet-$outlet ($name): OFF"
            myexit 1
            ;;
        1)
            echo "chain-$chain@outlet-$outlet ($name): ON (${watts}W)"
            myexit 0
            ;;
        *)
            echo "Could not retrieve chain-$chain@outlet-$outlet ($name) status, returned $res"
            myexit 255
    esac
}

# factorized on and off

function -eaton-on-off() {

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

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

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

    local chain="$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 $chain\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")

    local MODE=$(tr 'a-z' 'A-Z' <<< $mode)
    if [[ "$res" -eq "$expected" ]]; then
        echo "chain-$chain@outlet-$outlet ($name): ${MODE}"
        myexit 0
    else
        echo "Error could not switch ${mode} chain-$chain@outlet-$outlet ($name), returns $res"
        myexit 255
    fi
}

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

function main() {
    function help() {
        echo "Usage: $0 <command> IP args..."
        echo "command among list status on off"
    }
    [[ "$#" -le 1 ]] && { help; exit 1; }
    local subcommand="$1"; shift
    PDU_IP="$1"; shift
    init_globals_from_ip

    local function="eaton-${subcommand}"
    $function "$@"
    rm -f $TMP
}

[[ "$1" == "--debug" ]] && {
    shift
    set -x
}
main "$@"
