#!/usr/bin/perl
# upsniff, Boone, 05/27/99
# Sniff the status of the Matrix UPS via SNMP
#
# Modifications:
# 05/27/99 Boone      Initial coding
# 11/29/04 Boone      Newer versions (5.? and later) of the Perl SNMP
#                     module default to SNMPv3, but our UPS doesn't
#                     speak anything later than SNMPv1; specify the SNMP
#                     version in the session setup
# 04/30/08 Boone      Community string from command line
# 12/02/09 Boone      Default MIB dir seems to have changed at some version
#                     of the underlying SNMP kit; explicitly add both known
#                     options to the search list
# End Modifications

# Libraries

	use SNMP 1.8;

# Initialize

	$host = shift(@ARGV);
	$comm = shift(@ARGV);

	%varlist =
	(												# We want:
		"upsAdvBatteryCapacity.0", "10",			# At least 10%
		"upsAdvBatteryNumOfBadBattPacks.0", "0",	# No bad ones
		"upsAdvBatteryNumOfBattPacks.0", "1",		# This box only has one
		"upsAdvBatteryReplaceIndicator.0", "noBatteryNeedsReplacing",
													# No bad ones please
		"upsAdvBatteryRunTimeRemaining.0", "30000",	# >5 minutes (centiseconds)
		"upsAdvBatteryTemperature.0", "35",			# <35C
		"upsAdvInputFrequency.0", "60",				# 60Hz or it is weird
		"upsAdvInputLineFailCause.0", "selfTest",	# For alerts/logs if probs
		"upsAdvInputLineVoltage.0", "208",			# 208V nominal +/- 5%
		"upsAdvOutputCurrent.0", "40",				# Max 40A or so
		"upsAdvOutputFrequency.0", "60",			# 60Hz or it is weird
		"upsAdvOutputLoad.0", "95",					# 95% for some margin
		"upsAdvOutputVoltage.0", "205",				# 
		"upsBasicBatteryStatus.0", "batteryNormal",	# Normal please
		"upsBasicBatteryTimeOnBattery.0", "0",		# For alerts/logs if non-0
		"upsBasicInputPhase.0", "1",				# 
		"upsBasicOutputPhase.0", "1",				#
		"upsBasicOutputStatus.0", "onLine",			# For alerts/logs !onLine
		"upsBasicBatteryTimeOnBattery.0", "0",		# We prefer line power
		"upsCommStatus.0", "ok",					# else we can't sniff
	);

	%outstatus =									# status string in 1st field
	(												# 1=prob 0=ok in 2nd field
		"unknown", 1,								# Control freak :-)
		"onLine", 0,								# We want this
		"onBattery", 0,								# Dealt with separately
		"onSmartBoost", 1,							# Bad power is a problem
		"timedSleeping", 1,							# Shouldn't be in this mode
		"softwareBypass", 1,						# or this one
		"off", 1,									# or this one
		"rebooting", 1,								# or this one
		"switchedBypass", 1,						# or this one
		"hardwareFailureBypass", 1,					# s.b. detected other ways
		"sleepingUntilPowerReturn", 0,				# Yes, we know it's out
	);

	$rephw = "HardwareOK";
	$repst = "StatusOK";

# Set up connection

	$SNMP::debugging = 0;
	$SNMP::use_enums = 1;
	&SNMP::addMibDirs("/usr/share/snmp/mibs", "/usr/share/mibs");
	&SNMP::initMib();
	&SNMP::loadModules("PowerNet-MIB");
	$sess = new SNMP::Session(DestHost => $host, Community => $comm,
		Version => 1) ||
		die "new session failed: " . $sess -> {ErrorStr};

# Extract items

	foreach $var (keys(%varlist))
	{
		$value = $sess -> get($var);
		if ($sess -> {ErrorStr})
		{
			die "get failed: " . $sess->{ErrorStr};
		}
		$result{$var} = $value;
	}

# Hardware functionality

	if ($result{"upsCommStatus.0"} != "ok")
	{
		push(@problist, "UPS <-> SNMP adapter communication failure");
		print "HardwareERROR\n";
		print join("\n", @problist), "\n";
		exit(1);
	}

	if ($result{"upsAdvBatteryReplaceIndicator.0"} != "noBatteryNeedsReplacing")
	{
		if ($result{"upsAdvBatteryNumOfBadBattPacks.0"} != 0)
		{
			$hwprobs = 1;
			$rephw = "HardwareError";
			push(@problist, "$result batteries need to be replaced");
		}
	}

	$badcode = $outstatus{$result{"upsBasicOutputStatus.0"}};
	if ($badcode)
	{
		$statusprobs = 1;
		$repst = "StatusERROR";
		push(@problist, "output status $result{'upsBasicOutputStatus.0'}");
	}

# On battery?

	if ($result{"upsBasicOutputStatus"} eq "onBattery")
	{
		$onbattery = 1;
		$timeonbatt = &tick2min($result{"upsBasicBatteryTimeOnBattery.0"});
		$runremain = &tick2min($result{"upsAdvBatteryRunTimeRemaining.0"});
		$pctcap = $result{"upsAdvBatteryCapacity.0"};
	}

# Report

	if (! $onbattery && ! $hwprobs && ! $statusprobs)
	{
		print $rephw, " ", $repst, "\n";
		exit(0);
	}
	else
	{
		if (! $onbattery)
		{
			print join(" ", $rephw, $repst), "\n";
			print join("\n", @problist), "\n";
		}
		else
		{
			print join(" ", $rephw, $repst, "onBattery", $timeonbatt,
				$runremain, $pctcap), "\n";
			print join("\n", @problist), "\n";
		}
		exit(1);
	}

# Subroutines

	sub tick2min
	{
		local($ticks) = shift(@_);

		$return = $ticks / 60000;
	}

__END__

upsAdvBatteryCapacity.0 = 100
upsAdvBatteryNumOfBadBattPacks.0 = 0
upsAdvBatteryNumOfBattPacks.0 = 1
upsAdvBatteryReplaceIndicator.0 = noBatteryNeedsReplacing
upsAdvBatteryRunTimeRemaining.0 = 6000000
upsAdvBatteryTemperature.0 = 20
upsAdvInputFrequency.0 = 60
upsAdvInputLineFailCause.0 = selfTest
upsAdvInputLineVoltage.0 = 209
upsAdvInputMaxLineVoltage.0 = 209
upsAdvInputMinLineVoltage.0 = 209
upsAdvOutputCurrent.0 = 0
upsAdvOutputFrequency.0 = 60
upsAdvOutputLoad.0 = 0
upsAdvOutputVoltage.0 = 203
upsBasicBatteryStatus.0 = batteryNormal
upsBasicBatteryTimeOnBattery.0 = 0
upsBasicInputPhase.0 = 1
upsBasicOutputPhase.0 = 1
upsBasicOutputStatus.0 = onLine
upsCommStatus.0 = ok
