#!/usr/bin/perl -w

use Net::SNMP qw(:snmp);
use strict;
use Getopt::Std;

sub set_status($$);

$! = 1;

#
# Temperature limit, and health status
#
my %_health_stat = (
	255	=> 'OK',
	4	=> 'WARNING',
	2	=> 'WARNING',
	0	=> 'CRITICAL',
);
my %_health_stat_lang = (
	255	=> 'Normal Operation',
	4	=> 'System-Level failure, Operating',
	2	=> 'Non-Critical failure, Operating',
	0	=> 'Critical failure!',
);

#
# Test numbers, retries and high temperature alert
#
my $retries = 3;
#my $_ambient_temp_high	= 30;

#
# Exit states
#
my $return_val = 'UNKNOWN';
my %EXIT_VAL = (
	'OK'            => 0,
	'WARNING'       => 1,
	'CRITICAL'      => 2,
	'UNKNOWN'       => 3,
	'DEPENDANT'     => 4
);

#
# Set up the usable variables and default values
#
my $SNMP_DEBUG		= 0;
my $SNMP_COMMUNITY;
my $SNMP_HOST;
my $TEMP_HIGH;

#
# Code
#
my %_OPTS;
getopts("c:h:t:", \%_OPTS);
$SNMP_COMMUNITY	= $_OPTS{'c'} || '';
$SNMP_HOST	= $_OPTS{'h'} || '';
$TEMP_HIGH	= $_OPTS{'t'} || 30;

if ($SNMP_COMMUNITY eq '' || $SNMP_HOST eq '') {
	printf("%s: Invalid arguments passed. -c <community> -h <hostname>\n", $return_val);
	exit($EXIT_VAL{$return_val});
}

#
# Strip off any domains, just leave us with a short-name
#
if ($SNMP_HOST =~ /^([^\.]+)\./) {
	$SNMP_HOST = $1;
}

#
# Pre-pend 'rsa-' if it doesn't already exist
#
if ($SNMP_HOST !~ /^rsa-/) {
	$SNMP_HOST = 'rsa-' . $SNMP_HOST;
}

#
# OID's to be queried
#
my %MACH_OIDS = (
	'rsa_machine_type'		=> 	'.1.3.6.1.4.1.2.3.51.1.2.21.2.1.1.0',		# machineLevelVpdMachineType
	'imm_machine_model'		=> 	'.1.3.6.1.4.1.2.3.51.3.1.5.2.1.2.0',		# machineLevelVpdMachineModel
);

my %IMM_OIDS = (
	'Temperature'			=>	'.1.3.6.1.4.1.2.3.51.3.1.1.2.1.3.1',		# tempReading.1
	'immVpdVersionString.1'		=>	'.1.3.6.1.4.1.2.3.51.3.1.5.1.1.3.1',
	'systemHealthStat'		=>	'.1.3.6.1.4.1.2.3.51.3.1.4.1.0',
);

my %RSA_OIDS = (
	'systemHealthStat'		=>	'.1.3.6.1.4.1.2.3.51.1.2.7.1.0',
	'biosVpdBuildLevelName'		=>	'.1.3.6.1.4.1.2.3.51.1.2.21.1.1.2.0',
	'firmwareVpdBuildId.boot'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.6.1.3.1',
	'firmwareVpdBuildId.main'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.6.1.3.2',
);

my %RSA_EVENT = (
	'eventLogEntires'	=>	[
		'.1.3.6.1.4.1.2.3.51.1.3.4.2.1.1',					# eventLogIndex
		'.1.3.6.1.4.1.2.3.51.1.3.4.2.1.2',					# eventLogString
	],
	'eventBase'	=> '.1.3.6.1.4.1.2.3.51.1.3.4.2.1',				# eventLogTable
);

my %IMM_EVENT = (
	'eventLogEntires'	=>	[
		'.1.3.6.1.4.1.2.3.51.3.2.1.1.1.1',					# eventLogIndex
		'.1.3.6.1.4.1.2.3.51.3.2.1.1.1.2',					# eventLogString
		'.1.3.6.1.4.1.2.3.51.3.2.1.1.1.3',					# eventLogSeverity
		'.1.3.6.1.4.1.2.3.51.3.2.1.1.1.4',					# eventLogDate
		'.1.3.6.1.4.1.2.3.51.3.2.1.1.1.5',					# eventLogTime
	],
	'eventBase'	=> '.1.3.6.1.4.1.2.3.51.3.2.1.1.1',				# eventLogTable
);

