#!/usr/bin/perl
# vim:ts=4
#
# Check a foundry switch for state of a virtual server
# Steve S 2006
# http://www.steveshipway.org/forum
#
# Usage -
# check_foundry [-B] -H switchname [-C community] -v vServerName [-p vPort]
#
# Version 0.2


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

my($CRITACTIVE) = 500;
my($WARNACTIVE) = 400;
my($FOUNDRY) = '1.3.6.1.4.1.1991';
my($TIMEOUT) = 4;
my($DEBUG) = 0;
my($COMMUNITY) = 'public';
my($SWITCH) = '';
my($VSERVER) = '';
my($VPORT) = '';

my(%vports) = ();
my(%rports) = ();
my(%bind) = ();
my(%global) = ();

my($MSG,$STATUS) = ("",3);

use vars qw/$opt_c $opt_w $opt_h $opt_d $opt_H $opt_C $opt_v $opt_B $opt_p $opt_M $opt_T/;
my($snmp,$snmperr,$resp);
my($maxr,$okr,$state);

###########################################################################
sub dohelp() 
{
	print "To perform Nagios checks:\n";
	print "check_foundry -H switchname [-C community] -v vServerName [-p vPort]\n";
	print "To list vServers (info only)\n";
	print "check_foundry -H switchname [-C community]\n";
	print "To list rServer bindings (info only)\n";
	print "check_foundry -B -H switchname [-C community] [-v vServerName [-p vPort]]\n";
	print "For MRTG output (total active sessions)\n";
	print "check_foundry -M -H switchname [-C community]\n";
	print "For MRTG output (active sessions, rServer count)\n";
	print "check_foundry -M -H switchname [-C community] -v vServerName [-p vPort]\n";
	print "Add -d for debug mode.\n";
	print "Use -w and -c to set thresholds for number of Active sessions ($WARNACTIVE, $CRITACTIVE)\n";

	exit 0;
}
sub dooutput() {
	if($opt_M) {
		print "U\nU\n\nError: $MSG ($STATUS)\n"; exit 0;
	}
	$MSG = "Status: $STATUS" if(!$MSG);
	$STATUS = 3 if($STATUS<0 or $STATUS>3);
	print "$MSG\n";
	exit $STATUS;
}
###########################################################################
sub readfoundry()
{
	my($k,$oid,$seq);

	print "Starting SNMP\n" if($DEBUG);
	($snmp,$snmperr) = Net::SNMP->session( -hostname=>$SWITCH,
		-community=>$COMMUNITY, -timeout=>$TIMEOUT, -retries=>2 );
	if($snmperr) {
		print "($snmperr)\n" if($DEBUG);
		$MSG = "Error: $snmperr";
		$STATUS = 3;
		dooutput; # exit
		exit(0);
	}

	# gather general stats
	$resp = $snmp->get_request( -varbindlist=>[
		"$FOUNDRY.1.1.4.1.1.0",
		"$FOUNDRY.1.1.4.1.13.0"
	] );
	if(!$resp) {
		$MSG = "Error: Cannot read general OIDs. Is community string correct?";
		$STATUS = 3;
		dooutput; # exit
	}
	$global{active} = $resp->{"$FOUNDRY.1.1.4.1.1.0"}
		-$resp->{"$FOUNDRY.1.1.4.1.13.0"};

	# Optimise
	if(!$VSERVER and $opt_M) { $snmp->close; return; }

	# Gather vport  stats
	print "Getting vport table\n" if($DEBUG);
	$resp = $snmp->get_table( -baseoid=>"$FOUNDRY.1.1.4.26");
	if(!$resp) {
		$MSG = "Error: Cannot read VPort Statistics table";
		$STATUS = 3;
		dooutput; # exit
	}
	foreach $oid ( keys %{$resp} ) {
		$oid =~ /\.(\d+)\.(\d+\.\d+\.\d+\.\d+\.\d+)$/;
#		print "$oid\n" if($DEBUG);
		if($1 == 3) {
			$seq = $2;
			$k = $resp->{$oid}.":".$resp->{"$FOUNDRY.1.1.4.26.1.1.2.$seq"};
#			print "Adding vport:$k\n" if($DEBUG);
			$vports{$k} = {
				seq=>$seq,
				vserver=>$resp->{$oid},
				vport=>$resp->{"$FOUNDRY.1.1.4.26.1.1.2.$seq"},
				vip=>$resp->{"$FOUNDRY.1.1.4.26.1.1.1.$seq"},
				active=>$resp->{"$FOUNDRY.1.1.4.26.1.1.4.$seq"},
				total=>$resp->{"$FOUNDRY.1.1.4.26.1.1.5.$seq"}
			};
			$VPORT = $resp->{"$FOUNDRY.1.1.4.26.1.1.2.$seq"}
				if($resp->{$oid} eq $VSERVER and !$VPORT);
		}
	}
 
	# Gather Rport stats
	print "Getting rport table\n" if($DEBUG);
	$resp = $snmp->get_table( -baseoid=>"$FOUNDRY.1.1.4.24");
	if(!$resp) {
		$MSG = "Error: Cannot read RPort Statistics table";
		$STATUS = 3;
		dooutput; # exit
	}
	foreach $oid ( keys %{$resp} ) {
		$oid =~ /\.(\d+)\.(\d+\.\d+\.\d+\.\d+\.\d+)$/;
		if($1 == 3) {
			$seq = $2;
			$k = $resp->{$oid}.":".$resp->{"$FOUNDRY.1.1.4.24.1.1.2.$seq"};
			$rports{$k} = {
				seq=>$seq,
				vserver=>$resp->{$oid},
				vport=>$resp->{"$FOUNDRY.1.1.4.24.1.1.2.$seq"},
				vip=>$resp->{"$FOUNDRY.1.1.4.24.1.1.1.$seq"},
				active=>$resp->{"$FOUNDRY.1.1.4.24.1.1.7.$seq"},
				total=>$resp->{"$FOUNDRY.1.1.4.24.1.1.8.$seq"},
				state=>$resp->{"$FOUNDRY.1.1.4.24.1.1.5.$seq"}
			};
		}
	}

	# Gather bind information
	print "Getting bind table\n" if($DEBUG);
	$resp = $snmp->get_table( -baseoid=>"$FOUNDRY.1.1.4.6");
	if(!$resp) {
		$MSG = "Error: Cannot read bind table";
		$STATUS = 3;
		dooutput; # exit
	}
	foreach $oid ( keys %{$resp} ) {
		$oid =~ /\.(\d+)\.(\d+)$/;
#		print "$oid\n" if($DEBUG);
		if($1 == 2) {
			$seq = $2;
			$k = $resp->{$oid}.":".$resp->{"$FOUNDRY.1.1.4.6.1.1.3.$seq"};
			$bind{$k} = () if(!defined $bind{$k});
			push @{$bind{$k}}, 
				($resp->{"$FOUNDRY.1.1.4.6.1.1.4.$seq"}.":"
				.$resp->{"$FOUNDRY.1.1.4.6.1.1.5.$seq"});

		}
	}

	$snmp->close();
}
sub listvservers()
{
	print "Listing all vservers...\n" if($DEBUG);
	print "VServer Name    IP Address      Port  Svrs Active\n";
	foreach ( keys %vports ) {
		printf "%-15s %-16s %5d %3d (%5d)\n",
			$vports{$_}{vserver},$vports{$_}{vip},$vports{$_}{vport},
			($#{$bind{$_}}+1), $vports{$_}{active};
	}
}
sub listbindings()
{
	if(!$VSERVER) {
		foreach ( keys %bind ) {
			print $_.": ".(join ',',@{$bind{$_}})."\n";
		}
	} else {
		print "Bindings for $VSERVER:$VPORT :\n";
		print "".(join ', ',@{$bind{"$VSERVER:$VPORT"}})."\n";
	}
}

###########################################################################

getopts('dhH:C:v:p:MT:B');
dohelp if($opt_h);
$DEBUG = 1 if($opt_d);
$COMMUNITY = $opt_C if($opt_C);
$SWITCH = $opt_H if($opt_H);
$VSERVER = $opt_v if($opt_v);
$VPORT = $opt_p if($opt_p);
$TIMEOUT = $opt_T if($opt_T);

if(!$SWITCH) {
	$STATUS = 3; $MSG = "Must specify foundry switch name with -H.";
	dooutput; exit 3;
}
if(!$COMMUNITY) {
	$STATUS = 3; $MSG = "Must specify SNMP community string with -C.";
	dooutput; exit 3;
}
readfoundry;

if(!$VSERVER) {
	if($opt_M) {
		print $global{active}."\n"
			.$global{active}."\n\n"
			.$global{active}." active sessions.\n";
		exit 0;
	} elsif($opt_B) {
		listbindings;
	} else {
		listvservers;
	}
	exit(0);
}

if( $VSERVER =~ /(\S+):(\d+)/ ) { ($VSERVER,$VPORT) = ($1,$2); }
if( $VSERVER =~ /\d+\.\d+\.\d+\.\d+/ ) {
	foreach ( keys %vports ) {
		if( $vports{$_}{vip} eq $VSERVER ) {
			$VSERVER = $vports{$_}{vserver};
			$VPORT = $vports{$_}{vport} if(!$VPORT);
			last;
		}
	}
}

# Now, identify the vserver...
if(!defined $vports{"$VSERVER:$VPORT"}) {
	$MSG = "That Server/port is not recognised ($VSERVER:$VPORT)";
	$STATUS = 3;
	dooutput; exit 3;
}

# Bindings
listbindings if($DEBUG or $opt_B);
exit 0 if($opt_B);

if($opt_M) {
	# count active rservers
	$okr = 0;
	foreach ( @{$bind{"$VSERVER:$VPORT"}} ) {
		if($rports{$_}{state} == 6) { $okr +=1; } # active
	}
	# output
	print $vports{"$VSERVER:$VPORT"}{active}."\n";
	print "$okr\n";
#	print "".(1+$#{$bind{"$VSERVER:$VPORT"}})."\n";
	print "\n";
	print "Active: ".$vports{"$VSERVER:$VPORT"}{active}
		." RServers: $okr/".(1+$#{$bind{"$VSERVER:$VPORT"}})."\n";
	exit 0;
}

# Now, we need to work out which RServers we have and their health.
$STATUS = 0;
if( $vports{"$VSERVER:$VPORT"}{active} > $CRITACTIVE ) {
	$STATUS = 2; 
	$MSG = "CRIT: ".$vports{"$VSERVER:$VPORT"}{active}." active sessions to $VSERVER:$VPORT";
} elsif( $vports{"$VSERVER:$VPORT"}{active} > $WARNACTIVE ) {
	$STATUS = 1; 
	$MSG = "WARN: ".$vports{"$VSERVER:$VPORT"}{active}." active sessions to $VSERVER:$VPORT";
} else {
	$MSG = $vports{"$VSERVER:$VPORT"}{active}." active sessions to $VSERVER:$VPORT";
}

$maxr = $#{$bind{"$VSERVER:$VPORT"}} + 1;
$okr  = 0;
foreach ( @{$bind{"$VSERVER:$VPORT"}} ) {
	# loop through the rservers
	$state = $rports{$_}{state};
	if($state == 6) { $okr +=1; next; } # active
	if($state == 1) {
		$MSG .= "<BR>rServer $_ is only ENABLED"; $STATUS= 2;
	} elsif($state == 2) {
		$MSG .= "<BR>rServer $_ is FAILED"; $STATUS= 2;
	} elsif($state == 3) {
		$MSG .= "<BR>rServer $_ is TESTING"; $STATUS= 1 if($STATUS<2);
	} elsif($state == 4) {
		$MSG .= "<BR>rServer $_ is SUSPECT"; $STATUS= 1 if($STATUS<2);
 	} elsif($state == 5) {
		$MSG .= "<BR>rServer $_ is SHUTDOWN"; 
 	} elsif($state == 0) {
		$MSG .= "<BR>rServer $_ is DISABLED"; 
	}
}
if($maxr == $okr ) {
	$MSG .= "<BR>All $maxr RealServers are active.";
} else {
	$MSG .= "<BR>Only $okr/$maxr RealServers are active.";
}
dooutput;
exit(3);
