#!/usr/bin/perl -w # # check_snmp_counter - nagios plugin # # Douglas E. Warner # inspiration by Christoph Kron - check_ifstatus.pl # # 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. # use strict; use Net::SNMP; use Getopt::Long; use File::Basename; &Getopt::Long::config('auto_abbrev'); my $version = "0.6"; my $status; my $needhelp = ''; my $TIMEOUT = 30; my %ERRORS = ( 'OK' => '0', 'WARNING' => '1', 'CRITICAL' => '2', 'UNKNOWN' => '3', ); # default return value is UNKNOWN my $state = "UNKNOWN"; # time this script was run my $runtime = time(); # responses from script my $answer = ""; my $oidmsg = ""; my $error; my $oidwarn = 0; my $oidcrit = 0; # external variable declarations my $hostname; my $community = "public"; my $port = 161; my $snmpCounter; my $argDescr; my @ignorestring; my $counterFilePath; my $counterFile; my $absoluteval; my $isgauge; my $warntmp; my @warning; my $crittmp; my @critical; # snmp related variables my $session; my $response; my $snmpkey; my $snmpoid; my $key; our %snmpIndexes; our %snmpDescr; my $snmpSysUpTime = ".1.3.6.1.2.1.1.3.0"; my $snmpHostUptime; # file related variables my $fileRuntime; my $fileHostUptime; my %fileIndexes; ## main program # Just in case of problems, let's not hang NetSaint $SIG{'ALRM'} = sub { print ("ERROR: No snmp response from $hostname (alarm)\n"); exit $ERRORS{"UNKNOWN"}; }; alarm($TIMEOUT); # we must have -some- arguments if (scalar(@ARGV) == 0) { usage(); } # end if no options Getopt::Long::Configure("no_ignore_case"); $status = GetOptions( "h|help" => \$needhelp, "o|oid=s" => \$snmpCounter, "w|warning=s" => \$warntmp, "c|critical=s" => \$crittmp, "C|snmpcommunity=s" => \$community, "d|description=s" => \$argDescr, "i|ignore-string=s" => \@ignorestring, "p|port=i" => \$port, "f|filepath=s" => \$counterFilePath, "a|absoluteval" => \$absoluteval, "g|gauge" => \$isgauge, "H|hostname=s" => \$hostname, ); if ($status == 0 || $needhelp) { usage(); } # end if getting options fails or the user wants help if (!defined($snmpCounter)) { $state = "UNKNOWN"; $answer = "OID must be specified"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end check for oid if (!defined($warntmp)) { $state = "UNKNOWN"; $answer = "Warning levels must be specified"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end check for warning levels if (!defined($crittmp)) { $state = "UNKNOWN"; $answer = "Critical levels must be specified"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end check for critical levels if (!defined($counterFilePath)) { $state = "UNKNOWN"; $answer = "Filepath must be specified"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end check for filepath if (!defined($hostname)) { $state = "UNKNOWN"; $answer = "Hostname must be specified"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end check for hostname # split up the warning/critical min/max @warning = split(/,/, $warntmp); @critical = split(/,/, $crittmp); # setup counterFile now that we have hostname and oid $counterFile = "$counterFilePath/$hostname-$snmpCounter"; # check to see if warning and critical levels were set correctly if (@warning != 2 && @critical != 2) { usage(); } readolddata(); getSysUpTime(); getCounters(); if (defined($argDescr)) { getDescr(); } # end if the user wants a description for the index checkIgnores(); outputdata(); # check to see if we pulled data from the cache file or not if (!defined($fileRuntime)) { $state = "OK"; $answer = "never cached - caching
"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end if cache file didn't exist # check host's uptime to see if it goes backward if ($fileHostUptime > $snmpHostUptime) { $state = "WARNING"; $answer = "uptime goes backward - recaching data
"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end if host uptime goes backward # check if number of indexes in file is different than our new data if (scalar(keys(%fileIndexes)) != scalar(keys(%snmpIndexes))) { $state = "WARNING"; $answer = "number of indexes changed - recaching data
"; print "$state: $answer\n"; exit $ERRORS{$state}; } # end number of indexes different # foreach snmp key (sorted numerically), figure stuff out foreach $key (sort numerically (keys %snmpIndexes)) { my $timeperiod = ($runtime-$fileRuntime); my $lastperiod = ($snmpIndexes{$key} - $fileIndexes{$key}); # if we have a gauge, we don't want to divide over time $lastperiod = $lastperiod/$timeperiod if (!$isgauge); # if we're supposed to use the absolute value for comparison # (ie, pos/neg doesn't matter) $lastperiod = abs($lastperiod) if ($absoluteval); my $descr = ""; if (defined($argDescr)) { $descr = " ($snmpDescr{$key})"; } # end if there is a description for this, output; else nothing if ($lastperiod < $warning[0] && !($lastperiod < $critical[0])) { $oidwarn++; $oidmsg .= "$key$descr < $warning[0] for $timeperiod sec
"; } elsif ($lastperiod > $warning[1] && !($lastperiod > $critical[1])) { $oidwarn++; $oidmsg .= "$key$descr > $warning[1] for $timeperiod sec
"; } elsif ($lastperiod < $critical[0]) { $oidcrit++; $oidmsg .= "$key$descr < $critical[0] for $timeperiod sec
"; } elsif ($lastperiod > $critical[1]) { $oidcrit++; $oidmsg .= "$key$descr > $critical[1] for $timeperiod sec
"; } # end how is an oid behaving } # end foreach $key # figure out what state we're in if ($oidcrit > 0) { $state = "CRITICAL"; } elsif ($oidwarn > 0) { $state = "WARNING"; } else { $state = "OK"; } # end if we have warnings or not # setup final message $answer = "critical $oidcrit, warning $oidwarn
$oidmsg"; print ("$state: $answer\n"); exit $ERRORS{$state}; ## subroutines ## # the usage of this program (duh) sub usage { print < checks a provided counter and verifies that it was within , since it was last run Usage: check_snmp_counter (-o|--oid) -w , -c (-C|--snmpcommunity) (-H|--hostname) [-d|--description] [-i|--ignore-string] [-p|--port] (-f|--filepath) [-a|--absoluteval] [-g|--isgauge] END exit $ERRORS{"UNKNOWN"}; } # for sorting things numerically sub numerically { $a <=> $b } # end numerically # read in the old data (if it exists) sub readolddata { if (-e $counterFile) { open(FILE, "$counterFile"); chomp($fileRuntime = ); chomp($fileHostUptime = ); while (my $line = ) { chomp($line); my @splitline = split(/ /, $line); $fileIndexes{$splitline[0]} = $splitline[1]; } # end while rest of file close(FILE); } # end if file exists } # end readolddata # output data for cache sub outputdata { if ((-w $counterFile) || (-w dirname($counterFile))) { open(FILE, ">$counterFile"); print FILE "$runtime\n"; print FILE "$snmpHostUptime\n"; foreach $key (sort numerically (keys %snmpIndexes)) { print FILE "$key $snmpIndexes{$key}\n"; } # end for each value to output close(FILE); } else { $state = "WARNING"; $answer = "file $counterFile is not writable
"; print ("$state: $answer\n"); exit $ERRORS{$state}; } # end if file is writable } # end outputdata # get sysUpTime from host sub getSysUpTime { # get the uptime for the host given ($session, $error) = Net::SNMP->session( -hostname => $hostname, -community => $community, -port => $port ); if (!defined($session)) { $state = "UNKNOWN"; $answer = $error; print "$state: $answer"; exit $ERRORS{$state}; } $session->translate( [-timeticks => 0x0] ); $response = $session->get_request( -varbindlist => [$snmpSysUpTime] ); if (!defined($response)) { $answer=$session->error; $session->close; $state = "WARNING"; print "$state: $answer,$community,$snmpSysUpTime"; exit $ERRORS{$state}; } $snmpHostUptime = $response->{$snmpSysUpTime}; $session->close; } # end getSysUpTime # get counters the user wants from host sub getCounters { # get the value(s) for the oid given ($session, $error) = Net::SNMP->session( -hostname => $hostname, -community => $community, -port => $port, ); if (!defined($session)) { $state = "UNKNOWN"; $answer = $error; print "$state: $answer"; exit $ERRORS{$state}; } if ( !defined($response = $session->get_table($snmpCounter)) && !defined($response = $session->get_request($snmpCounter)) ) { $answer = $session->error; $session->close; $state = "WARNING"; print "$state: $answer,$community,$snmpCounter\n"; exit $ERRORS{$state}; } foreach $snmpkey (keys %{$response}) { $snmpkey =~ /.*\.(\d+)$/; $key = $1; # if the value this is isn't in @ignorestring, okay $snmpIndexes{$key} = $response->{$snmpkey}; } $session->close; } # end getCounters # get descriptions the user wants from host sub getDescr { # get the value(s) for the oid given ($session, $error) = Net::SNMP->session( -hostname => $hostname, -community => $community, -port => $port ); if (!defined($session)) { $state = "UNKNOWN"; $answer = $error; print "$state: $answer"; exit $ERRORS{$state}; } if ( !defined($response = $session->get_table($argDescr)) && !defined($response = $session->get_request($argDescr)) ) { $answer = $session->error; $session->close; $state = "WARNING"; print "$state: $answer,$community,$argDescr"; exit $ERRORS{$state}; } foreach $snmpkey (keys %{$response}) { $snmpkey =~ /.*\.(\d+)$/; $key = $1; # if the value this is isn't in @ignorestring, okay $snmpDescr{$key} = $response->{$snmpkey}; } $session->close; } # end getCounters # check to see if we're supposed to be ignoring a key. if so, kill it sub checkIgnores { foreach my $key (keys(%snmpIndexes)) { if (arrayInScalar(@ignorestring, $snmpIndexes{$key}) || arrayInScalar(@ignorestring, $snmpDescr{$key})) { delete($snmpIndexes{$key}); delete($snmpDescr{$key}); } # end if ignore, nuke key } # end foreach key } # end checkIgnores # check to see if variables in @array match $scalar sub arrayInScalar { my $scalar = pop(@_); my @array = @_; if (scalar(@array) == 0) { return 0; #false } # end if no array foreach my $item (@array) { if ($scalar =~ /$item/i) { return 1; #true } # end if item found, return true } # end each item in array return 0; #false } # end arrayInScalar