Source Code for OSS
rightnow
thirdparty
#!/usr/bin/perl -w
# foster parent: ioannis.stoilis@rightnow.com
# based on http://exchange.nagios.org/directory/Plugins/Hardware/Storage-Systems/SAN-and-NAS/NetApp/check_netapp-%28combined-NetApp-Health-and-Quota-Check%29/details
#
#
# check_netapp - nagios plugin
#
# Copyright (C) 2009 Guenther Mair,
# Derived from check_ifoperstatus by Christoph Kron.
#
# 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.
#
#
# Report bugs to: guenther.mair@hoslo.ch
#
# $Id: check_netapp 19 2009-07-14 19:16:18Z gunny $
#
use POSIX;
use strict;
use lib "/usr/lib/nagios/plugins" ;
use utils qw($TIMEOUT %ERRORS &print_revision &support);
use Storable;
use Net::SNMP;
use Getopt::Long;
&Getopt::Long::config('bundling');
my $PROGNAME = 'check_netapp';
my $REVISION = '$Rev: 19 $';
sub print_help();
sub usage();
sub process_arguments();
sub bailout;
sub raidPDiskName;
sub getValue;
my $DEBUG = 0;
my $timeout;
my $hostname;
my $session;
my $error;
my $response;
my $sub_response;
my $opt_h;
my $opt_V;
my $short;
my $checks;
my $key;
my $value;
my $lastc;
my $name;
my $status;
my $answer = '';
my $snmpkey = 0;
my $community = "public";
my $snmp_version = 1;
my $maxmsgsize = 1472 ; # Net::SNMP default is 1472
my ($seclevel, $authproto, $secname, $authpass, $privpass, $auth, $priv, $context);
my $port = 161;
my $state = 'OK';
my $length = 0;
my %raidPStates = (
'1','active',
'2','reconstructionInProgress',
'3','parityReconstructionInProgress',
'4','parityVerificationInProgress',
'5','scrubbingInProgress',
'6','failed');
my $productModel = '';
my $productModelOID = '1.3.6.1.4.1.789.1.1.5.0';
my $fsOverallStatus = '';
my $fsOverallStatusOID = '1.3.6.1.4.1.789.1.5.7.1.0';
my $fsMaxUsedBytesPerCent = '';
my $fsMaxUsedBytesPerCentOID = '1.3.6.1.4.1.789.1.5.7.3.0';
my $raidPStatus = '';
my $raidPStatusOIDTable = '1.3.6.1.4.1.789.1.6.10.1.2';
my $raidPDiskNameOIDTable = '1.3.6.1.4.1.789.1.6.10.1.10';
my $enclPowerSuppliesStatus = '';
my $enclPowerSuppliesFailedOIDTable = '1.3.6.1.4.1.789.1.21.1.2.1.15';
my $enclFansStatus = '';
my $enclFansFailedOIDTable = '1.3.6.1.4.1.789.1.21.1.2.1.18';
my $enclElectronicsStatus = '';
my $enclElectronicsFailedOIDTable = '1.3.6.1.4.1.789.1.21.1.2.1.33';
my $enclTempSensorsOverTempStatus = '';
my $enclTempSensorsOverTempWarnOIDTable = '1.3.6.1.4.1.789.1.21.1.2.1.22';
my $enclTempSensorsOverTempFailOIDTable = '1.3.6.1.4.1.789.1.21.1.2.1.21';
## qrV2Table
my $qrV2TableMessageHQ = '';
my $qrV2TableMessageSQ = '';
my $qrV2TableMessageTH = '';
my %qrKBytesUsed;
my $qrV2HighKBytesUsedOIDTable = '1.3.6.1.4.1.789.1.4.6.1.4';
my $qrV2LowKBytesUsedOIDTable = '1.3.6.1.4.1.789.1.4.6.1.5';
my %qrKBHardLimit;
my $qrV2QuotaUnlimitedOIDTable = '1.3.6.1.4.1.789.1.4.6.1.6';
my $qrV2HighKBytesLimitOIDTable = '1.3.6.1.4.1.789.1.4.6.1.7';
my $qrV2LowKBytesLimitOIDTable = '1.3.6.1.4.1.789.1.4.6.1.8';
my %qrPathNames;
my $qrV2PathNameOIDTable = '1.3.6.1.4.1.789.1.4.6.1.12';
my %qrKBThreshold;
my $qrV2ThresholdUnlimitedOIDTable = '1.3.6.1.4.1.789.1.4.6.1.17';
my $qrV2HighKBytesThresholdOIDTable = '1.3.6.1.4.1.789.1.4.6.1.18';
my $qrV2LowKBytesThresholdOIDTable = '1.3.6.1.4.1.789.1.4.6.1.19';
my %qrKBSoftLimit;
my $qrV2SoftQuotaUnlimitedOIDTable = '1.3.6.1.4.1.789.1.4.6.1.20';
my $qrV2HighKBytesSoftLimitOIDTable = '1.3.6.1.4.1.789.1.4.6.1.21';
my $qrV2LowKBytesSoftLimitOIDTable = '1.3.6.1.4.1.789.1.4.6.1.22';
## Validate Arguments
process_arguments();
## Just in case of problems, let's not hang Nagios
$SIG{'ALRM'} = sub {
print ("ERROR: No snmp response from $hostname (alarm)\n");
exit $ERRORS{"UNKNOWN"};
};
alarm($timeout);
## Main function
# get product model name
$response = $session->get_request($productModelOID);
bailout('unable to get the product model name') unless defined $response->{$productModelOID};
$productModel = $response->{$productModelOID};
# overall fs space status + filled fs space
if ( ($checks & 1) > 0 ) {
# percentage of used fs space
$response = $session->get_request($fsMaxUsedBytesPerCentOID);
bailout('unable to get fsMaxUsedBytesPerCent') unless defined $response->{$fsMaxUsedBytesPerCentOID};
$fsMaxUsedBytesPerCent = $response->{$fsMaxUsedBytesPerCentOID};
$fsOverallStatus = " fsOverallStatus ";
# overall fs space status
$response = $session->get_request($fsOverallStatusOID);
bailout('unable to get fsOverallStatus') unless defined $response->{$fsOverallStatusOID};
if ($response->{$fsOverallStatusOID} == 1) {
$fsOverallStatus .= "OK";
} elsif ($response->{$fsOverallStatusOID} == 2) {
$fsOverallStatus .= "nearlyFull";
$state = "WARNING" if ($state eq "OK");
} elsif ($response->{$fsOverallStatusOID} == 3) {
$fsOverallStatus .= "full";
$state = "CRITICAL";
} else {
bailout('got unexpected response for fsOverallStatus: ' . $response->{$fsOverallStatusOID});
}
if ( $short == 0 ) {
$fsOverallStatus .= " (" . $fsMaxUsedBytesPerCent . "%)";
}
}
# check RAID states
if ( ($checks & 2) > 0 ) {
undef $response;
if (!defined ($response = $session->get_table($raidPStatusOIDTable))) {
bailout($session->error);
}
$length = length($raidPStatusOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
if ( $response->{$key} == 6 ) {
$state = "CRITICAL";
$raidPStatus .= " " . raidPDiskName($snmpkey) . " (" . $raidPStates{$response->{$key}} . ")";
} elsif ( ($response->{$key} == 2) || ($response->{$key} == 3) ) {
$state = "WARNING" if ($state eq "OK");
$raidPStatus .= " " . raidPDiskName($snmpkey) . " (" . $raidPStates{$response->{$key}} . ")";
}
}
if ( $raidPStatus eq '' ) {
$raidPStatus = " raidPStates OK";
}
}
# check power supply failures
if ( ($checks & 4) > 0 ) {
undef $response;
if (!defined ($response = $session->get_table($enclPowerSuppliesFailedOIDTable))) {
bailout($session->error);
}
foreach $key ( keys %{$response}) {
if ( $response->{$key} ne '' ) {
$enclPowerSuppliesStatus .= ' power supply failure: ' . $response->{$key};
$state = "CRITICAL";
}
}
if ( $enclPowerSuppliesStatus eq '' ) {
$enclPowerSuppliesStatus = " power supplies OK";
}
}
# check fan failures
if ( ($checks & 8) > 0 ) {
undef $response;
if (!defined ($response = $session->get_table($enclFansFailedOIDTable))) {
bailout($session->error);
}
foreach $key ( keys %{$response}) {
if ( $response->{$key} ne '' ) {
$enclFansStatus .= ' fan failure: ' . $response->{$key};
$state = "CRITICAL";
}
}
if ( $enclFansStatus eq '' ) {
$enclFansStatus = " fans OK";
}
}
# check electronic failures
if ( ($checks & 16) > 0 ) {
undef $response;
if (!defined ($response = $session->get_table($enclElectronicsFailedOIDTable))) {
bailout($session->error);
}
foreach $key ( keys %{$response}) {
if ( $response->{$key} ne '' ) {
$enclElectronicsStatus .= ' electronic failure: ' . $response->{$key};
$state = "CRITICAL";
}
}
if ( $enclElectronicsStatus eq '' ) {
$enclElectronicsStatus = " electronics OK";
}
}
# check temperatures
if ( ($checks & 32) > 0 ) {
undef $response;
if (!defined ($response = $session->get_table($enclTempSensorsOverTempWarnOIDTable))) {
bailout($session->error);
}
foreach $key ( keys %{$response}) {
if ( $response->{$key} ne '' ) {
$enclTempSensorsOverTempStatus .= ' temperature warning: ' . $response->{$key};
$state = "WARNING" if ($state eq "OK");
}
}
undef $response;
if (!defined ($response = $session->get_table($enclTempSensorsOverTempFailOIDTable))) {
bailout($session->error);
}
foreach $key ( keys %{$response}) {
if ( $response->{$key} ne '' ) {
$enclTempSensorsOverTempStatus .= ' temperature failure: ' . $response->{$key};
$state = "CRITICAL";
}
}
if ( $enclTempSensorsOverTempStatus eq '' ) {
$enclTempSensorsOverTempStatus = " temperature OK";
}
}
## Check Disk Usage
# get PathNames
if (defined ($response = $session->get_table($qrV2PathNameOIDTable))) {
$length = length($qrV2PathNameOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
$qrPathNames{$snmpkey} = $response->{$key};
}
# get KBytesUsed
if (!defined ($response = $session->get_table($qrV2HighKBytesUsedOIDTable))) {
bailout($session->error);
}
$length = length($qrV2HighKBytesUsedOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
$qrKBytesUsed{$snmpkey} = $response->{$key}*4294967296;
}
if (!defined ($response = $session->get_table($qrV2LowKBytesUsedOIDTable))) {
bailout($session->error);
}
$length = length($qrV2LowKBytesUsedOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
$qrKBytesUsed{$snmpkey} += $response->{$key};
}
# get hard limit for entries with configured hard quota
if ( ($checks & 64) > 0 ) {
if (!defined ($response = $session->get_table($qrV2QuotaUnlimitedOIDTable))) {
bailout($session->error);
}
$length = length($qrV2QuotaUnlimitedOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
if ( $response->{$key} == 1 ) {
$qrKBHardLimit{$snmpkey} = getValue($qrV2HighKBytesLimitOIDTable.".".$snmpkey)*4294967296;
$qrKBHardLimit{$snmpkey} += getValue($qrV2LowKBytesLimitOIDTable.".".$snmpkey);
}
}
while (($key, $value) = each(%qrKBHardLimit)) {
print "Quota configured for Volume '" . $qrPathNames{$key} . "' ($key): $value KB.\n" if $DEBUG;
print "KB used on this Volume: ".$qrKBytesUsed{$key}."\n" if $DEBUG;
if ( $qrKBytesUsed{$key} > $value ) {
if ( $short == 0 ) {
$qrV2TableMessageHQ .= " hard quota exceeded for Volume '".$qrPathNames{$key}."' (".$qrKBytesUsed{$key}."/".$value.")";
} else {
$qrV2TableMessageHQ .= " '".$qrPathNames{$key}."'";
}
$state = "CRITICAL";
}
}
if ( $qrV2TableMessageHQ eq '' ) {
$qrV2TableMessageHQ = " hard quotas OK";
} elsif ( $short == 1 ) {
$qrV2TableMessageHQ = " HQ: " . $qrV2TableMessageHQ;
}
}
# get soft limit for entries with configured soft quota
if ( ($checks & 128) > 0 ) {
if (!defined ($response = $session->get_table($qrV2SoftQuotaUnlimitedOIDTable))) {
bailout($session->error);
}
$length = length($qrV2SoftQuotaUnlimitedOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
if ( $response->{$key} == 1 ) {
$qrKBSoftLimit{$snmpkey} = getValue($qrV2HighKBytesSoftLimitOIDTable.".".$snmpkey)*4294967296;
$qrKBSoftLimit{$snmpkey} += getValue($qrV2LowKBytesSoftLimitOIDTable.".".$snmpkey);
}
}
while (($key, $value) = each(%qrKBSoftLimit)) {
print "Quota configured for Volume '" . $qrPathNames{$key} . "' ($key): $value KB.\n" if $DEBUG;
print "KB used on this Volume: ".$qrKBytesUsed{$key}."\n" if $DEBUG;
if ( $qrKBytesUsed{$key} > $value ) {
if ( $short == 0 ) {
$qrV2TableMessageSQ .= " soft quota exceeded for Volume '".$qrPathNames{$key}."' (".$qrKBytesUsed{$key}."/".$value.")";
} else {
$qrV2TableMessageSQ .= " '".$qrPathNames{$key}."'";
}
$state = "WARNING" if ($state eq "OK");
}
}
if ( $qrV2TableMessageSQ eq '' ) {
$qrV2TableMessageSQ = " soft quotas OK";
} elsif ( $short == 1 ) {
$qrV2TableMessageSQ = " SQ: " . $qrV2TableMessageSQ;
}
}
# get KBThreshold for entries with configured quota
if ( ($checks & 256) > 0 ) {
if (!defined ($response = $session->get_table($qrV2ThresholdUnlimitedOIDTable))) {
bailout($session->error);
}
$length = length($qrV2ThresholdUnlimitedOIDTable) + 1;
foreach $key ( keys %{$response}) {
$snmpkey = substr($key, $length);
if ( $response->{$key} == 1 ) {
$qrKBThreshold{$snmpkey} = getValue($qrV2HighKBytesThresholdOIDTable.".".$snmpkey)*4294967296;
$qrKBThreshold{$snmpkey} += getValue($qrV2LowKBytesThresholdOIDTable.".".$snmpkey);
}
}
while (($key, $value) = each(%qrKBThreshold)) {
print "Threshold configured for Volume '" . $qrPathNames{$key} . "' ($key): $value KB.\n" if $DEBUG;
print "KB used on this Volume: ".$qrKBytesUsed{$key}."\n" if $DEBUG;
if ( $qrKBytesUsed{$key} > $value ) {
if ( $short == 0 ) {
$qrV2TableMessageTH .= " threshold exceeded for Volume '".$qrPathNames{$key}."' (".$qrKBytesUsed{$key}."/".$value.")";
} else {
$qrV2TableMessageTH .= " '".$qrPathNames{$key}."'";
}
$state = "WARNING" if ($state eq "OK");
}
}
if ( $qrV2TableMessageTH eq '' ) {
$qrV2TableMessageTH = " thresholds OK";
} elsif ( $short == 1 ) {
$qrV2TableMessageTH = " TH: " . $qrV2TableMessageTH;
}
}
}
print $productModel . " Status:".$fsOverallStatus.$raidPStatus.$enclPowerSuppliesStatus.$enclFansStatus.$enclElectronicsStatus.$enclTempSensorsOverTempStatus.$qrV2TableMessageHQ.$qrV2TableMessageSQ.$qrV2TableMessageTH."\n";
$session->close;
exit $ERRORS{$state};
### subroutines
sub getValue {
my $OID = shift;
my $result;
$result = $session->get_request($OID);
bailout('unable to get '.$OID) unless defined $result->{$OID};
return $result->{$OID};
}
sub raidPDiskName {
my $snmpkey = shift;
my $localresponse = $session->get_request($raidPDiskNameOIDTable . "." . $snmpkey);
return $localresponse->{$raidPDiskNameOIDTable . "." . $snmpkey};
}
sub bailout {
my $msg = shift;
$session->close;
print "An unexpected error occurred: " . $msg . "\n";
exit $ERRORS{"UNKNOWN"};
}
sub usage() {
printf "\nMissing arguments!\n";
printf "\n";
printf "usage: \n";
printf "check_netapp -H <HOSTNAME> [-C <community>]\n";
printf "Copyright (C) 2009 Guenther Mair\n";
printf "\n\n";
exit $ERRORS{"UNKNOWN"};
}
sub print_help() {
printf "check_netapp plugin for Nagios\n";
printf "\nUsage:\n";
printf " -H (--hostname) Hostname to query - (required)\n";
printf " -C (--community) SNMP read community (defaults to public,\n";
printf " used with SNMP v1 and v2c\n";
printf " -v (--snmp_version) 1 for SNMP v1 (default)\n";
printf " 2 for SNMP v2c\n";
printf " SNMP v2c will use get_bulk for less\n";
printf " overhead if monitoring with -d\n";
printf " -i (--include) Include only a selected subset of all checks available.\n";
printf " Since this uses as simple bit-field, you will have to\n";
printf " calculate the SUM of the checks you want to execute:\n";
printf " 1 - check overall file system state\n";
printf " 2 - check raid states\n";
printf " 4 - check power supply states\n";
printf " 8 - check fan states\n";
printf " 16 - check electronics state\n";
printf " 32 - check temperature states\n";
printf " 64 - check quota hard limit settings\n";
printf " 128 - check quota soft limit settings\n";
printf " 256 - check thresholds set on Netapp device\n";
printf " -s (--short) print messages as short as possible\n";
printf " -L (--seclevel) choice of \"noAuthNoPriv\", \"authNoPriv\", or \"authPriv\"\n";
printf " -U (--secname) username for SNMPv3 context\n";
printf " -A (--authpass) authentication password (cleartext ascii or localized key\n";
printf " in hex with 0x prefix generated by using \"snmpkey\" utility\n";
printf " auth password and authEngineID\n";
printf " -a (--authproto) Authentication protocol ( MD5 or SHA1)\n";
printf " -X (--privpass) privacy password (cleartext ascii or localized key\n";
printf " in hex with 0x prefix generated by using \"snmpkey\" utility\n";
printf " privacy password and authEngineID\n";
printf " -p (--port) SNMP port (default 161)\n";
printf " -M (--maxmsgsize) Max message size - usefull only for v1 or v2c\n";
printf " -t (--timeout) seconds before the plugin times out (default=$TIMEOUT)\n";
printf " -V (--version) Plugin version\n";
printf " -h (--help) usage help \n\n";
print_revision($PROGNAME, '$Revision: 19 $');
}
sub process_arguments() {
$status = GetOptions(
"V" => \$opt_V, "version" => \$opt_V,
"h" => \$opt_h, "help" => \$opt_h,
"s" => \$short, "short" => \$short,
"i=i" => \$checks, "include=i" => \$checks,
"v=i" => \$snmp_version, "snmp_version=i" => \$snmp_version,
"C=s" => \$community, "community=s" => \$community,
"L=s" => \$seclevel, "seclevel=s" => \$seclevel,
"a=s" => \$authproto, "authproto=s" => \$authproto,
"U=s" => \$secname, "secname=s" => \$secname,
"A=s" => \$authpass, "authpass=s" => \$authpass,
"X=s" => \$privpass, "privpass=s" => \$privpass,
"p=i" => \$port, "port=i" => \$port,
"H=s" => \$hostname, "hostname=s" => \$hostname,
"M=i" => \$maxmsgsize, "maxmsgsize=i" => \$maxmsgsize,
"t=i" => \$timeout, "timeout=i" => \$timeout,
);
if ( $status == 0 ){
print_help();
exit $ERRORS{'OK'};
}
if ( $opt_V ) {
print_revision($PROGNAME,'$Revision: 19 $');
exit $ERRORS{'OK'};
}
if ( $opt_h ) {
print_help();
exit $ERRORS{'OK'};
}
if ( $short ) {
$short = 1;
} else {
$short = 0;
}
unless ( defined $checks ) {
$checks = 1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 + 256;
}
if ( ! utils::is_hostname($hostname) ) {
usage();
exit $ERRORS{"UNKNOWN"};
}
unless ( defined $timeout ) {
$timeout = $TIMEOUT;
}
if ( $snmp_version =~ /3/ ) {
# Must define a security level even though default is noAuthNoPriv
# v3 requires a security username
if (defined $seclevel && defined $secname) {
# Must define a security level even though defualt is noAuthNoPriv
unless ( grep /^$seclevel$/, qw(noAuthNoPriv authNoPriv authPriv) ) {
usage();
exit $ERRORS{"UNKNOWN"};
}
# Authentication wanted
if ( $seclevel eq 'authNoPriv' || $seclevel eq 'authPriv' ) {
unless ( $authproto eq 'MD5' || $authproto eq 'SHA1' ) {
usage();
exit $ERRORS{"UNKNOWN"};
}
if ( ! defined $authpass) {
usage();
exit $ERRORS{"UNKNOWN"};
} else {
if ( $authpass =~ /^0x/ ) {
$auth = "-authkey => $authpass" ;
} else {
$auth = "-authpassword => $authpass";
}
}
}
# Privacy (DES encryption) wanted
if ( $seclevel eq 'authPriv' ) {
if ( ! defined $privpass ) {
usage();
exit $ERRORS{"UNKNOWN"};
} else {
if ( $privpass =~ /^0x/ ){
$priv = "-privkey => $privpass";
} else {
$priv = "-privpassword => $privpass";
}
}
}
# Context name defined or default
unless ( defined $context ) {
$context = '';
}
} else {
usage();
exit $ERRORS{'UNKNOWN'}; ;
}
} # end snmpv3
if ( $snmp_version =~ /[12]/ ) {
($session, $error) = Net::SNMP->session(
-hostname => $hostname,
-community => $community,
-port => $port,
-version => $snmp_version,
-maxmsgsize => $maxmsgsize
);
if ( ! defined($session) ) {
$state='UNKNOWN';
$answer=$error;
print ("$state: $answer");
exit $ERRORS{$state};
}
} elsif ( $snmp_version =~ /3/ ) {
if ($seclevel eq 'noAuthNoPriv') {
($session, $error) = Net::SNMP->session(
-hostname => $hostname,
-port => $port,
-version => $snmp_version,
-username => $secname,
);
} elsif ( $seclevel eq 'authNoPriv' ) {
($session, $error) = Net::SNMP->session(
-hostname => $hostname,
-port => $port,
-version => $snmp_version,
-username => $secname,
$auth,
-authprotocol => $authproto,
);
} elsif ($seclevel eq 'authPriv' ) {
($session, $error) = Net::SNMP->session(
-hostname => $hostname,
-port => $port,
-version => $snmp_version,
-username => $secname,
$auth,
-authprotocol => $authproto,
$priv
);
}
if ( ! defined($session) ) {
$state='UNKNOWN';
$answer=$error;
print ("$state: $answer");
exit $ERRORS{$state};
}
} else {
$state='UNKNOWN';
print ("$state: No support for SNMP v$snmp_version yet\n");
exit $ERRORS{$state};
}
}
## End validation