Skip to content

Instantly share code, notes, and snippets.

@patrickpr
Created June 24, 2020 20:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save patrickpr/15b29483d61bef52fb372d7b11a4d3bd to your computer and use it in GitHub Desktop.
Save patrickpr/15b29483d61bef52fb372d7b11a4d3bd to your computer and use it in GitHub Desktop.
#!/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