# my %PerMachineLimits = (
# 	'8849'	=>  {
# 		'ambient_temp'	=>	35
# 	}
# );

my %PerMachineOids = (
#
# 306m
#
	'8849'	=>  {
		'OIDS'	=> {
			%RSA_OIDS,
			'Temperature'	=>	'.1.3.6.1.4.1.2.3.51.1.2.1.1.1.0',		# ccTemp
		},
		%RSA_EVENT,
	},
#
# 3650
#
	'7979'	=>  {
		'OIDS'	=> {
			%RSA_OIDS,
			'Temperature'	=>	'.1.3.6.1.4.1.2.3.51.1.2.1.5.1.0',		# systemAmbientTemp
			'ismpBuildID'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.1.19.2.0',
		},
		%RSA_EVENT,
	},
#
# 3550
#
	'7978'	=>  {
		'OIDS'	=> {
			%RSA_OIDS,
			'Temperature'	=>	'.1.3.6.1.4.1.2.3.51.1.2.1.5.1.0',		# systemAmbientTemp
			'ismpBuildID'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.1.19.2.0',
		},
		%RSA_EVENT,
	},
#
# Development needs fixing
#
	'71M7'	=>  {
		'OIDS'	=> {
			%RSA_OIDS,
			'Temperature'	=>	'.1.3.6.1.4.1.2.3.51.1.2.1.5.1.0',		# systemAmbientTemp
			'ismpBuildID'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.1.19.2.0',
		},
		%RSA_EVENT,
	},
#
# So does web-app1
#
	'4444'	=>  {
		'OIDS'	=> {
			%RSA_OIDS,
			'Temperature'	=>	'.1.3.6.1.4.1.2.3.51.1.2.1.5.1.0',		# systemAmbientTemp
			'ismpBuildID'	=>	'.1.3.6.1.4.1.2.3.51.1.2.21.1.19.2.0',
		},
		%RSA_EVENT,
	},
#
# New M2 class machines with IMM instead of RSA
#
# 3560M2
#
	'794792M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},
#
# 3550M2
#
	'794652M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},
#
# AJM: added
# 3550M3
#
	'7944D2M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},

#
# 3650M3
#
	'7945H2M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},
	'7945C2M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},
	'7945D2M'	=> {
		'OIDS'	=> {
			%IMM_OIDS,
		},
		%IMM_EVENT,
	},
);

#
# Start the SNMP session
#
my ($session, $error) = Net::SNMP->session(
	-version	=> 1,
	-nonblocking	=> 0,
	-hostname	=> $SNMP_HOST,
	-community	=> $SNMP_COMMUNITY,
	-port		=> 161,
	-timeout	=> 3,
	-debug		=> $SNMP_DEBUG,
);

#
# Now do the work
#
if (!defined($session)) {
	printf("%s: %s.\n", $return_val, $error);
	exit($EXIT_VAL{$return_val});
}

#
# Stack the OID's we care aboyt, from %OIDS
#
my $result0;
my $run = 0;
my $machine_type = '';
while ($machine_type eq '' && $run < $retries) {
	foreach my $key (keys %MACH_OIDS) {
	#	push(@OID_REQUEST, $OIDS{$key});
		eval {
			$result0 = $session->get_request(-varbindlist => [$MACH_OIDS{$key}]);
		};

		$machine_type = $result0->{$MACH_OIDS{$key}};

		if (defined($machine_type) && $machine_type ne '') {
			last;
		} else {
			$machine_type = '';
		}
	}
	$run++;
}

if ($machine_type eq '') {
	printf("CRITICAL: Unable to get machine type.\n");
	exit($EXIT_VAL{'CRITICAL'});
}
if (!defined($PerMachineOids{$machine_type})) {
	printf("UNKNOWN: Unknown machine type. ($machine_type)\n");
	exit($EXIT_VAL{'UNKNOWN'});
}

my %MIXED_OIDS = (
	%{$PerMachineOids{$machine_type}{'OIDS'}},
);
	
my @eventLogEntries	= $PerMachineOids{$machine_type}{'eventLogEntires'};
my $eventBase		= $PerMachineOids{$machine_type}{'eventBase'};

#
# Query the SNMP device, getting all the OID's at once.  Set up a retry loop incase it fails the first time.
#
my @OID_REQUEST;
foreach my $key (keys %{$PerMachineOids{$machine_type}{'OIDS'}}) {
	push(@OID_REQUEST, $PerMachineOids{$machine_type}{'OIDS'}{$key});
}

