#!/usr/bin/python
#
# check_ups_socomec
# Check Socomec UPS (via NetVision network interface) health
# Author: Angelo Conforti <angeloxx@angeloxx.it>
# Version: 0.2
#
# Changelog:
# 0.1 @ 07.10.01 - First realease
# 0.2 @ 08.02.14 - Added support to UPS's Alarms
#
# Warning
#   - if Input Voltage is lower than --voltage
#   - if Load % is more than --load
#
# Critical
#   - if battery capacity il lower than --battery
#

import sys, threading, StringIO
from optparse import OptionParser
from pysnmp import asn1, v1, v2c
from pysnmp import role
import pysnmp

#
# Definizione del dizionario che contiene la corrispondenza Alert <-> Messaggio
# 

snmpalerts_alarms = ""
snmpalerts_root = '.1.3.6.1.4.1.4555.1.1.1.1.6.3'
snmpalerts_leaf = {
 '1': 'AlarmBatteryBad',
 '2': 'AlarmOnBattery',
 '3': 'AlarmLowBattery',
 '4': 'AlarmDepletedBattery',
 '5': 'AlarmTempBad',
 '6': 'AlarmInputBad',
 '7': 'AlarmOutputBad',
 '8': 'AlarmOutputOverload',
 '9': 'AlarmOnBypass',
 '10': 'AlarmBypassBad',
 '11': 'AlarmOutputOffAsRequested',
 '12': 'AlarmUpsOffAsRequested',
 '13': 'AlarmChargerFailed',
 '14': 'AlarmUpsOutputOff',
 '15': 'AlarmUpsSystemOff',
 '16': 'AlarmFanFailure',
 '17': 'AlarmFuseFailure',
 '18': 'AlarmGeneralFault',
 '19': 'AlarmDiagnosticTestFailed',
 '20': 'AlarmCommunicationLost',
 '21': 'AlarmAwaitingPower',
 '22': 'AlarmShutdownPending',
 '23': 'AlarmShutdownImminent',
 '24': 'AlarmTestInProgress',
 '25': 'AlarmPowerSupplyFault',
 '26': 'AlarmAuxMainFail',
 '27': 'AlarmManualBypassClose',
 '28': 'AlarmShortCircuit',
 '29': 'AlarmBatteryChargerFailure',
 '30': 'AlarmInverterOverCurrent',
 '31': 'AlarmInverterDistorsion',
 '32': 'AlarmPrechargeVoltageFail',
 '33': 'AlarmBoostTooLow',
 '34': 'AlarmBoostTooHigh',
 '35': 'AlarmBatteryTooHigh',
 '36': 'AlarmImproperCondition',
 '37': 'AlarmOverloadTimeout',
 '38': 'AlarmControlSystemFailure',
 '39': 'AlarmDataCorrupted',
 '40': 'AlarmPllFault',
 '41': 'AlarmInputGeneralAlarm',
 '42': 'AlarmRectifierGeneralAlarm',
 '43': 'AlarmBoostGeneralAlarm',
 '44': 'AlarmInverterGeneralAlarm',
 '45': 'AlarmBatteryGeneralAlarm',
 '46': 'AlarmOutputOver',
 '47': 'AlarmOutputUnder',
 '48': 'AlarmBypassGeneralAlarm',
 '49': 'AlarmStopForOverload',
 '50': 'AlarmImminentStop',
 '51': 'AlarmModule1Alarm',
 '52': 'AlarmModule2Alarm',
 '53': 'AlarmModule3Alarm',
 '54': 'AlarmModule4Alarm',
 '55': 'AlarmModule5Alarm',
 '56': 'AlarmModule6Alarm',
 '57': 'AlarmExternalAlarm1',
 '58': 'AlarmExternalAlarm2',
 '59': 'AlarmExternalAlarm3',
 '60': 'AlarmExternalAlarm4',
 '61': 'AlarmEService',
 '62': 'AlarmRedundancyLost',
 '63': 'AlarmPeriodicServiceCheck',
 '64': 'AlarmAllTransferDisabled',
 '65': 'AlarmAutoTransferDisabled',
 '66': 'AlarmBatteryRoom',
 '67': 'AlarmManualBypass',
 '68': 'AlarmBatteryDischarged',
 '69': 'AlarmInsufficientResources',
 '70': 'AlarmOptionalBoards',
 '71': 'AlarmRectifierFault',
 '72': 'AlarmBoostFault',
 '73': 'AlarmInverterFault',
 '74': 'AlarmParallelModuleFault',
 '75': 'AlarmGenSetGeneral',
 '76': 'AlarmGenSetFault',
 '77': 'AlarmEmergencyStopActive',
 '78': 'AlarmBatteryCircuitOpen',
 '79': 'AlarmFansFailure',
 '80': 'AlarmPhaseRotationFault',
 '81': 'AlarmA62',
 '82': 'AlarmA63'
}

