#!/usr/bin/perl -w
################# check_pixfailover #################
# Date : 24 Feb 2010
# Author: Richard Yates (rjwyates at gmail.com)
# Licence : GPL - http://www.fsf.org/licenses/gpl.txt
# Help : ./check_pixfailover.pl -h
#############################################

# check_pixfailover version:
my $chkver='1.00';

# Libraries to use
use strict;
use Net::SNMP;
use Getopt::Long;

# Nagios specific libraries
use lib '/usr/local/libexec/nagios';
use utils qw(%ERRORS $TIMEOUT &print_revision &support);
use vars qw($PROGNAME $exitstring $opt_H $opt_C $opt_p $opt_h $opt_d $opt_l
	$opt_x $opt_X $opt_L $opt_V $opt_2 $opt_P $opt_A $opt_t);

# SNMP Data
my $oidcfwHardwareStatusValuePrimary = "1.3.6.1.4.1.9.9.147.1.2.1.1.1.3.6";
my $oidcfwHardwareStatusValueSecondary = "1.3.6.1.4.1.9.9.147.1.2.1.1.1.3.7";
my @oidlists = ($oidcfwHardwareStatusValuePrimary, $oidcfwHardwareStatusValueSecondary);
my @snmpdesc = ('Invalid', 'Other', 'Up', 'Down', 'Error', 'OverTemp', 'Busy', 'NoMedia', 'Backup', 'Active', 'Standby');
my @snmpstate = (3, 2, 1, 2, 2, 2, 1, 2, 1, 0, 0);

# Other required variables
my ($session,$error,$i,$resultat,$authproto,$privproto,@v3proto);
my $exit_val = undef;
my @statusid = undef;
my @statusdesc = undef;
my @statusstate = undef;


#############################################
### Main Program
#############################################

