#! /usr/bin/perl
###############################################################################
#                                                                             #
#                 Check an RFC1628-compliant UPS via SNMP                     #
#                              (Input tests)                                  #
#                                                                             #
###############################################################################
#
# Copyright (c) 2005 Simon Butcher <simon@butcher.name>
#  
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA    02111-1307    USA
#
##############################################################################
#
#
# Performance data:
#    +-------+-----------------------------------------------+
#    | Field | Description                                   |
#    +-------+-----------------------------------------------+
#    |   1   | Input Line Index                              |
#    |   2   | Input Frequency (Hertz)                       |
#    |   3   | Input Voltage (Volts)                         |
#    |   4   | Input Current (RMS Amps)                      |
#    |   5   | Input Power (Watts)                           |
#    +-------+-----------------------------------------------+
#
##############################################################################


use Net::SNMP;
use Getopt::Std;

$script = "check_snmp_ups_input";
$script_title =
  "Monitor the status of an RFC1628-compliant UPS via SNMP:\n" .
  "Input Monitoring";
$script_version = "20051203";

# SNMP options
$version = "1";
$timeout = 2;

# Various SNMP OID prefixes to look at..
$oid_ups_input_numlines = ".1.3.6.1.2.1.33.1.3.2";
$oid_ups_input_table    = ".1.3.6.1.2.1.33.1.3.3.1.";
$oid_ups_input_freq     = "2."; # 0.1 Hz
$oid_ups_input_voltage  = "3."; # Volts
$oid_ups_input_current  = "4."; # 0.1 RMS Amps
$oid_ups_input_power    = "5."; # true power, watts

# The information we returned
$ups_input_index = 1; # 0;
$ups_input_freq = 0;
$ups_input_voltage = 0;
$ups_input_current = 0;
$ups_input_power = 0;

# Thresholds
$critical_volt_max = -1;
$critical_volt_min = -1;
$critical_freq_max = -1;
$critical_freq_min = -1;
$warning_volt_max = -1;
$warning_volt_min = -1;
$warning_freq_max = -1;
$warning_freq_min = -1;

# Our return status - we start with 0 (OK) and hope for the best
$status = 0;

# Our return string with lots of interesting stuff in it
$returnstr = "";

# The SNMP hostname and community to use for the query
$hostname = "";
$community = "public";


# Grab the command line options
getopts("h:H:C:i:w:c:");

# If we didn't get any options, show some help and quit
if ($opt_h){
    usage();
    exit(-1); # Unknown
}

# Get the hostname, if it was given..
if (defined($opt_H)){
    $hostname = $opt_H;
} else {
    # We really need a hostname
    usage();
    exit(-1); # Unknown
}

# Get the SNMP community
if (defined($opt_C)){
    $community = $opt_C;
}

# Get the SNMP community
if (defined($opt_i)){
    $ups_input_index = $opt_i;

    # Make sure the index is at least 1..
    if ($ups_input_index < 1) {
	$ups_input_index = 1;
    }
}

# Grab the warning thresholds
if (defined($opt_w)) {
    my @warning = split(/\,/, $opt_w);

    if (!($warning[0] eq "")) {
	$warning_volt_min = $warning[0];
    }
    if (!($warning[1] eq "")) {
	$warning_volt_max = $warning[1];

	# Make sure this value is higher than the minimum
	if ($warning_volt_max <= $warning_volt_min) {
	    print "Warning voltage maximum is lower than the minimum!\n";
	    exit(-1); # Unknown
	}
    }

    if (!($warning[2] eq "")) {
	$warning_freq_min = $warning[2];
    }
    if (!($warning[3] eq "")) {
	$warning_freq_max = $warning[3];

	# Make sure this value is higher than the minimum
	if ($warning_freq_max <= $warning_freq_min) {
	    print "Warning freuency maximum is lower than the minimum!\n";
	    exit(-1); # Unknown
	}
    }
}

# Grab the critical thresholds
if (defined($opt_c)) {
    my @critical = split(/\,/, $opt_c);

    if (!($critical[0] eq "")) {
	$critical_volt_min = $critical[0];
    }
    if (!($critical[1] eq "")) {
	$critical_volt_max = $critical[1];

	# Make sure this value is higher than the minimum
	if ($critical_volt_max <= $critical_volt_min) {
	    print "Critical voltage maximum is lower than the minimum!\n";
	    exit(-1); # Unknown
	}
    }

    if (!($critical[2] eq "")) {
	$critical_freq_min = $critical[2];
    }
    if (!($critical[3] eq "")) {
	$critical_freq_max = $critical[3];

	# Make sure this value is higher than the minimum
	if ($critical_freq_max <= $critical_freq_min) {
	    print "Critical freuency maximum is lower than the minimum!\n";
	    exit(-1); # Unknown
	}
    }
}

# If we received both warning and critical thresholds, make sure the
# warning values don't contradict the critical values
if (($warning_volt_min != -1) &&
    ($critical_volt_min != -1) &&
    ($critical_volt_min >= $warning_volt_min)) {
    print "The critical voltage minimum must be lower than the warning minimum\n";
    exit(-1); # Unknown
}
if (($warning_volt_max != -1) &&
    ($critical_volt_max != -1) &&
    ($critical_volt_max <= $warning_volt_max)) {
    print "The warning voltage maximum must be lower than the critical maximum\n";
    exit(-1); # Unknown
}
if (($warning_freq_min != -1) &&
    ($critical_freq_min != -1) &&
    ($critical_freq_min >= $warning_freq_min)) {
    print "The critical frequency minimum must be lower than the warning minimum\n";
    exit(-1); # Unknown
}
if (($warning_freqq_max != -1) &&
    ($critical_freq_max != -1) &&
    ($critical_freq_max <= $warning_freq_max)) {
    print "The warning frequency maximum must be lower than the critical maximum\n";
    exit(-1); # Unknown
}