my $result1;
$run = 0;
while (!defined($result1)) {
	if ($run > 0) {
#
# Sleep for a short while between retries
#
		select(undef, undef, undef, 0.250 * $run);
	}
	if ($run > $retries) {
		printf("CRITICAL: Failed to obtain RSA Information after %d attempts, giving up.\n", $run);
		exit($EXIT_VAL{'CRITICAL'});
	}
	eval {
		$result1 = $session->get_request(-varbindlist => [@OID_REQUEST]);
	};
	$run++;
}

#
# Query the SNMP device, getting the entore event log in one hit.
#
my $t_result;
$run = 0;
while (!$t_result) {
	if ($run > 0) {
#
# Sleep for a short while between retries
#
		select(undef, undef, undef, 0.250 * $run);
	}
	if ($run > $retries)
	{
		printf("CRITICAL: Failed to obtain Event Log information from the RSA RSA after %d attempts, giving up.\n", $run);
		exit($EXIT_VAL{'CRITICAL'});
	}
	eval {
		$t_result = $session->get_entries(
			-columns	=> @eventLogEntries,
			-endindex	=> 50,
		);
	};
	$run++;
}

#
# Ok, $t_result is now a hash with multiple base key children..
#
#	${$eventBase}.1 is always the index
#	${$eventBase}.2 is always the log entry (may include the severity)
#
# The rest don't necessarily exist..
#	${$eventBase}.3 is the severity
#	${$eventBase}.4 is the date
#	${$eventBase}.5 is the time
#
# We cheat.  Increase a loop, looking for the strings.  If it exists, get the
# LogEntry.  If a severity exists, use that instead of the LogEntry.
#
# Loopingn through the index (.1.) was unreliable ont he RSAII's for some reason.

my @log_entries;
$run = 1;
while (defined($t_result->{$eventBase . '.2.' . $run})) {
#
# 0 error
# 1 warning
# 2 informational
# 3 other
#
	my $log = 0;
	my $entry;
	if (defined($t_result->{$eventBase . '.2.' . $run})) {
		$entry = $t_result->{$eventBase . '.2.' . $run};
	} else {
		last;
	}
	if (defined($t_result->{$eventBase . '.3.' . $run})) {
		if ($t_result->{$eventBase . '.3.' . $run} == 0) {
			$return_val = set_status($return_val, 'CRITICAL');
			$log++;
		} elsif ($t_result->{$eventBase . '.3.' . $run} == 1) {
			$return_val = set_status($return_val, 'WARNING');
			$log++;
		}
	} else {
		if ($entry =~ /Severity:WARN/) {
			$return_val = set_status($return_val, 'WARNING');
			$log++;
		} elsif ($entry =~ /Severity:ERR/) {
			$return_val = set_status($return_val, 'CRITICAL');
			$log++;
		}
	}
	if ($log > 0) {
		push @log_entries, $entry;
	}
			
	$run++;
}

# #
# # Pull out the temperature value (decimal, followed by 'Centigrade')
# #
# if (defined $PerMachineLimits{$machine_type}{'ambient_temp'}) {
# 	$_ambient_temp_high	= $PerMachineLimits{$machine_type}{'ambient_temp'};
# }
my $sys_temp = 0;
my $use_temp;
if (defined($MIXED_OIDS{"Temperature"})) {
	$use_temp =  $result1->{$MIXED_OIDS{"Temperature"}};
}

if ($use_temp =~ /([\d.-]+)(?: Centigrade)?/) {
 	$sys_temp = $1;
}

if ($sys_temp == 0 || $sys_temp > $TEMP_HIGH) {
	$return_val	= 'CRITICAL';
}
	
if ($return_val eq 'UNKNOWN') {
	$return_val = set_status($return_val, $_health_stat{$result1->{$MIXED_OIDS{"systemHealthStat"}}});
}

printf("%s: State: %s, Temperature: %sC (%sC), Bad log entries: %d\n", $return_val, $_health_stat_lang{$result1->{$MIXED_OIDS{"systemHealthStat"}}}, $sys_temp, $TEMP_HIGH,  scalar @log_entries);
exit($EXIT_VAL{$return_val});


1;

sub set_status($$) {
	my $current = shift;
	my $new	    = shift;
	if ($current eq 'CRITICAL') {
		return $current;
	}
	if ($new eq 'CRITICAL') {
		return $new;
	}
	if ($new eq 'WARNING') {
		return $new;
	}
	return $new;
}