# First get all command line options
Getopt::Long::Configure ("bundling");
if (!GetOptions('d'	=> \$opt_d,	'debug'		=> \$opt_d,
		'h'     => \$opt_h,	'help'		=> \$opt_h,
		'H:s'   => \$opt_H,	'hostname:s'	=> \$opt_H,
		'p:i'   => \$opt_p,	'port:i'	=> \$opt_p,
		'C:s'   => \$opt_C,	'community:s'	=> \$opt_C,
		'A:s'	=> \$opt_A,	'acceptable:s'	=> \$opt_A,
		'l:s'	=> \$opt_l,	'login:s'	=> \$opt_l,
		'x:s'	=> \$opt_x,	'passwd:s'	=> \$opt_x,
		'X:s'	=> \$opt_X,	'privpass:s'	=> \$opt_X,
		'L:s'	=> \$opt_L,	'protocols:s'	=> \$opt_L,   
		't:i'   => \$opt_t,	'timeout:i'	=> \$opt_t,
		'V'	=> \$opt_V,	'version'	=> \$opt_V,
		'2'     => \$opt_2,	'v2c'		=> \$opt_2,
		'P:s'	=> \$opt_P,	'prevres:s'	=> \$opt_P)) {
	print "FAILOVER UNKNOWN: Error processing command line options\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

# Basic options checks
if (defined($opt_t) && (($opt_t =~ /^[+-]?\d+$/) || ($opt_t < 1) || ($opt_t > 60))) {
	print "Timeout must be between 1 and 60!\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

$TIMEOUT = $opt_t ? $opt_t : $TIMEOUT;

if (defined ($opt_h)) {
	print_help ();
	exit $ERRORS{'UNKNOWN'};
}

if (defined($opt_V)) {
	print_version ();
	exit $ERRORS{'UNKNOWN'};
}

if (!defined($opt_H)) {
	print "You must include the host to check.\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

if (defined($opt_A) && !(($opt_A eq "aa") || ($opt_A eq "as"))) {
	print "Acceptable result must be either 'aa' or 'as'.\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

# SNMP login options checks
if (!defined($opt_C) && (!defined($opt_l) || !defined($opt_x))) {
	print "You have to include SNMP login information or community string.\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

if ((defined($opt_l) || defined($opt_x)) && (defined($opt_C) || defined($opt_2))) {
	print "You cannot mix snmp v1,2c and 3 protocols.\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'};
}

if (defined ($opt_L)) {
	if (!defined($opt_l)) {
		print "Put snmp V3 login info with protocols.\n";
		print_usage ();
		exit $ERRORS{'UNKNOWN'};
	};
	@v3proto = split(/,/,$opt_L);
	if ((defined ($v3proto[0])) && ($v3proto[0] ne "")) {
		$authproto = $v3proto[0];
	}
	if (defined ($v3proto[1])) {
		$privproto = $v3proto[1];
	}
	if ((defined ($v3proto[1])) && (!defined($opt_X))) {
		print "Put snmp V3 priv login info with priv protocols.\n";
		print_usage ();
		exit $ERRORS{'UNKNOWN'};
	}
}

# SNMP port options check
if (defined ($opt_p) && (($opt_p =~ /^[+-]?\d+$/) || ($opt_p < 1) || ($opt_p > 65535))) {
	print "SNMP Port has to be between 1 and 65535.\n";
	print_usage ();
	exit $ERRORS{'UNKNOWN'}
} else {
	$opt_p = 161;
}

# SNMP Timeout
if (defined($TIMEOUT)) {
	print "Alarm at $TIMEOUT + 5\n" if (defined $opt_d);
	alarm($TIMEOUT + 5);
} else {
	print "No global timeout defined : $TIMEOUT + 10\n" if (defined $opt_d);
	alarm ($TIMEOUT + 10);
}

$SIG{'ALRM'} = sub {
	print "No answer from host.\n";
	exit $ERRORS{'UNKNOWN'};
};

# Connect to host
if (defined($opt_l) && defined($opt_x)) {
	# SNMPv3 login
	print "SNMPv3 login\n" if (defined $opt_d);
	if (!defined ($opt_X)) {
		print "SNMPv3 AuthNoPriv login : $opt_l, $authproto\n" if (defined $opt_d);
		($session, $error) = Net::SNMP->session(
			-hostname	=> $opt_H,
			-version	=> '3',
			-username	=> $opt_l,
			-authpassword	=> $opt_x,
			-authprotocol	=> $authproto,
			-timeout	=> $TIMEOUT
		);
	} else {
		print "SNMPv3 AuthPriv login : $opt_l, $authproto, $privproto\n" if (defined $opt_d);
		($session, $error) = Net::SNMP->session(
			-hostname	=> $opt_H,
			-version	=> '3',
			-username	=> $opt_l,
			-authpassword	=> $opt_x,
			-authprotocol	=> $authproto,
			-privpassword	=> $opt_X,
			-privprotocol	=> $privproto,
			-timeout	=> $TIMEOUT
	    );
	}
} else {
	if (defined ($opt_2)) {
		# SNMPv2 Login
		print "SNMP v2c login\n" if (defined $opt_d);
		  ($session, $error) = Net::SNMP->session(
		 -hostname  => $opt_H,
		 -version   => 2,
		 -community => $opt_C,
		 -port      => $opt_p,
		 -timeout   => $TIMEOUT
		);
  	} else {
		# SNMPV1 login
		print "SNMP v1 login\n" if (defined $opt_d);
		($session, $error) = Net::SNMP->session(
		-hostname  => $opt_H,
		-community => $opt_C,
		-port      => $opt_p,
		-timeout   => $TIMEOUT
		);
	}
}

if (!defined($session)) {
	print ("Failover UNKNOWN: ERROR opening session: $error.\n");
	exit $ERRORS{'UNKNOWN'};
}

$resultat = (Net::SNMP->VERSION < 4) ?
	$session->get_request(@oidlists)
	: $session->get_request(-varbindlist => \@oidlists);

if (!defined($resultat)) {
	printf("Failover ERROR: Description table: %s.\n", $session->error);
	$session->close;
	exit $ERRORS{'UNKNOWN'};
}

$session->close;

if (!defined ($$resultat{$oidcfwHardwareStatusValuePrimary})) {
  print "Failover UNKNOWN: No Primary unit status\n";
  exit $ERRORS{'UNKNOWN'};
}

$statusid[0] = $$resultat{$oidcfwHardwareStatusValuePrimary};
$statusid[1] = $$resultat{$oidcfwHardwareStatusValueSecondary};

print "SNMP Result:$statusid[0]:$statusid[1]\n" if (defined $opt_d);

$statusstate[0] = $snmpstate[$statusid[0]];

for ($i = 0; $i < 2; $i++) {
	$statusdesc[$i] = $snmpdesc[$statusid[$i]];
	$statusstate[$i] = $snmpstate[$statusid[$i]];
}

print "SNMP Translation:$statusdesc[0]:$statusdesc[1]\n" if (defined $opt_d);

if (($statusstate[0] || $statusstate[1]) == $ERRORS{'CRITICAL'}) {
	print "Critical state found\n" if (defined $opt_d);
	$exit_val = 'CRITICAL';
} elsif (($statusstate[0] || $statusstate[1]) == $ERRORS{'WARNING'}) {
	print "Warning state found\n" if (defined $opt_d);
	$exit_val = 'WARNING';
} elsif (($statusstate[0] && $statusstate[1]) == $ERRORS{'OK'}) {
	if ($statusdesc[0] ne $statusdesc[1]) {
		# Active / Standby
		print "Active/Standby state found\n" if (defined $opt_d);
		if (!defined ($opt_A) || (defined ($opt_A) && ($opt_A eq "as"))) {
			$exit_val = 'OK';
		} else {
			$exit_val = 'WARNING';
		}
	} else {
		# Active / Active
		print "Active/Active state found\n" if (defined $opt_d);
		if (defined ($opt_A) && ($opt_A eq "aa")) {
			$exit_val = 'OK';
		} else {
			$exit_val = 'WARNING';
		}
	}
} else {
	print "Unknown state found\n" if (defined $opt_d);
	$exit_val = 'UNKNOWN';
}

$exitstring = "Failover " . $exit_val . ": Primary:" . $statusdesc[0] . " Secondary:" . $statusdesc[1];

if ((defined ($opt_P)) && ($opt_P ne $exitstring) && ($exit_val eq 'OK') && ($opt_P =~ /OK/)) {
	# last state defined, last state not equal to this state, state OK and last state 'OK'
	print "Previous state inconsistant with current state.\n" if (defined $opt_d);
	$exit_val = 'WARNING';
	$exitstring = "Failover WARNING: Primary:" . $statusdesc[0] . " Secondary:" . $statusdesc[1];
}

print ("$exitstring\n");
exit $ERRORS{$exit_val};



#############################################
### Subroutines
#############################################
sub print_version { print "$0 version : $chkver\n"; }

sub print_usage {
    print "Usage: $0 [-d] -H <host> [-p <port>] -C <snmp_community> [-A <acceptable>] [-2] 
		| (-l login -x passwd [-X pass -L <authp>,<privp>]) [-P <prevres>] [-t <timeout>] [-V]\n";
}

sub print_help {
	print "\nCisco PIX Failover Monitor for Nagios, version ",$chkver,"\n";
	print "GPL licence, (c)2010 Richard Yates\n";
	print_usage();
	print <<EOT;
	-d, --debug
		print extra debugging information 
	-h, --help
		print this help message
	-H, --hostname=HOST
		name or IP address of host to check
	-p, --port=PORT
		SNMP port (Default 161)
	-C, --community=COMMUNITY_NAME
		community name for the host's SNMP agent (implies v1 protocol)
	-A, --acceptable=ACCEPTABLE_RESULTS
		acceptable results are a coma-seperated value:
		aa - Active / Active
		as - Active / Standby
	-2, --v2c
		use snmp v2c
	-l, --login=LOGIN ; -x, --passwd=PASSWD
		login and auth password for snmpv3 authentication 
		if no priv password, implies AuthNoPriv 
	-X, --privpass=PASSWD
		priv password for snmpv3 (AuthPriv protocol)
	-L, --protocols=<authproto>,<privproto>
		<authproto> : authentication protocol (md5|sha : default md5)
		<privproto> : priv protocol (des|aes : default des)
	-P, --prevres=<previous result>
		previous check result, if specified checks if unit has failed over
	-t, --timeout=INTEGER
		timeout for SNMP in seconds (Default: 5)
	-V, --version
		prints version number
EOT
}