#!/usr/bin/perl # V1.0 # check_snmp_counter2, nagios-plugin to read SNMP-values and calculate # the delta between two readings. Requires Net::SNMP. # # # Copyright (C) 2004 Carl Bingel / Dacom Svenska # # 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 qw/ticks_to_time/; use Getopt::Long; my $OID_SYSUPTIME = ".1.3.6.1.2.1.1.3.0"; ## Where to store the database with readings (one file) my $DEFAULT_DB_LOCATION = "/tmp/check_snmp_counter.dat"; &main(); sub main { ## ## **Parse command-line options ## my( $oids, $snmp_community, $snmp_hostname, $snmp_timeout, $counter_db, $counter_isoctets, $counter_inkilos, $counter_inmegas, $help); my( $critical, @critical, $warning, @warning, $descr, @descr, $opt_printuptime); my $exit_level = 0; GetOptions( "oids|o=s" => \$oids, "community|C=s" => \$snmp_community, "hostname|H=s" => \$snmp_hostname, "timeout|t=i" => \$snmp_timeout, "counterdb=s" => \$counter_db, "isoctets" => \$counter_isoctets, "inkilos" => \$counter_inkilos, "inmegas" => \$counter_inmegas, "help" => \$help, "critical|c=s", \$critical, "warning|w=s", \$warning, "descr=s", \$descr, "printuptime", \$opt_printuptime ); ## Display help display_help() if $help; ##-o|--oids my( @oids) = split( /,/, $oids); abort( "ERROR: No OIDs specified. Use the -o|--oids parameter.") if( @oids < 1); ##-C|--community $snmp_community = "public" if( $snmp_community eq ""); ##-H|--hostname abort( "ERROR: You must specify hostname. Use the -H|--hostname parameter.") if( $snmp_hostname eq ""); ##-t|--timeout $snmp_timeout = 120 if( $snmp_timeout eq ""); ##--counterdb $counter_db = $DEFAULT_DB_LOCATION if( $counter_db eq ""); ##-w|--warning @warning = split( /,/, $warning); abort( "ERROR: You must specify a warning level/range for each oid. Use the -w|--warning parameter.") if( (scalar @warning) != (scalar @oids)); ##-c|--critical @critical = split( /,/, $critical); abort( "ERROR: You must specify a critical level/range for each oid. Use the -c|--critical parameter.") if( (scalar @critical) != (scalar @oids)); ##--descr @descr = split( /,/, $descr); ## ## Create new SNMP-session-object ## my( $snmp_session, $error) = Net::SNMP->session( -hostname => $snmp_hostname, -community => $snmp_community, -translate => [ -timeticks => 0] ## return uptime in numeric format ); if( ! defined $snmp_session) { abort( "ERROR: Couldn't create snmp session: $error"); } ## set timeout $snmp_session->timeout( $snmp_timeout); ## ## Request the actual counters ## my $snmp_result = $snmp_session->get_request( -varbindlist => [ @oids, $OID_SYSUPTIME]); if( ! defined $snmp_result) { abort( "ERROR during get-request: ".$snmp_session->error()); } ## Read counter-db my $db = &read_counter_db( $counter_db); if( ! exists $db->{$snmp_hostname}) { $db->{$snmp_hostname} = {}; } my $counters = $db->{$snmp_hostname}; ## Check system uptime my $sysUpTime = $snmp_result->{$OID_SYSUPTIME}; ## ## Loop through each OID and calculate rate ## my $oid_counter=0; my( $result_string, $perfdata); foreach my $oid (@oids) { my $since_last_check=0; ## time-delta, calculate seconds since last poll my $time_delta = time() - $counters->{$oid}->{'timestamp'}; ## Check if system has been reset since last poll if( $sysUpTime < $counters->{$oid}->{'uptime'}) { ## system has been reset since last check! $since_last_check = undef; ## Check if counter has wrapped since last poll } elsif( $counters->{$oid}->{'value'} > $snmp_result->{$oid}) { ## INT counter has probably wrapped if( $counters->{$oid}->{'value'} > 4294967295) { ## counter-64 has wrapped $since_last_check = 18446744073709551616 - $counters->{$oid}->{'value'} + $snmp_result->{$oid}; } elsif( $counters->{$oid}->{'value'} > 65535) { ## counter-32 has wrapped $since_last_check = 4294967295 - $counters->{$oid}->{'value'} + $snmp_result->{$oid}; } elsif( $counters->{$oid}->{'value'} > 256) { ## counter-16 has wrapped $since_last_check = 65535 - $counters->{$oid}->{'value'} + $snmp_result->{$oid}; } } else { $since_last_check = $snmp_result->{$oid} - $counters->{$oid}->{'value'}; } ## Update database-fields $counters->{$oid}->{'uptime'} = $sysUpTime; $counters->{$oid}->{'timestamp'} = time(); $counters->{$oid}->{'value'} = $snmp_result->{$oid}; ## ## Modify rate whether counter is in octets/bits and if value should be in kilos or megas ## my $unit_c="byte"; if( $counter_isoctets) { $since_last_check *= 8; $unit_c="bit"; } my $rate = int( $since_last_check / $time_delta); my $unit_cm; if( $counter_inkilos) { $rate = int( $rate / 1024); $unit_cm="k"; } elsif( $counter_inmegas) { $rate = $rate / (1024 * 1024); $unit_cm="m"; } ## ## Check warning/critical levels ## my $status = "OK"; if( ! is_in_range( $rate, $critical[$oid_counter])) { ## State = critical $exit_level = 2; $status = "CRITICAL"; } elsif( ! is_in_range( $rate, $warning[$oid_counter])) { ## State = warning $exit_level = 1 if( $exit_level == 0); ## only set to warning if state is OK before $status = "WARNING"; } ## ## Rate-label is the text within the square-brackets in the textual output rate[]=, default is 0, 1, 2 and so on, may be changed with the --descr-switch ## my $rate_label = $oid_counter; $rate_label = $descr[$oid_counter] if( $descr[$oid_counter] ne ""); ## Append to textual output $result_string.=$status.": rate[".$rate_label."]=".$rate." ".$unit_cm.$unit_c."/s "; $oid_counter++; } ## print system uptime in textual output $result_string.=" sysUpTime=".ticks_to_time( $sysUpTime) if( $opt_printuptime); ## Output textual output print $result_string."\n"; ## close SNMP-session $snmp_session->close(); ## Write counter-db &write_counter_db( $db, $counter_db); ## exit exit( $exit_level); } ## Test wether a value is within a specified range ## sub is_in_range { my( $value, $range) = @_; my( $min, $max) = ( 0, 0); if( $range =~ m/(\d+):(\d+)/) { ($min, $max) = ( $1, $2); } elsif( $range =~ m/(\d+)/) { $max = $1; } if(($value >= $min) && ($value <= $max)) { return( 1); } return( 0); } ## ## Display help ## sub display_help { print "check_snmp_counter by Carl Bingel / Svensk IT konsult AB 2003. Distributed under Gnu Public License. usage: check_snmp_counter -H -o [,] -o | --oids [,] Which oid[s] to poll for counter-data. Specified in dotted-decimal-format (ex .1.3.6.1.2.1.2.2.1.10.2). If you want to poll several OIDS (from the same host), separate the OIDS with a comma. (mandatory) -H | --hostname Host to poll for SNMP data (mandatory) -C | --community Specify which community-string to use -t | --timeout Specify the time (in seconds) after when a SNMP-request times out -w|--warning When to issue a warning. (mandatory parameter). may be specified as a :-range. If just specified as a lone integer, the range 0: is assumed. The warning is issued when the monitored rate falls OUT of the range specified. If youre polling several OIDs, you have to specify one range for each OID, separated with comma (example -w 0:1000,0:1500). -c|--critical When to issue a critical state. (Mandatory parameter). has same syntax as in --warning. When the rate is out of both ranges for warning and critical, critical is always issued. The same goes if youre polling several OIDs. --descr Short description of OID. This text is displayed in the rate[]=27kbit/s of the textual output for better readability in the Nagios user interface. Example --descr \"Outbound traffic:Inbound traffic\". --counterdb Location of file containing the database with counter values. This file must be writable by the nagios-user! Default=$DEFAULT_DB_LOCATION --isoctets The counter counts octets (usually bytes) of data and not bits --inkilos Rate gets divided by 1024 (kilo) --inmegas Rate gets divided by 1048576 (mega) --printuptime Print system uptime in textual output --help Display this help-page "; exit; } ## ## Read counter-db ## sub read_counter_db { my( $filename) = @_; my $counter_db; open( CF, $filename); while( ) { chomp; my( $hostname, $oid, $uptime, $timestamp, $value) = split( /;/); $counter_db->{$hostname}->{$oid} = { 'uptime' => $uptime, 'timestamp' => $timestamp, 'value' => $value }; } close( CF); return( $counter_db); } ## ## Write counter-db ## sub write_counter_db { my( $db, $filename) = @_; open( CF, ">".$filename); foreach my $hostname (keys %{$db}) { foreach my $oid (keys %{$db->{$hostname}}) { print CF join( ";", ($hostname,$oid,$db->{$hostname}->{$oid}->{'uptime'}, $db->{$hostname}->{$oid}->{'timestamp'}, $db->{$hostname}->{$oid}->{'value'}))."\n"; } } close( CF); } ## ## ABORT PROGRAM W/ ERROR-TEXT ## sub abort { my( $msg) = @_; print STDERR "check_snmp_counter: ".$msg."\n"; print "check_snmp_counter: ".$msg."\n"; exit( 3); ## unknown }