# Initialise the SNMP session via the Net::SNMP perl module
my ($snmp_session, $snmp_error) = Net::SNMP->session(
    -community => $community,
    -hostname => $hostname,
    -version => $version,
    -timeout => $timeout,
);

# Grab interesting details
check_device();
    
# Shut down the SNMP session
$snmp_session->close();

# Return the return string and return status
print "$returnstr\n";
exit($status);


##############################################################################
##############################################################################
#
# Change the status level
#
sub status {
    my $newstatus = $_[0];

    # If the new status is greater than the old status, change the old status
    if ($newstatus > $status) {
	$status = $newstatus;
    }
}


##############################################################################
##############################################################################
#
# Grab a value from snmp
#
sub grab_snmp_value {
    my $this_oid = $_[0];
    my $this_value = "";

    # Try to grab the OID's value, if it exists
    if (defined($snmp_session->get_request($this_oid))) {
        foreach ($snmp_session->var_bind_names()) {
            $this_value = $snmp_session->var_bind_list()->{$_};
        }
    }

    # Return the value we got..
    return $this_value;
}


##############################################################################
##############################################################################
#
# Grab lots of interesting info about the device
#
sub check_device {
    # Grab some values
    $ups_input_freq =
	grab_snmp_value($oid_ups_input_table .
			$oid_ups_input_freq .
			$ups_input_index) * 0.1;
    $ups_input_voltage =
	grab_snmp_value($oid_ups_input_table .
			$oid_ups_input_voltage .
			$ups_input_index);
    $ups_input_current = 
	grab_snmp_value($oid_ups_input_table .
			$oid_ups_input_current .
			$ups_input_index) * 0.1;
    $ups_input_power = 
	grab_snmp_value($oid_ups_input_table .
			$oid_ups_input_power .
			$ups_input_index);

    # Start the return string..
    $returnstr =
	"Input Line " . $ups_input_index . ":";

    # If we have the input voltage, add that
    if (!($ups_input_voltage eq "")) {
	# Have we broken a voltage threshold?
	if ((($critical_volt_min != -1) &&
	     ($ups_input_voltage < $critical_volt_min)) ||
	    (($critical_volt_max != -1) &&
	     ($ups_input_voltage > $critical_volt_max))) {
	    status(2); # Critical
	    $returnstr = "$returnstr Critical:";
	} elsif ((($warning_volt_min != -1) &&
		  ($ups_input_voltage < $warning_volt_min)) ||
		 (($warning_volt_max != -1) &&
		  ($ups_input_voltage > $warning_volt_max))) {
	    status(1); # Warning
	    $returnstr = "$returnstr Warning:";
	}
	
	# Mention the voltage level
	$returnstr =
	    "$returnstr " .
	    "$ups_input_voltage VAC";

	# If we have the input current, add that
	if (!($ups_input_current eq "") &&
	    ($ups_input_current != 0)) {
	    $returnstr =
		"$returnstr, " .
		"$ups_input_current\A";
	}

	# If we have the input frequency, add that
	if (!($ups_input_freq eq "") &&
	    ($ups_input_freq != 0)) {
	    $returnstr = "$returnstr,";

	    # Have we broken a frequency threshold?
	    if ((($critical_freq_min != -1) &&
		 ($ups_input_freq < $critical_freq_min)) ||
		(($critical_freq_max != -1) &&
		 ($ups_input_freq > $critical_freq_max))) {
		status(2); # Critical
		$returnstr = "$returnstr Critical:";
	    } elsif ((($warning_freq_min != -1) &&
		      ($ups_input_freq < $warning_freq_min)) ||
		     (($warning_freq_max != -1) &&
		      ($ups_input_freq > $warning_freq_max))) {
		status(1); # Warning
		$returnstr = "$returnstr Warning:";
	    }

	    # Mention the frequency..
	    $returnstr =
		"$returnstr " .
		"$ups_input_freq\Hz";
	}

	# If we have the input "true" power, add that
	if (!($ups_input_power eq "") &&
	    ($ups_input_power != 0)) {
	    $returnstr =
		"$returnstr, " .
		"$ups_input_power\W";
	}
    } else {
	# Complain
	print "Unable to obtain line input information\n";
	exit(-1); # Unknown
    }

    # Add on some performance monitoring stuff to the return string :)
    $returnstr =
	"$returnstr" .
	"|$ups_input_index;$ups_input_freq;$ups_input_voltage" .
	";$ups_input_current;$ups_input_power" .
	";";
}


##############################################################################
##############################################################################
#
# Usage information
#
sub usage {
    print << "USAGE";
$script ($script_version)
Copyright (c) 2005 Simon Butcher <simon@butcher.name>

$script_title
    
Usage: $script -H <hostname> [-C <community>] [-i <i>]
       [-w <vmin>,<vmax>,<fmin>,<fmax>]
       [-c <vmin>,<vmax>,<fmin>,<fmax>]

Options:
    -H 	Hostname or IP address of the UPS
    -C 	SNMP read community (default is $community)
    -i  Input index number (defaults to 1 if not set)
    -w  Warning level input voltage and frequency thresholds
    -c  Critical level input voltage and frequency thresholds
        For example: 220VAC +/- 10VAC, 50Hz +/- 2Hz: 210,230,48,52

USAGE
     exit(-1); # Unknown
}