def snmpquery(host,qry):
    client = role.manager((host, 161))
    req = eval('v1').GETREQUEST()
    rsp = eval('v1').GETRESPONSE()

    try:
        (answer, src) = client.send_and_receive(req.encode(community='public', encoded_oids=map(asn1.OBJECTID().encode, [qry])))
    except role.NetworkError:
        print "KO: Unable to contact or resolve name of remote SensorProbe"
        sys.exit(NAGIOS_CRITICAL)

    # Decode SNMP response
    rsp.decode(answer)
    vals = map(lambda x: x[0](), map(asn1.decode, rsp['encoded_vals']))
    return vals[0]

NAGIOS_OK = 0
NAGIOS_WARNING = 1
NAGIOS_CRITICAL = 2
NAGIOS_UNKNOWN = 3

uso = "%prog -r host --battery 50 --voltage 200 --load 90"
parser = OptionParser(uso)
parser.add_option("-r", "--host",      dest="host",      help="host", default="")
parser.add_option("-c", "--community", dest="community", help="community SNMP", default="public")
parser.add_option("-b", "--battery",   dest="battery",   help="soglia di crit per valore di carica", default='50')
parser.add_option("-l", "--load",      dest="load",      help="soglia di warn per valore di carico", default='90')
parser.add_option("-v", "--voltage",   dest="voltage",   help="soglia di warn per valore di voltage", default='200')


(options, args) = parser.parse_args()
if not options.host:
    parser.error("option host is mandatory")
    sys.exit(NAGIOS_UNKNOWN)


capacity = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.2.4.0");
load1 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.1");
load2 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.2");
load3 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.3");
voltage1 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.2.1") / 10;
voltage2 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.2.2") / 10;
voltage3 = snmpquery(options.host,".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.2.3") / 10;

for k,v in snmpalerts_leaf.iteritems():
	read = snmpquery(options.host,snmpalerts_root + "."+  k + ".0");
	if read == 1:
		snmpalerts_alarms = snmpalerts_alarms + v + " "

if snmpalerts_alarms == '':
	snmpalerts_alarms = 'None' 
	
## Si potrebbe comprimere e di molto il programma, qui...
values = "Battery %s - Load %s,%s,%s - Input Voltage %s,%s,%s - Alarms %s" % (capacity,load1,load2,load3,voltage1,voltage2,voltage3,snmpalerts_alarms)

if  capacity <= int(options.battery):
    print "CRIT: " + values
    sys.exit(NAGIOS_CRITICAL)

if  voltage1 <= int(options.voltage) or voltage2 <= int(options.voltage) or voltage3 <= int(options.voltage):
    print "WARN1: " + values
    sys.exit(NAGIOS_WARNING)

if  load1 >= int(options.load) or load2 >= int(options.load) or load3>= int(options.load):
    print "WARN2: " + values
    sys.exit(NAGIOS_WARNING)
    
if snmpalerts_alarms != 'None':
    print "CRIT: " + values
    sys.exit(NAGIOS_CRITICAL)
	
    

print "OK: " + values
sys.exit(NAGIOS_OK)
