Created
June 24, 2020 20:28
-
-
Save patrickpr/15b29483d61bef52fb372d7b11a4d3bd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
############################## check_snmp_table ############## | |
# Version : 1.0.0 | |
# Date : Sept 2016 | |
# Author : Patrick Proy ( patrick at proy.org) | |
# Help : http://nagios.proy.org | |
# Licence : GPL - http://www.fsf.org/licenses/gpl.txt | |
# TODO : | |
################################################################# | |
use strict; | |
use Net::SNMP; | |
use Getopt::Long; | |
# Nagios specific | |
use lib "/usr/lib/nagios/plugins"; | |
use utils qw(%ERRORS $TIMEOUT); | |
#my $TIMEOUT = 15; | |
#my %ERRORS=('OK'=>0,'WARNING'=>1,'CRITICAL'=>2,'UNKNOWN'=>3,'DEPENDENT'=>4); | |
# SNMP Datas | |
# Globals | |
my $Name='check_snmp_table'; | |
my $Version='1.0.0'; | |
my $o_host = undef; # hostname | |
my $o_community = undef; # community | |
my $o_port = 161; # port | |
my $o_version2 = undef; #use snmp v2c | |
my $o_descr = undef; # description filter | |
my $o_warn = undef; # warning value for check_oids | |
my $o_crit= undef; # critical value for check_oids | |
my $o_help= undef; # wan't some help ? | |
my $o_verb= undef; # verbose mode | |
my $o_version= undef; # print version | |
my $o_noreg= undef; # Do not use Regexp for name | |
my $o_negate= undef; # Negate the regexp if set | |
my $o_timeout= 5; # Default 5s Timeout | |
my $o_short= undef; # Short output parameters | |
my @o_shortL= undef; # output type,where,cut | |
# SNMPv3 specific | |
my $o_login= undef; # Login for snmpv3 | |
my $o_passwd= undef; # Pass for snmpv3 | |
my $v3protocols=undef; # V3 protocol list. | |
my $o_authproto='md5'; # Auth protocol | |
my $o_privproto='des'; # Priv protocol | |
my $o_privpass= undef; # priv password | |
# SNMP Message size parameter (Makina Corpus contrib) | |
my $o_octetlength=undef; | |
# SNMP table params | |
my $o_oid_table=undef; # oid of base table | |
my $o_oid_description=undef; # suboid of table | |
my @o_oid_check=undef; # list of suboid to check () | |
my $o_oid_check_option; | |
my $num_oid_check=undef; # number of oids to check | |
my @o_ok_values=undef; # List of values returning OK. | |
my $o_ok_values_option = undef; | |
my @o_warn_values = undef; # warning value for check_oids | |
my @o_crit_values = undef; # critical value for check_oids | |
# functions | |
sub p_version { print "$Name version : $Version\n"; } | |
sub print_usage { | |
print "Usage: $Name [-v] -H <host> -C <snmp_community> [-2] | (-l login -x passwd [-X pass -L <authp>,<privp>]) [-p <port>] -m <name in desc_oid> -O <table_oid> -D <desc_oid> -K <check_oid> -R <ok_values> -w <warn_level(s)> -c <crit_level(s)> [-t <timeout>] [-r -e] [-S 0|1[,1,<car>]] [-o <octet_length>]\n"; | |
} | |
sub round ($$) { | |
sprintf "%.$_[1]f", $_[0]; | |
} | |
sub is_pattern_valid { # Test for things like "<I\s*[^>" or "+5-i" | |
my $pat = shift; | |
if (!defined($pat)) { $pat=" ";} # Just to get rid of compilation time warnings | |
return eval { "" =~ /$pat/; 1 } || 0; | |
} | |
# Get the alarm signal (just in case snmp timout screws up) | |
$SIG{'ALRM'} = sub { | |
print ("ERROR: General time-out (Alarm signal)\n"); | |
exit $ERRORS{"UNKNOWN"}; | |
}; | |
sub isnnum { # Return true if arg is not a number | |
my $num = shift; | |
if ( $num =~ /^-?(\d+\.?\d*)|(^\.\d+)$/ ) { return 0 ;} | |
return 1; | |
} | |
sub help { | |
print "\nSNMP NetApp Table Monitor for Nagios version ",$Version,"\n"; | |
print "(c) Patrick Proy\n\n"; | |
print_usage(); | |
print <<EOT; | |
-v, --verbose | |
print extra debugging information (and lists all storages) | |
-h, --help | |
print this help message | |
-H, --hostname=HOST | |
name or IP address of host to check | |
-C, --community=COMMUNITY NAME | |
community name for the host's SNMP agent (implies SNMP v1) | |
-2, --v2c | |
Use snmp v2c | |
-l, --login=LOGIN ; -x, --passwd=PASSWD | |
Login and auth password for snmpv3 authentication | |
If no priv password exists, implies AuthNoPriv | |
-X, --privpass=PASSWD | |
Priv password for snmpv3 (AuthPriv protocol) | |
-L, --protocols=<authproto>,<privproto> | |
<authproto> : Authentication protocol (md5|sha : default md5) | |
<privproto> : Priv protocole (des|aes : default des) | |
-x, --passwd=PASSWD | |
Password for snmpv3 authentication | |
-p, --port=PORT | |
SNMP port (Default 161) | |
-m, --name=NAME | |
Name in description OID (can be mounpoints '/home' or 'Swap Space'...) | |
This is treated as a regexp : -m /var will match /var , /var/log, /opt/var ... | |
Test it before, because there are known bugs (ex : trailling /) | |
No trailing slash for mountpoints ! | |
-r, --noregexp | |
Do not use regexp to match NAME in description OID | |
-e, --exclude | |
Select all storages except the one(s) selected by -m | |
No action on storage type selection | |
-O, --table | |
oid of table to check | |
-D | |
suboid of description | |
-K | |
suboid(s) to check : separate with "," | |
-w, --warn=INTEGER | |
Last snapshot in minutes | |
-c, --critical=INTEGER | |
Last snapshot in minutes | |
-S, --short=<type>[,<where>,<cut>] | |
<type>: Make the output shorter : | |
0 : only print the global result except the disk in warning or critical | |
ex: "< 80% : OK" | |
1 : Don't print all info for every disk | |
ex : "/ : 66 %used (< 80) : OK" | |
<where>: (optional) if = 1, put the OK/WARN/CRIT at the beginning | |
<cut>: take the <n> first caracters or <n> last if n<0 | |
-o, --octetlength=INTEGER | |
max-size of the SNMP message, usefull in case of Too Long responses. | |
Be carefull with network filters. Range 484 - 65535, default are | |
usually 1472,1452,1460 or 1440. | |
-t, --timeout=INTEGER | |
timeout for SNMP in seconds (Default: 5) | |
-V, --version | |
prints version number | |
EOT | |
} | |
sub verb { my $t=shift; print $t,"\n" if defined($o_verb) ; } | |
sub check_value { # Check if a value is in range | |
my $val = shift; | |
my $range = shift; | |
if (!defined ($range)) { | |
if (!defined ($val) || $val eq "") { return 1;} | |
return 0; | |
} | |
if (!defined ($val)) { | |
if ($range eq "") { return 1;} | |
return 0; | |
} | |
if (!defined ($range) || !defined ($val)) { return 0;} | |
verb ($val."/".$range); | |
if (isnnum ($val) || isnnum($range)) { | |
if ($val eq $range) { | |
return 1; | |
} | |
return 0; | |
} | |
if ( $range =~ /:/) { # num:num format | |
my @range_num=split(/:/,$range); | |
if (!defined ($range_num[0]) || $range_num[0] eq "") { | |
if ($val > $range_num[1]) { return 0;} | |
return 1; | |
} | |
if (!defined ($range_num[1])) { | |
if ($val < $range_num[0] ) { return 0;} | |
return 1; | |
} | |
if ($val < $range_num[0] || $val > $range_num[1]) { return 0; } | |
return 1; | |
} | |
if ($range =~ /-/) { # num-num-num.... format | |
my @range_num=split(/,/,$range); | |
foreach my $cur_val (@range_num) { | |
if ($val==$cur_val) { return 1; } | |
} | |
return 0; | |
} | |
# not a range, just a number | |
if ($val==$range) { return 1; } | |
return 0; | |
} | |
sub check_options { | |
Getopt::Long::Configure ("bundling"); | |
GetOptions( | |
'v' => \$o_verb, 'verbose' => \$o_verb, | |
'h' => \$o_help, 'help' => \$o_help, | |
'H:s' => \$o_host, 'hostname:s' => \$o_host, | |
'p:i' => \$o_port, 'port:i' => \$o_port, | |
'C:s' => \$o_community, 'community:s' => \$o_community, | |
'2' => \$o_version2, 'v2c' => \$o_version2, | |
'l:s' => \$o_login, 'login:s' => \$o_login, | |
'x:s' => \$o_passwd, 'passwd:s' => \$o_passwd, | |
'X:s' => \$o_privpass, 'privpass:s' => \$o_privpass, | |
'L:s' => \$v3protocols, 'protocols:s' => \$v3protocols, | |
'c:s' => \$o_crit, 'critical:s' => \$o_crit, | |
'w:s' => \$o_warn, 'warn:s' => \$o_warn, | |
't:i' => \$o_timeout, 'timeout:i' => \$o_timeout, | |
'm:s' => \$o_descr, 'name:s' => \$o_descr, | |
'r' => \$o_noreg, 'noregexp' => \$o_noreg, | |
'e' => \$o_negate, 'exclude' => \$o_negate, | |
'V' => \$o_version, 'version' => \$o_version, | |
'S:s' => \$o_short, 'short:s' => \$o_short, | |
'o:i' => \$o_octetlength, 'octetlength:i' => \$o_octetlength, | |
'O:s' => \$o_oid_table, 'tables:s' => \$o_oid_table, | |
'D:s' => \$o_oid_description, | |
'K:s' => \$o_oid_check_option, | |
'R:s' => \$o_ok_values_option, | |
); | |
if (defined($o_help) ) { help(); exit $ERRORS{"UNKNOWN"}}; | |
if (defined($o_version) ) { p_version(); exit $ERRORS{"UNKNOWN"}}; | |
# check mount point regexp | |
if (!is_pattern_valid($o_descr)) | |
{ print "Bad pattern for mount point !\n"; print_usage(); exit $ERRORS{"UNKNOWN"}} | |
# check snmp information | |
if ( !defined($o_community) && (!defined($o_login) || !defined($o_passwd)) ) | |
{ print "Put snmp login info!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}} | |
if ((defined($o_login) || defined($o_passwd)) && (defined($o_community) || defined($o_version2)) ) | |
{ print "Can't mix snmp v1,2c,3 protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}} | |
if (defined ($v3protocols)) { | |
if (!defined($o_login)) { print "Put snmp V3 login info with protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}} | |
my @v3proto=split(/,/,$v3protocols); | |
if ((defined ($v3proto[0])) && ($v3proto[0] ne "")) {$o_authproto=$v3proto[0]; } # Auth protocol | |
if (defined ($v3proto[1])) {$o_privproto=$v3proto[1]; } # Priv protocol | |
if ((defined ($v3proto[1])) && (!defined($o_privpass))) { | |
print "Put snmp V3 priv login info with priv protocols!\n"; print_usage(); exit $ERRORS{"UNKNOWN"}} | |
} | |
# Check compulsory attributes | |
if ( ! defined($o_descr) || ! defined($o_host) || !defined($o_warn) || | |
!defined($o_crit)) { print "Missing host/description/warning/critical\n";print_usage(); exit $ERRORS{"UNKNOWN"}}; | |
# Check short values | |
if ( defined ($o_short)) { | |
@o_shortL=split(/,/,$o_short); | |
if ((isnnum($o_shortL[0])) || ($o_shortL[0] !=0) && ($o_shortL[0]!=1)) { | |
print "-S first option must be 0 or 1\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
if (defined ($o_shortL[1])&& $o_shortL[1] eq "") {$o_shortL[1]=undef}; | |
if (defined ($o_shortL[2]) && isnnum($o_shortL[2])) | |
{print "-S last option must be an integer\n";print_usage(); exit $ERRORS{"UNKNOWN"};} | |
} | |
#### octet length checks | |
if (defined ($o_octetlength) && (isnnum($o_octetlength) || $o_octetlength > 65535 || $o_octetlength < 484 )) { | |
print "octet lenght must be < 65535 and > 484\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
####check for oid | |
if ( ! defined ($o_oid_table) || ($o_oid_table !~ /^[0-9\.]+$/) ) { | |
print "set oid base table in oid format\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
if ( ! defined ($o_oid_description) || ($o_oid_description !~ /^[0-9\.]+$/) ) { | |
print "set description oid in oid format \n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
if ( ! defined ($o_oid_check_option)) { | |
print "set oid base check\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
@o_oid_check=split(/,/,$o_oid_check_option); | |
$num_oid_check = $#o_oid_check; | |
@o_warn_values=split(/,/,$o_warn); | |
@o_crit_values=split(/,/,$o_crit); | |
if (defined ($o_ok_values_option)) { | |
@o_ok_values=split(/,/,$o_ok_values_option); | |
if ($#o_oid_check != $#o_ok_values ) { | |
print "Same number of check and OK\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
} | |
if ($#o_oid_check != $#o_warn_values || $#o_oid_check != $#o_crit_values) { | |
print "Same number of check and warn/crit values\n";print_usage(); exit $ERRORS{"UNKNOWN"}; | |
} | |
$num_oid_check=$#o_oid_check; | |
verb ("num values (-1) : ". $num_oid_check); | |
# Check for warn levels | |
# if (($o_warn < 0) || ($o_crit < 0)) { print " warn and critical > 0 \n";print_usage(); exit $ERRORS{"UNKNOWN"}}; | |
# if ($o_warn >= $o_crit) { print " warn < crit \n";print_usage(); exit $ERRORS{"UNKNOWN"}}; | |
} | |
########## MAIN ####### | |
check_options(); | |
# Check gobal timeout | |
if (defined($TIMEOUT)) { | |
verb("Alarm at $TIMEOUT"); | |
alarm($TIMEOUT); | |
} else { | |
verb("no timeout defined : $o_timeout + 10"); | |
alarm ($o_timeout+10); | |
} | |
# Connect to host | |
my ($session,$error); | |
if ( defined($o_login) && defined($o_passwd)) { | |
# SNMPv3 login | |
verb("SNMPv3 login"); | |
if (!defined ($o_privpass)) { | |
verb("SNMPv3 AuthNoPriv login : $o_login, $o_authproto"); | |
($session, $error) = Net::SNMP->session( | |
-hostname => $o_host, | |
-version => '3', | |
-username => $o_login, | |
-authpassword => $o_passwd, | |
-authprotocol => $o_authproto, | |
-port => $o_port, | |
-timeout => $o_timeout | |
); | |
} else { | |
verb("SNMPv3 AuthPriv login : $o_login, $o_authproto, $o_privproto"); | |
($session, $error) = Net::SNMP->session( | |
-hostname => $o_host, | |
-version => '3', | |
-username => $o_login, | |
-authpassword => $o_passwd, | |
-authprotocol => $o_authproto, | |
-privpassword => $o_privpass, | |
-privprotocol => $o_privproto, | |
-port => $o_port, | |
-timeout => $o_timeout | |
); | |
} | |
} else { | |
if (defined ($o_version2)) { | |
# SNMPv2 Login | |
verb("SNMP v2c login"); | |
($session, $error) = Net::SNMP->session( | |
-hostname => $o_host, | |
-version => 2, | |
-community => $o_community, | |
-port => $o_port, | |
-timeout => $o_timeout | |
); | |
} else { | |
# SNMPV1 login | |
verb("SNMP v1 login"); | |
($session, $error) = Net::SNMP->session( | |
-hostname => $o_host, | |
-community => $o_community, | |
-port => $o_port, | |
-timeout => $o_timeout | |
); | |
} | |
} | |
if (!defined($session)) { | |
printf("ERROR: %s.\n", $error); | |
exit $ERRORS{"UNKNOWN"}; | |
} | |
if (defined($o_octetlength)) { | |
my $oct_resultat=undef; | |
my $oct_test= $session->max_msg_size(); | |
verb(" actual max octets:: $oct_test"); | |
$oct_resultat = $session->max_msg_size($o_octetlength); | |
if (!defined($oct_resultat)) { | |
printf("ERROR: Session settings : %s.\n", $session->error); | |
$session->close; | |
exit $ERRORS{"UNKNOWN"}; | |
} | |
$oct_test= $session->max_msg_size(); | |
verb(" new max octets:: $oct_test"); | |
} | |
my $resultat=undef; | |
my $stype=undef; | |
# Get rid of UTF8 translation in case of accentuated caracters (thanks to Dimo Velev). | |
$session->translate(Net::SNMP->TRANSLATE_NONE); | |
# Get description in table | |
my $descr_table = $o_oid_table . "." . $o_oid_description; | |
$resultat = $session->get_table(Baseoid => $descr_table); | |
if (!defined($resultat) ) { | |
printf("ERROR: Description table : %s.\n", $session->error); | |
$session->close; | |
exit $ERRORS{"UNKNOWN"}; | |
} | |
my @tindex = undef; | |
my @oids = undef; | |
my @descr = undef; | |
my $num_int = 0; | |
my $count_oid = 0; | |
my $test = undef; | |
my $perf_out= undef; | |
my $num_oids=0; | |
verb("Filter : $o_descr"); | |
foreach my $key ( keys %$resultat) { | |
verb("OID : $key, Desc : $$resultat{$key}"); | |
# test by regexp or exact match / include or exclude | |
if (defined($o_negate)) { | |
$test = defined($o_noreg) | |
? $$resultat{$key} ne $o_descr | |
: $$resultat{$key} !~ /$o_descr/; | |
} else { | |
$test = defined($o_noreg) | |
? $$resultat{$key} eq $o_descr | |
: $$resultat{$key} =~ /$o_descr/; | |
} | |
if ($test) { | |
# get the index number of the interface | |
$tindex[$num_int] = $key; | |
$tindex[$num_int] =~ s/$descr_table//; | |
for (my $i=0; $i <= $num_oid_check ; $i++) { | |
verb ("push : " . $o_oid_table . "." . $o_oid_check[$i] . $tindex[$num_int]); | |
$oids[$num_oids] = $o_oid_table . "." . $o_oid_check[$i] . $tindex[$num_int]; | |
$num_oids++; | |
} | |
#$oids[$num_int]=$o_oid_table . "." . $o_oid_check[0] . $tindex[$num_int]; | |
$descr[$num_int]=$$resultat{$key}; | |
verb("Match for $tindex[$num_int] : $$resultat{$key}"); | |
$num_int++; | |
} | |
} | |
verb("storages selected : $num_int , oids : $num_oids"); | |
if ( $num_int == 0 ) { print "Unknown description : $o_descr : ERROR\n" ; exit $ERRORS{"UNKNOWN"};} | |
my $result=undef; | |
if ($session->version == 0) { | |
# snmpv1 | |
$result = $session->get_request(Varbindlist => \@oids); | |
} else { | |
# snmp v2c or v3 : get_bulk_request is not really good for this, so do simple get | |
$result = $session->get_request(Varbindlist => \@oids); | |
foreach my $key ( keys %$result) { verb("$key : $$result{$key}"); } | |
} | |
if (!defined($result)) { printf("ERROR: check table :%s.\n", $session->error); $session->close; | |
exit $ERRORS{"UNKNOWN"}; | |
} | |
$session->close; | |
# Only a few ms left... | |
alarm(0); | |
my $i=undef; | |
my $warn_state=0; | |
my $crit_state=0; | |
my $output=undef; | |
# to erase | |
my $status_table=undef; | |
my $lag_table=undef; | |
my $loc_status=undef; | |
my $locstate=undef; | |
for ($i=0;$i<$num_int;$i++) { | |
verb("Descr : $descr[$i]"); | |
for (my $j=0; $j <= $num_oid_check ; $j++) { | |
verb("State : ". $$result{$o_oid_table . "." . $o_oid_check[$j] . $tindex[$i]}); | |
#verb("Lag : $$result{$lag_table . $tindex[$i]}"); | |
if (defined($o_shortL[2])) { | |
if ($o_shortL[2] < 0) {$descr[$i]=substr($descr[$i],$o_shortL[2]);} | |
else {$descr[$i]=substr($descr[$i],0,$o_shortL[2]);} | |
} | |
$locstate=0; | |
$loc_status = $$result{$o_oid_table . "." . $o_oid_check[$j] . $tindex[$i]}; | |
if (defined ($o_ok_values[$j])) { | |
if ( check_value($loc_status,$o_ok_values[$j])) { | |
verb ("ok state"); | |
$locstate=0; | |
} else { | |
if ( check_value($loc_status ,$o_crit_values[$j])) { verb ("crit state");$locstate=$crit_state= 1; } | |
else { | |
if ( check_value($loc_status ,$o_warn_values[$j])) { verb ("warn state");$locstate=$warn_state= 1; } | |
} | |
} | |
} else { | |
if ( check_value($loc_status ,$o_crit_values[$j])) { verb ("crit state");$locstate=$crit_state= 1; } | |
else { | |
if ( check_value($loc_status ,$o_warn_values[$j])) { verb ("warn state");$locstate=$warn_state= 1; } | |
} | |
} | |
if (!defined($o_shortL[0]) || ($locstate==1)) { # print full output if warn or critical state | |
$output.=sprintf ("%s: %s ",$descr[$i],$loc_status); | |
} elsif ($o_shortL[0] == 1) { | |
$output.=sprintf ("%s: %s ",$descr[$i],$loc_status); | |
} | |
} | |
} | |
if (!defined ($output)) { $output="All selected elements "; } | |
if ($crit_state == 1 ) { | |
print "Critical : ", $output,"\n"; | |
exit $ERRORS{"CRITICAL"}; | |
} | |
if ($warn_state == 1 ) { | |
print "Warning : ", $output,"\n"; | |
exit $ERRORS{"WARNING"}; | |
} | |
print "OK : ", $output,"\n"; | |
exit $ERRORS{"OK"}; | |
exit 0; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment