#! /usr/bin/perl -w

# ups module: Supports APC and Liebert

# Version: $Revision: 0.15 $
# Author: Matt Selsky
# Date: $Date: 2007/02/11 02:07:50 $
#
# Based on the hplj module by Mark Benvenuto
#
# Copyright (c) 2003 - 2007
# The Trustees of Columbia University in the City of New York
# Academic Information Systems
# 
# License restrictions apply, see doc/license.html for details.

use strict;
use FindBin;
use lib "$FindBin::Bin/../common";
use Survivor::Check;

use SNMP;

my %argfmt = ('community' => 'optional string default(public)',
	      'replaceBattery' => 'optional boolean default(true)',
	      'timeOnBattery' => 'optional boolean default(true)',
	      'snmpversion' => 'optional string one(1,2,2c,3) default(1)',
	      'snmptimeout' => 'optional number between(1,inf) default(5)');

my $sc = new Survivor::Check();

# Disable threading pending threadsafe SNMP.
$sc->UseThreads(0);

$sc->GetOpt(\%argfmt, \&UpsValidate);

unless(	
	$sc->Arg('replaceBattery') || 
	$sc->Arg('timeOnBattery')
	) {
    $sc->ExitError(MODEXEC_MISCONFIG, 0,
		   "Must specify at least one of " .
		   "'replaceBattery' and 'timeOnBattery'");
}

# Calculate timeout in microseconds

my $snmptimeout = $sc->Arg('snmptimeout') * 1000000;

my $r = $sc->Exec( \&UpsCheck);

exit($r);

sub UpsValidate {
    return(MODEXEC_OK, 0, 'Ups OK');
}

sub UpsCheck {
    my $self = shift;
    my $host = shift;
    my $problem = 0;

    my $sess = new SNMP::Session( DestHost => $host,
				  Community => $self->Arg('community'),
				  UseLongNames => 1,
				  UseNumeric => 1,
				  Version => $sc->Arg("snmpversion"),
				  Timeout => $snmptimeout );

    if(!defined $sess) {
	# should this be problem instead? XXX
	return(MODEXEC_WARNING, 0, 'Unable to resolve hostname');
    }

    my $vendor = $sess->get('.1.3.6.1.2.1.1.2.0'); # sysObjectID

    return(MODEXEC_WARNING, 1, 'No response to SNMP query')
	unless (defined $vendor);

    return(MODEXEC_WARNING, 1, 'Unexpected empty response to SNMP query')
	unless ($vendor ne "");

    my ($vars);
    if ($vendor =~ /476\.1\.42/) {
	$vendor = 'Liebert';
	$vars = new SNMP::VarList( # Any Alarms?  On battery power?
				  ['.1.3.6.1.2.1.33.1.6.1.0'],
				  ['.1.3.6.1.2.1.33.1.2.2.0'], 
				  );	
    } else {
	$vendor = 'APC'; # Assume
	
	# The first MIB is:
	#
	# upsAdvBatteryReplaceIndicator OBJECT-TYPE
	#   SYNTAX INTEGER  {
	#      noBatteryNeedsReplacing(1),
	#      batteryNeedsReplacing(2)

	$vars = new SNMP::VarList( # Change battery?  On battery power?
				  ['.1.3.6.1.4.1.318.1.1.1.2.2.4.0'],
				  ['.1.3.6.1.4.1.318.1.1.1.2.1.2.0'], 
				  );	
    }

    my @val = $sess->get($vars);

    foreach my $v (@val) {
	# snmp failed; whine about it
	if(!defined $v) {
	    return(MODEXEC_WARNING, 1, 'No response to SNMP query');
	} elsif($v eq "") {
	    return(MODEXEC_WARNING, 1, 'Unexpected empty response to SNMP query');
	}
    }

    # Replace battery?
    my @errmsg;

    if ($vendor eq 'Liebert') {
	if( $val[0] > 0 && $self->Arg('replaceBattery') == 1 ) {
	    # There is *some* alarm on the battery
	    push ( @errmsg, 'Battery has alarm' );
	}
    } else {
	if( $val[0] == 2 && $self->Arg('replaceBattery') == 1 ) {
	    push ( @errmsg, 'Battery needs replacing' );
	}
    }

    # How long on battery?
    if( $val[1] > 0 && $self->Arg('timeOnBattery') == 1 ) {

	# been on battery for some time
	# Liebert returns seconds
	# APC returns timeticks, whatever that means

	my $n;

    	if ($vendor eq 'Liebert') {
		# Convert seconds to minutes and round
		$n = int(($val[1] / 60) + .5);
	} else {
		# Convert timeticks to minutes and round
		$n = int(($val[1] / 6000) + .5);
	}

	push ( @errmsg, 'On battery for '. $n .' minutes' );
	$problem = 1;
    }

    if ($problem) { # a *problem*?
	return(MODEXEC_PROBLEM, scalar @errmsg, join(', ', @errmsg) );
    } elsif(@errmsg) {     # We have some error
	return(MODEXEC_WARNING, scalar @errmsg, join(', ', @errmsg) );
    } else {          # all clear
	return(MODEXEC_OK, 0, 'No problems detected');
    }
}
