X-Git-Url: http://git.uio.no/git/?a=blobdiff_plain;f=check_openmanage;h=5a1e30971fb4f5d07f3eb885d503d6029b42a8c6;hb=3fd597dd6a1a62ea9a2d91f0c2c9ba96eebf0212;hp=4ab78a9751e78da4dd5bd07648d9cd3a61f27fd2;hpb=434167a1aba80e57fa63c5dff00b7bb5370aa007;p=check_openmanage.git diff --git a/check_openmanage b/check_openmanage index 4ab78a9..5a1e309 100755 --- a/check_openmanage +++ b/check_openmanage @@ -51,7 +51,7 @@ $SIG{__WARN__} = sub { push @perl_warnings, [@_]; }; # Version and similar info $NAME = 'check_openmanage'; -$VERSION = '3.7.0-alpha'; +$VERSION = '3.7.2'; $AUTHOR = 'Trond H. Amundsen'; $CONTACT = 't.h.amundsen@usit.uio.no'; @@ -74,10 +74,12 @@ $HELP = <<'END_HELP'; GENERAL OPTIONS: + -f, --config Specify configuration file -p, --perfdata Output performance data [default=no] -t, --timeout Plugin timeout in seconds [default=30] -c, --critical Custom temperature critical limits -w, --warning Custom temperature warning limits + -F, --fahrenheit Use Fahrenheit as temperature unit -d, --debug Debug output, reports everything -h, --help Display this help text -V, --version Display version info @@ -129,10 +131,14 @@ END_LICENSE 'check' => [], # check control 'critical' => [], # temperature critical limits 'warning' => [], # temperature warning limits + 'tempunit' => 'C', # temperature unit + 'fahrenheit' => 0, # Use fahrenheit + 'configfile' => undef, # configuration file 'timeout' => 30, # default timeout is 30 seconds 'debug' => 0, # debugging / verbose output 'help' => 0, # display help output 'perfdata' => undef, # output performance data + 'legacy_perfdata' => 0, # legacy performance data output 'info' => 0, # display servicetag 'extinfo' => 0, # display extra info 'htmlinfo' => undef, # html tags in output @@ -161,6 +167,7 @@ END_LICENSE 'privkey' => undef, # SMNP v3 'privprotocol' => undef, # SMNP v3 'use_get_table' => 0, # hack for SNMPv3 on Windows with net-snmp + 'hide_servicetag' => 0, # hidden servicetag ); # Get options @@ -168,11 +175,15 @@ GetOptions('b|blacklist=s' => \@{ $opt{blacklist} }, 'check=s' => \@{ $opt{check} }, 'c|critical=s' => \@{ $opt{critical} }, 'w|warning=s' => \@{ $opt{warning} }, + 'tempunit=s' => \$opt{tempunit}, + 'F|fahrenheit' => \$opt{fahrenheit}, + 'f|configfile=s' => \$opt{configfile}, 't|timeout=i' => \$opt{timeout}, 'd|debug' => \$opt{debug}, 'h|help' => \$opt{help}, 'V|version' => \$opt{version}, 'p|perfdata:s' => \$opt{perfdata}, + 'legacy-perfdata' => \$opt{legacy_perfdata}, 'i|info' => \$opt{info}, 'e|extinfo' => \$opt{extinfo}, 'I|htmlinfo:s' => \$opt{htmlinfo}, @@ -200,36 +211,23 @@ GetOptions('b|blacklist=s' => \@{ $opt{blacklist} }, 'privkey=s' => \$opt{privkey}, 'privprotocol=s' => \$opt{privprotocol}, 'use-get_table' => \$opt{use_get_table}, + 'hide-servicetag' => \$opt{hide_servicetag}, ) or do { print $USAGE; exit $E_UNKNOWN }; # If user requested help if ($opt{help}) { print $USAGE, $HELP; - exit $E_OK; + exit $E_UNKNOWN; } # If user requested version info if ($opt{version}) { print $LICENSE; - exit $E_OK; -} - -# Setting timeout -$SIG{ALRM} = sub { - print "PLUGIN TIMEOUT: $NAME timed out after $opt{timeout} seconds\n"; exit $E_UNKNOWN; -}; -alarm $opt{timeout}; - -# If we're using SNMP -$snmp = defined $opt{hostname} ? 1 : 0; - -# SNMP session variables -$snmp_session = undef; -$snmp_error = undef; +} -# The omreport command -$omreport = undef; +# Initialize blacklist +%blacklist = (); # Check flags, override available with the --check option %check = ( 'storage' => 1, # check storage subsystem @@ -248,6 +246,31 @@ $omreport = undef; 'esmhealth' => 1, # check the ESM log overall health ); +# Messages +@report_storage = (); # messages with associated nagios level (storage) +@report_chassis = (); # messages with associated nagios level (chassis) +@report_other = (); # messages with associated nagios level (other) + +# Read config file +parse_configfile() if defined $opt{configfile}; + +# Setting timeout +$SIG{ALRM} = sub { + print "PLUGIN TIMEOUT: $NAME timed out after $opt{timeout} seconds\n"; + exit $E_UNKNOWN; +}; +alarm $opt{timeout}; + +# If we're using SNMP +$snmp = defined $opt{hostname} ? 1 : 0; + +# SNMP session variables +$snmp_session = undef; +$snmp_error = undef; + +# The omreport command +$omreport = undef; + # Default line break $linebreak = isatty(*STDOUT) ? "\n" : '
'; @@ -275,11 +298,6 @@ if (!$snmp && -f $FW_LOCK) { @enclosures = (); # enclosures %snmp_enclosure = (); # enclosures -# Messages -@report_storage = (); # messages with associated nagios level (storage) -@report_chassis = (); # messages with associated nagios level (chassis) -@report_other = (); # messages with associated nagios level (other) - # Counters for everything %count = ( @@ -398,7 +416,7 @@ $globalstatus = $E_OK; # default global health status is "OK" adjust_checks() if defined $opt{check}; # Blacklisted components -%blacklist = defined $opt{blacklist} ? %{ get_blacklist() } : (); +set_blacklist($opt{blacklist}) if defined $opt{blacklist}; # If blacklisting is in effect, don't check global health status if (scalar keys %blacklist > 0) { @@ -424,11 +442,357 @@ else { check_omreport_options(); } +# Temperature unit +if ($opt{fahrenheit}) { + $opt{tempunit} = 'F'; +} + +# Check tempunit syntax +if ($opt{tempunit} !~ m{\A C|F|K|R \z}xms) { + print "ERROR: Unknown temperature unit '$opt{tempunit}'\n"; + exit $E_UNKNOWN; +} #--------------------------------------------------------------------- # Helper functions #--------------------------------------------------------------------- +# Make a regex from a glob pattern. Shamelessly stolen from Perl +# Cookbook chapter 6.9 +sub glob2regex { + my $globstr = shift; + my %patmap + = ( '*' => '.*', + '?' => '.', + '[' => '[', + ']' => ']', + ); + $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge; + return '\A' . $globstr . '\z'; +} + +# +# Read config file +# +sub parse_configfile { + our $tiny = undef; + + # Regexp for boolean values + our $off = qr{\A (0|off|false) \s* \z}ixms; + our $on = qr{\A (1|on|true) \s* \z}ixms; + + # Mapping between command line options and the corresponding + # config file options + our %opt2config + = ( 'info' => 'output_servicetag', + 'extinfo' => 'output_sysinfo', + 'postmsg' => 'output_post_message', + 'state' => 'output_servicestate', + 'shortstate' => 'output_servicestate_abbr', + 'show_blacklist' => 'output_blacklist', + 'hide_servicetag' => 'output_hide_servicetag', + 'htmlinfo' => 'output_html', + 'okinfo' => 'output_ok_verbosity', + 'protocol' => 'snmp_version', + 'community' => 'snmp_community', + 'port' => 'snmp_port', + 'ipv6' => 'snmp_use_ipv6', + 'tcp' => 'snmp_use_tcp', + 'warning' => 'temp_threshold_warning', + 'critical' => 'temp_threshold_critical', + 'all' => 'check_everything', + 'perfdata' => 'performance_data', + 'tempunit' => 'temperature_unit', + 'timeout' => 'timeout', + 'blacklist' => 'blacklist', + 'legacy_perfdata' => 'legacy_performance_data', + ); + + # Load the perl module + if ( eval { require Config::Tiny; 1 } ) { + $tiny = Config::Tiny->new(); + } + else { + print "ERROR: Required perl module 'Config::Tiny' not found\n"; + exit $E_UNKNOWN; + } + + # Read the config file + $tiny = Config::Tiny->read($opt{configfile}) + or do { report('other', (sprintf q{Couldn't read configuration file: %s}, Config::Tiny->errstr()), $E_UNKNOWN); + return; }; + + # Syntax check + foreach my $section (keys %{ $tiny }) { + KEYWORD: + foreach my $keyword (keys %{ $tiny->{$section} }) { + next KEYWORD if $keyword eq 'check_everything'; + if ($keyword =~ m{\A check_(.+)}xms) { + my $c = $1; + foreach my $cl (keys %check) { + next KEYWORD if $c eq $cl; + } + } + else { + LEGAL: + foreach my $legal (keys %opt2config) { + next KEYWORD if $keyword eq $opt2config{$legal}; + } + } + if ($section eq '_') { + report('other', qq{CONFIG ERROR: In the global section: Unknown statement "$keyword"}, $E_UNKNOWN); + } + else { + report('other', qq{CONFIG ERROR: Unknown statement "$keyword" in section "$section"}, $E_UNKNOWN); + } + } + } + + # Adjust checks according to statements in the configuration file + sub configfile_adjust_checks { + my $keyword = shift; + CHECK_CONFIG: + foreach my $key (keys %check) { + my $copt = join '_', 'check', $key; + next CHECK_CONFIG if !defined $tiny->{$keyword}->{$copt} or $tiny->{$keyword}->{$copt} eq q{}; + if ($tiny->{$keyword}->{$copt} =~ m{$on}ixms) { + $check{$key} = 1; + } + elsif ($tiny->{$keyword}->{$copt} =~ m{$off}ixms) { + $check{$key} = 0; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$copt' must be boolean (True/False)", $E_UNKNOWN); + } + } + return; + } + + # Set blacklist according to statements in the configuration file + sub configfile_set_blacklist { + my $keyword = shift; + if (defined $tiny->{$keyword}->{blacklist} and $tiny->{$keyword}->{blacklist} ne q{}) { + # set_blacklist() takes an array ref + set_blacklist([$tiny->{$keyword}->{blacklist}]); + } + return; + } + + # Set timeout according to statements in the configuration file + sub configfile_set_timeout { + my $keyword = shift; + if (defined $tiny->{$keyword}->{timeout} and $tiny->{$keyword}->{timeout} ne q{}) { + if ($tiny->{$keyword}->{timeout} =~ m{\A \d+ \z}xms) { # integer + $opt{timeout} = $tiny->{$keyword}->{timeout}; + } + else { + report('other', "CONFIG ERROR: Rvalue for 'timeout' must be a positive integer", $E_UNKNOWN); + } + } + return; + } + + # Set a boolean option + sub configfile_set_boolean { + my ($keyword, $bool) = @_; + my $cbool = $opt2config{$bool}; + if (defined $tiny->{$keyword}->{$cbool} and $tiny->{$keyword}->{$cbool} ne q{}) { + if ($tiny->{$keyword}->{$cbool} =~ m{$on}ixms) { + $opt{$bool} = 1; + } + elsif ($tiny->{$keyword}->{$cbool} =~ m{$off}ixms) { + $opt{$bool} = 0; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$cbool' must be boolean (True/False)", $E_UNKNOWN); + } + } + return; + } + + # Set htmlinfo option from config file + sub configfile_set_htmlinfo { + my $keyword = shift; + my $conf = $opt2config{htmlinfo}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{$on}ixms) { + $opt{htmlinfo} = 1; + } + elsif ($tiny->{$keyword}->{$conf} =~ m{$off}ixms) { + $opt{htmlinfo} = undef; + } + else { + $opt{htmlinfo} = $tiny->{$keyword}->{$conf}; + } + } + return; + } + + # Set OK output verbosity + sub configfile_set_ok_verbosity { + my $keyword = shift; + my $conf = $opt2config{okinfo}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{\A \d+ \z}xms) { + $opt{okinfo} = $tiny->{$keyword}->{$conf}; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$conf' must be a positive integer", $E_UNKNOWN); + } + } + return; + } + + # Set SNMP protocol version from config file + sub configfile_set_snmp_version { + my $keyword = shift; + my $conf = $opt2config{protocol}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{\A 1|2|3 \z}xms) { + $opt{protocol} = $tiny->{$keyword}->{$conf}; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$conf' must be '1', '2' or '3'", $E_UNKNOWN); + } + } + return; + } + + # Set SNMP community name from config file + sub configfile_set_snmp_community { + my $keyword = shift; + my $conf = $opt2config{community}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + $opt{community} = $tiny->{$keyword}->{$conf}; + } + return; + } + + # Set SNMP port number from config file + sub configfile_set_snmp_port { + my $keyword = shift; + my $conf = $opt2config{port}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{\A \d+ \z}xms) { # integer + $opt{port} = $tiny->{$keyword}->{$conf}; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$conf' must be a positive integer", $E_UNKNOWN); + } + } + return; + } + + # Set temperature threshold from config file + sub configfile_set_temp_threshold { + my $keyword = shift; + my $level = shift; + my $conf = $opt2config{$level}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + $opt{$level} = [$tiny->{$keyword}->{$conf}]; # array ref + } + return; + } + + # Set perfdata from config file + sub configfile_set_perfdata { + my $keyword = shift; + my $conf = $opt2config{perfdata}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{$on}ixms) { + $opt{perfdata} = 1; + } + elsif ($tiny->{$keyword}->{$conf} =~ m{$off}ixms) { + $opt{perfdata} = undef; + } + elsif ($tiny->{$keyword}->{$conf} =~ m{\A minimal|multiline \z}xms) { + $opt{perfdata} = $tiny->{$keyword}->{$conf}; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$conf' must be either boolean, 'minimal' or 'multiline'", $E_UNKNOWN); + } + } + return; + } + + # Set temp unit from config file + sub configfile_set_tempunit { + my $keyword = shift; + my $conf = $opt2config{tempunit}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + if ($tiny->{$keyword}->{$conf} =~ m{\A C|F|K|R \z}ixms) { + $opt{tempunit} = $tiny->{$keyword}->{$conf}; + } + else { + report('other', "CONFIG ERROR: Rvalue for '$conf' must one of C/F/K/R", $E_UNKNOWN); + } + } + return; + } + + # Set postmsg string from config file + sub configfile_set_postmsg { + my $keyword = shift; + my $conf = $opt2config{postmsg}; + if (defined $tiny->{$keyword}->{$conf} and $tiny->{$keyword}->{$conf} ne q{}) { + $opt{postmsg} = $tiny->{$keyword}->{$conf}; # array ref + } + return; + } + + # Sections in the config file to check for statements + my @sections = (); + + # First: Populate the sections array with the global section + @sections = ('_'); + + # Second: Populate the sections array with host glob pattern (but + # not exact match) + PATTERN: + foreach my $glob (sort keys %{ $tiny }) { + next PATTERN if $glob eq '_'; # global section + next PATTERN if $glob eq $opt{hostname}; # exact match + my $regex = glob2regex($glob); # make regexp + if ($opt{hostname} =~ m{$regex}) { + push @sections, $glob; + } + } + + # Third: Populate the sections array with exact hostname + if (defined $tiny->{$opt{hostname}}) { + push @sections, $opt{hostname}; + } + + # Loop through the sections array and get options + foreach my $sect (@sections) { + configfile_adjust_checks($sect); + configfile_set_blacklist($sect); + configfile_set_timeout($sect); + configfile_set_htmlinfo($sect); + configfile_set_ok_verbosity($sect); + configfile_set_boolean($sect, 'all'); + configfile_set_boolean($sect, 'info'); + configfile_set_boolean($sect, 'extinfo'); + configfile_set_boolean($sect, 'state'); + configfile_set_boolean($sect, 'shortstate'); + configfile_set_boolean($sect, 'show_blacklist'); + configfile_set_boolean($sect, 'ipv6'); + configfile_set_boolean($sect, 'tcp'); + configfile_set_boolean($sect, 'legacy_perfdata'); + configfile_set_boolean($sect, 'hide_servicetag'); + configfile_set_snmp_version($sect); + configfile_set_snmp_community($sect); + configfile_set_snmp_port($sect); + configfile_set_temp_threshold($sect, 'warning'); + configfile_set_temp_threshold($sect, 'critical'); + configfile_set_perfdata($sect); + configfile_set_tempunit($sect); + configfile_set_postmsg($sect); + } + + return; +} + # # Store a message in one of the message arrays # @@ -446,7 +810,6 @@ sub report { return push @{ $type2array{$type} }, [ $msg, $exval, $id ]; } - # # Run command, put resulting output lines in an array and return a # pointer to that array @@ -608,14 +971,23 @@ sub snmp_check { # Detecting blade via SNMP # sub snmp_detect_blade { - my $DellBaseBoardType = '1.3.6.1.4.1.674.10892.1.300.80.1.7.1.1'; - my $result = $snmp_session->get_request(-varbindlist => [$DellBaseBoardType]); + # In some setups, the IDs for the blade and interconnect + # board are mixed up, so we need to check both. + my $DellBaseBoardType1 = '1.3.6.1.4.1.674.10892.1.300.80.1.7.1.1'; + my $DellBaseBoardType2 = '1.3.6.1.4.1.674.10892.1.300.80.1.7.1.2'; + my $result1 = $snmp_session->get_request(-varbindlist => [$DellBaseBoardType1]); + my $result2 = $snmp_session->get_request(-varbindlist => [$DellBaseBoardType2]); # Identify blade. Older models (4th and 5th gen models) and/or old # OMSA (4.x) don't have this OID. If we get "noSuchInstance" or # similar, we assume that this isn't a blade - if (exists $result->{$DellBaseBoardType} && $result->{$DellBaseBoardType} eq '3') { + if (exists $result1->{$DellBaseBoardType1} && $result1->{$DellBaseBoardType1} eq '3') { + $blade = 1; + return; + } + if (exists $result2->{$DellBaseBoardType2} && $result2->{$DellBaseBoardType2} eq '3') { $blade = 1; + return; } return; } @@ -691,12 +1063,12 @@ sub check_omreport_options { # Read the blacklist option and return a hash containing the # blacklisted components # -sub get_blacklist { +sub set_blacklist { + my $foo = shift; my @bl = (); - my %blacklist = (); - if (scalar @{ $opt{blacklist} } >= 0) { - foreach my $black (@{ $opt{blacklist} }) { + if (scalar @{ $foo } >= 0) { + foreach my $black (@{ $foo }) { my $tmp = q{}; if (-f $black) { open my $BL, '<', $black @@ -721,11 +1093,11 @@ sub get_blacklist { next if $c !~ m/=/xms; my ($key, $val) = split /=/xms, $c; my @vals = split /,/xms, $val; - $blacklist{$key} = \@vals; + push @{ $blacklist{$key} }, @vals; } } - return \%blacklist; + return; } # @@ -775,7 +1147,7 @@ sub adjust_checks { # adjust the check hash if ($opt{only} eq 'chassis') { - map { $check{$_} = 1 } qw(memory fans power temp cpu voltage + map { $check{$_} = 1 } qw(memory fans power temp cpu voltage sdcard batteries amperage intrusion esmhealth); } else { @@ -868,6 +1240,13 @@ sub run_omreport { # Workaround for Openmanage BUG introduced in OMSA 5.5.0 $rawtext =~ s{\n;}{;}gxms if $command eq 'storage controller'; + # Workaround for logical connectors where there are extra + # information that isn't possible to parse consistently. Remove + # everything after and including "Path Health" + if ($command =~ m{\A storage\sconnector}xms) { + $rawtext =~ s{Path\sHealth.*}{}xms; + } + # Report if no controllers found if ($command eq 'storage controller' and $rawtext =~ m{No\scontrollers\sfound}xms) { report('storage', 'Storage Error! No controllers found', $E_UNKNOWN); @@ -1093,7 +1472,7 @@ sub warranty_url { # This helper function returns the corresponding value of a hash key, # but takes into account that the key may not exist sub get_hashval { - my $key = shift || return undef; + my $key = shift || return; my $hash = shift; return defined $hash->{$key} ? $hash->{$key} : "Undefined value $key"; } @@ -1122,6 +1501,23 @@ sub get_nonempty_string { return $alt; } +# Converts from Celsius to something else +sub temp_from_celsius { + my $x = shift; + my $to = shift; + + if ($to eq 'F') { + return sprintf '%.1f', ($x * 9/5 + 32); + } + elsif ($to eq 'K') { + return sprintf '%.1f', ($x + 273.15); + } + elsif ($to eq 'R') { + return sprintf '%.1f', ($x * 9/5 + 32 + 459.67); + } + return $x; +} + #--------------------------------------------------------------------- # Check functions @@ -1333,6 +1729,7 @@ sub check_physical_disks { my $media = undef; # media type (e.g. HDD, SSD) my $bus = undef; # bus protocol (e.g. SAS, SATA) my $spare = undef; # spare state (e.g. global hotspare) + my $cert = undef; # if drive is certified or not my @output = (); if ($snmp) { @@ -1352,6 +1749,7 @@ sub check_physical_disks { '1.3.6.1.4.1.674.10893.1.20.130.4.1.26' => 'arrayDiskNexusID', '1.3.6.1.4.1.674.10893.1.20.130.4.1.31' => 'arrayDiskSmartAlertIndication', '1.3.6.1.4.1.674.10893.1.20.130.4.1.35' => 'arrayDiskMediaType', + '1.3.6.1.4.1.674.10893.1.20.130.4.1.36' => 'arrayDiskDellCertified', '1.3.6.1.4.1.674.10893.1.20.130.5.1.7' => 'arrayDiskEnclosureConnectionControllerNumber', '1.3.6.1.4.1.674.10893.1.20.130.6.1.7' => 'arrayDiskChannelConnectionControllerNumber', ); @@ -1461,6 +1859,7 @@ sub check_physical_disks { $spare = get_hashval($out->{arrayDiskSpareState}, \%spare_state) || q{}; $bus = get_hashval($out->{arrayDiskBusType}, \%bus_type); $media = get_hashval($out->{arrayDiskMediaType}, \%media_type); + $cert = defined $out->{arrayDiskDellCertified} ? $out->{arrayDiskDellCertified} : 1; $capacity = exists $out->{arrayDiskLengthInMB} ? $out->{arrayDiskLengthInMB} * 1024**2 : -1; @@ -1491,12 +1890,19 @@ sub check_physical_disks { $media = get_nonempty_string('Media', $out, undef); $bus = get_nonempty_string('Bus Protocol', $out, undef); $spare = get_nonempty_string('Hot Spare', $out, q{}); + $cert = get_nonempty_string('Certified', $out, 1); $ctrl = $out->{ctrl}; $capacity = get_nonempty_string('Capacity', $out, q{}); $capacity =~ s{\A .*? \((\d+) \s bytes\) \z}{$1}xms; if ($capacity eq 'Unavailable') { $capacity = -1; } + if ($cert eq 'Yes' or $cert eq 'Not Applicable') { + $cert = 1; + } + else { + $cert = 0; + } } $count{pdisk}++; @@ -1554,6 +1960,26 @@ sub check_physical_disks { $name, $vendor, $product, $capacity, $ctrl, $state, $progr; report('storage', $msg, $E_WARNING, $nexus); } + # Special case: Uncertified disk + elsif ($status eq 'Non-Critical' and !$cert) { + if (blacklisted('pdisk_cert', $nexus)) { + my $msg = sprintf '%s [%s %s, %s] on ctrl %d is %s, Not Certified', + $name, $vendor, $product, $capacity, $ctrl, $state; + report('storage', $msg, $E_OK, $nexus); + } + else { + my $msg = sprintf '%s [%s %s, %s] on ctrl %d is Not Certified', + $name, $vendor, $product, $capacity, $ctrl; + report('storage', $msg, $E_WARNING, $nexus); + } + } + # Special case: Foreign disk + elsif ($status eq 'Non-Critical' and $state eq 'Foreign' + and blacklisted('pdisk_foreign', $nexus)) { + my $msg = sprintf '%s [%s %s, %s] on ctrl %d is %s', + $name, $vendor, $product, $capacity, $ctrl, $state; + report('storage', $msg, $E_OK, $nexus); + } # Default elsif ($status ne 'Ok') { my $msg = sprintf '%s [%s %s, %s] on ctrl %d needs attention: %s', @@ -1814,7 +2240,7 @@ sub check_cache_battery { $state = get_hashval($out->{batteryState}, \%bat_state) || 'Unknown state'; $learn = get_hashval($out->{batteryLearnState}, \%bat_learn_state) || 'Unknown learn state'; $pred = get_hashval($out->{batteryPredictedCapacity}, \%bat_pred_cap) || 'Unknown predicted capacity status'; - $ctrl = ($out->{batteryConnectionControllerNumber} || 10000) - 1; + $ctrl = $snmp_controller{$out->{batteryConnectionControllerNumber}}; $nexus = convert_nexus(($out->{batteryNexusID} || 9999)); $id = $nexus; $id =~ s{\A \d+:(\d+) \z}{$1}xms; @@ -2439,6 +2865,15 @@ sub check_enclosure_temp { $min_warn =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $min_warn = '[N/A]'; $min_crit =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $min_crit = '[N/A]'; + # Convert temp units + if ($opt{tempunit} ne 'C') { + $reading = temp_from_celsius($reading, $opt{tempunit}); + $max_warn = temp_from_celsius($max_warn, $opt{tempunit}); + $max_crit = temp_from_celsius($max_crit, $opt{tempunit}); + $min_warn = temp_from_celsius($min_warn, $opt{tempunit}); + $min_crit = temp_from_celsius($min_crit, $opt{tempunit}); + } + # Inactive temp probes if ($status eq 'Unknown' and $state eq 'Inactive') { my $msg = sprintf '%s in enclosure %s [%s] is %s', @@ -2446,26 +2881,26 @@ sub check_enclosure_temp { report('storage', $msg, $E_OK, $nexus); } elsif ($status ne 'Ok' and $max_crit ne '[N/A]' and $reading > $max_crit) { - my $msg = sprintf '%s in enclosure %s [%s] is critically high at %d C', - $name, $encl_id, $encl_name, $reading; + my $msg = sprintf '%s in enclosure %s [%s] is critically high at %s %s', + $name, $encl_id, $encl_name, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $nexus); } elsif ($status ne 'Ok' and $max_warn ne '[N/A]' and $reading > $max_warn) { - my $msg = sprintf '%s in enclosure %s [%s] is too high at %d C', - $name, $encl_id, $encl_name, $reading; + my $msg = sprintf '%s in enclosure %s [%s] is too high at %s %s', + $name, $encl_id, $encl_name, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $nexus); } elsif ($status ne 'Ok' and $min_crit ne '[N/A]' and $reading < $min_crit) { - my $msg = sprintf '%s in enclosure %s [%s] is critically low at %d C', - $name, $encl_id, $encl_name, $reading; + my $msg = sprintf '%s in enclosure %s [%s] is critically low at %s %s', + $name, $encl_id, $encl_name, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $nexus); } elsif ($status ne 'Ok' and $min_warn ne '[N/A]' and $reading < $min_warn) { - my $msg = sprintf '%s in enclosure %s [%s] is too low at %d C', - $name, $encl_id, $encl_name, $reading; + my $msg = sprintf '%s in enclosure %s [%s] is too low at %s %s', + $name, $encl_id, $encl_name, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $nexus); } @@ -2476,7 +2911,7 @@ sub check_enclosure_temp { if (defined $reading && $reading =~ m{\A -?\d+ \z}xms) { # take into account that with certain states the # reading doesn't exist or is not an integer - $msg .= sprintf ' at %s C', $reading; + $msg .= sprintf ' at %s %s', $reading, $opt{tempunit}; if ($min_warn eq '[N/A]' or $min_crit eq '[N/A]') { $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit; } @@ -2495,7 +2930,7 @@ sub check_enclosure_temp { if (defined $reading && $reading ne '[N/A]') { # take into account that with certain states the # reading doesn't exist or is not an integer - $msg .= sprintf ' reads %d C', $reading; + $msg .= sprintf ' reads %s %s', $reading, $opt{tempunit}; if ($min_warn eq '[N/A]' or $min_crit eq '[N/A]') { $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit; } @@ -2512,19 +2947,23 @@ sub check_enclosure_temp { # Collect performance data if (defined $opt{perfdata} && $reading ne '[N/A]') { - $name =~ s{\A Temperature\sProbe\s(\d+) \z}{temp_$1}gxms; - my $label = "${name}"; - my $mini = $label; - $mini =~ s{temp_}{t}xms; + my $index = $name; + $index =~ s{\A Temperature\sProbe\s(\d+) \z}{$1}gxms; + my $legacy_name = $name; + $legacy_name =~ s{\A Temperature\sProbe\s(\d+) \z}{temp_$1}gxms; + my $legacy_label = lc "enclosure_${encl_id}_${legacy_name}"; + my $legacy_mini = $legacy_label; + $legacy_mini =~ s{enclosure_(.+?)_temp_(.+?)}{e$1t$2}xms; push @perfdata, { - type => 'E', - id => $encl_id, - unit => 'C', - label => $label, - mini => $mini, - value => $reading, - warn => $max_warn, - crit => $max_crit, + type => 'E', + id => $opt{perfdata} eq 'minimal' ? "${encl_id}_t${index}" : "${encl_id}_temp_${index}", + unit => $opt{tempunit}, + label => q{}, + legacy => $legacy_label, + mini => $legacy_mini, + value => $reading, + warn => $max_warn, + crit => $max_crit, }; } } @@ -2843,33 +3282,29 @@ sub check_fans { $count{fan}++; next FAN if blacklisted('fan', $index); - if ($status ne 'Ok') { - my $msg = sprintf 'Chassis fan %d [%s] needs attention: %s', - $index, $location, $status; - my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; - report('chassis', $msg, $err, $index); - } - else { - my $msg = sprintf 'Chassis fan %d [%s]: %s', - $index, $location, $reading; - report('chassis', $msg, $E_OK, $index); - } + # Default + my $msg = sprintf 'Chassis fan %d [%s] reading: %s RPM', + $index, $location, $reading; + my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; + report('chassis', $msg, $err, $index); # Collect performance data if (defined $opt{perfdata}) { my $pname = $location; $pname =~ s{\s}{_}gxms; $pname =~ s{proc_}{cpu#}xms; + my $legacy_pname = $pname; $pname =~ s{_rpm\z}{}ixms; push @perfdata, { - type => 'F', - id => $index, - unit => 'rpm', - label => $pname, - mini => 'f', - value => $reading, - warn => $max_warn, - crit => $max_crit, + type => 'F', + id => $index, + unit => 'rpm', + label => $pname, + legacy => lc "fan_${index}_${legacy_pname}", + mini => "f$index", + value => $reading, + warn => $max_warn, + crit => $max_crit, }; } } @@ -3111,6 +3546,15 @@ sub check_temperatures { $count{temp}++; next TEMP if blacklisted('temp', $index); + # Convert temp units + if ($opt{tempunit} ne 'C') { + $reading = temp_from_celsius($reading, $opt{tempunit}); + $max_warn = temp_from_celsius($max_warn, $opt{tempunit}); + $max_crit = temp_from_celsius($max_crit, $opt{tempunit}); + $min_warn = temp_from_celsius($min_warn, $opt{tempunit}); + $min_crit = temp_from_celsius($min_crit, $opt{tempunit}); + } + if ($type eq 'Discrete') { my $msg = sprintf 'Temperature probe %d [%s] is %s', $index, $location, $discrete; @@ -3121,56 +3565,56 @@ sub check_temperatures { # First check according to custom thresholds if (exists $crit_threshold{$index}{max} and $reading > $crit_threshold{$index}{max}) { # Custom critical MAX - my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom max=%d)', - $index, $location, $reading, $crit_threshold{$index}{max}; + my $msg = sprintf 'Temperature Probe %d [%s] reads %s %s (custom max=%s)', + $index, $location, $reading, $opt{tempunit}, $crit_threshold{$index}{max}; report('chassis', $msg, $E_CRITICAL, $index); } elsif (exists $warn_threshold{$index}{max} and $reading > $warn_threshold{$index}{max}) { # Custom warning MAX - my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom max=%d)', - $index, $location, $reading, $warn_threshold{$index}{max}; + my $msg = sprintf 'Temperature Probe %d [%s] reads %s %s (custom max=%s)', + $index, $location, $reading, $opt{tempunit}, $warn_threshold{$index}{max}; report('chassis', $msg, $E_WARNING, $index); } elsif (exists $crit_threshold{$index}{min} and $reading < $crit_threshold{$index}{min}) { # Custom critical MIN - my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom min=%d)', - $index, $location, $reading, $crit_threshold{$index}{min}; + my $msg = sprintf 'Temperature Probe %d [%s] reads %s %s (custom min=%s)', + $index, $location, $reading, $opt{tempunit}, $crit_threshold{$index}{min}; report('chassis', $msg, $E_CRITICAL, $index); } elsif (exists $warn_threshold{$index}{min} and $reading < $warn_threshold{$index}{min}) { # Custom warning MIN - my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom min=%d)', - $index, $location, $reading, $warn_threshold{$index}{min}; + my $msg = sprintf 'Temperature Probe %d [%s] reads %s %s (custom min=%s)', + $index, $location, $reading, $opt{tempunit}, $warn_threshold{$index}{min}; report('chassis', $msg, $E_WARNING, $index); } elsif ($status ne 'Ok' and $max_crit ne '[N/A]' and $reading > $max_crit) { - my $msg = sprintf 'Temperature Probe %d [%s] is critically high at %d C', - $index, $location, $reading; + my $msg = sprintf 'Temperature Probe %d [%s] is critically high at %s %s', + $index, $location, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $index); } elsif ($status ne 'Ok' and $max_warn ne '[N/A]' and $reading > $max_warn) { - my $msg = sprintf 'Temperature Probe %d [%s] is too high at %d C', - $index, $location, $reading; + my $msg = sprintf 'Temperature Probe %d [%s] is too high at %s %s', + $index, $location, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $index); } elsif ($status ne 'Ok' and $min_crit ne '[N/A]' and $reading < $min_crit) { - my $msg = sprintf 'Temperature Probe %d [%s] is critically low at %d C', - $index, $location, $reading; + my $msg = sprintf 'Temperature Probe %d [%s] is critically low at %s %s', + $index, $location, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $index); } elsif ($status ne 'Ok' and $min_warn ne '[N/A]' and $reading < $min_warn) { - my $msg = sprintf 'Temperature Probe %d [%s] is too low at %d C', - $index, $location, $reading; + my $msg = sprintf 'Temperature Probe %d [%s] is too low at %s %s', + $index, $location, $reading, $opt{tempunit}; my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; report('chassis', $msg, $err, $index); } # Ok else { - my $msg = sprintf 'Temperature Probe %d [%s] reads %d C', - $index, $location, $reading; + my $msg = sprintf 'Temperature Probe %d [%s] reads %s %s', + $index, $location, $reading, $opt{tempunit}; if ($min_warn eq '[N/A]' and $min_crit eq '[N/A]') { $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit; } @@ -3186,17 +3630,18 @@ sub check_temperatures { if (defined $opt{perfdata}) { my $pname = $location; $pname =~ s{\s}{_}gxms; - $pname =~ s{_temp\z}{}xms; + $pname =~ s{_temp\z}{}ixms; $pname =~ s{proc_}{cpu#}xms; push @perfdata, { - type => 'T', - id => $index, - unit => 'C', - label => $pname, - mini => 't', - value => $reading, - warn => $max_warn, - crit => $max_crit, + type => 'T', + id => $index, + unit => $opt{tempunit}, + label => $pname, + legacy => lc "temp_${index}_${pname}", + mini => "t$index", + value => $reading, + warn => $max_warn, + crit => $max_crit, }; } } @@ -3578,21 +4023,20 @@ sub check_volts { report('chassis', $msg, $err, $index); # Collect performance data - if (defined $opt{perfdata}) { + if (defined $opt{perfdata} and !$opt{legacy_perfdata}) { $reading =~ s{\s+V\z}{}xms; # remove unit $reading =~ s{\.000\z}{}xms; # if integer next VOLT if $reading !~ m{\A \d+(\.\d+)? \z}xms; # discrete reading (not number) my $label = join q{_}, $location; $label =~ s{\s}{_}gxms; push @perfdata, { - type => 'V', - id => $index, - unit => 'V', - label => $label, - mini => 'v', - value => $reading, - warn => 0, - crit => 0, + type => 'V', + id => $index, + unit => 'V', + label => $label, + value => $reading, + warn => 0, + crit => 0, }; } } @@ -3774,6 +4218,12 @@ sub check_pwrmonitoring { $max_warn /= 10; $unit = 'A'; } + if ($unit eq 'mA' and $type ne 'amperageProbeTypeIsDiscrete') { + $reading /= 1000; + $max_crit /= 1000; + $max_warn /= 1000; + $unit = 'A'; + } } else { $index = get_nonempty_string('Index', $out, 9999); @@ -3810,13 +4260,15 @@ sub check_pwrmonitoring { if (defined $type and $type eq 'amperageProbeTypeIsDiscrete') { my $msg = sprintf 'Amperage probe %d [%s] is %s', $index, $location, $reading; - report('chassis', $msg, $status2nagios{$status}, $index); + my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; + report('chassis', $msg, $err, $index); } # Default else { my $msg = sprintf 'Amperage probe %d [%s] reads %s %s', $index, $location, $reading, $unit; - report('chassis', $msg, $status2nagios{$status}, $index); + my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status}; + report('chassis', $msg, $err, $index); } # Collect performance data @@ -3825,14 +4277,15 @@ sub check_pwrmonitoring { my $label = join q{_}, $location; $label =~ s{\s}{_}gxms; push @perfdata, { - type => $unit, - id => $index, - unit => $unit, - label => $label, - mini => lc $unit, - value => $reading, - warn => $max_warn, - crit => $max_crit, + type => $unit, + id => $index, + unit => $unit, + label => $label, + legacy => (join q{_}, 'pwr_mon', $index, lc $label), + mini => "p${index}" . lc $unit, + value => $reading, + warn => $max_warn, + crit => $max_crit, }; } } @@ -3854,7 +4307,7 @@ sub check_pwrmonitoring { AMP2: foreach my $line (@{ run_command("$omreport $omopt_chassis pwrmonitoring -fmt ssv") }) { chop $line; - if ($line eq 'Location;Reading') { + if ($line eq 'Location;Reading' or $line eq 'Amperage') { $found = 1; next AMP2; } @@ -3862,20 +4315,25 @@ sub check_pwrmonitoring { $found = 0; next AMP2; } - if ($found and $line =~ m/\A ([^;]+?) ; (\d*\.\d+) \s [AW] \z/xms) { - my $aname = lc $1; - my $aval = $2; + if ($found and $line =~ m/\A ([^;]+?) ; (\d*\.\d+) \s ([AW]) \z/xms) { + my $aname = $1; + my $aval = $2; + my $aunit = $3; $aname =~ s{\s}{_}gxms; # don't use an existing index while (exists $used{$index}) { ++$index; } push @perfdata, { - label => "pwr_mon_${index}_${aname}", - mini => "p${index}a", - value => $aval, - warn => 0, - crit => 0, + type => $aunit, + id => $index, + unit => $aunit, + label => $aname, + legacy => "pwr_mon_${index}_${aname}", + mini => "p${index}a", + value => $aval, + warn => 0, + crit => 0, }; ++$index; } @@ -4227,7 +4685,7 @@ sub get_omreport_chassis_info { $sysinfo{model} = $val; } if ($key eq 'Chassis Service Tag' or $key eq 'Service Tag') { - $sysinfo{serial} = $val; + $sysinfo{serial} = $opt{hide_servicetag} ? 'XXXXXXX' : $val; } if ($key eq 'System Revision') { $sysinfo{rev} = q{ } . $val; @@ -4317,7 +4775,7 @@ sub get_snmp_chassis_info { $sysinfo{model} =~ s{\s+\z}{}xms; # remove trailing whitespace } elsif (exists $chassis_oid{$oid} and $chassis_oid{$oid} eq 'chassisServiceTagName') { - $sysinfo{serial} = $result->{$oid}; + $sysinfo{serial} = $opt{hide_servicetag} ? 'XXXXXXX' : $result->{$oid}; } elsif (exists $chassis_oid{$oid} and $chassis_oid{$oid} eq 'chassisSystemRevisionName') { $sysinfo{rev} = q{ } . $result->{$oid}; @@ -4685,8 +5143,8 @@ else { # Prefix with service tag if specified with option '-i|--info' if ($opt{info}) { - if (defined $opt{htmlinfo}) { - $msg = '[$sysinfo{serial}] " . $msg; } else { @@ -4753,11 +5211,16 @@ if ($exit_code == $E_OK && defined $opt{only} && $opt{only} !~ m{\A critical|war } } elsif ($exit_code == $E_OK && !$opt{debug}) { - if (defined $opt{htmlinfo}) { - printf q{OK - System: '%s%s', SN: '%s'}, + if (defined $opt{htmlinfo} and !$opt{hide_servicetag}) { + printf q{OK - System: '%s%s', SN: '%s'}, documentation_url($sysinfo{model}), $sysinfo{model}, $sysinfo{rev}, warranty_url($sysinfo{serial}), $sysinfo{serial}; } + if (defined $opt{htmlinfo} and $opt{hide_servicetag}) { + printf q{OK - System: '%s%s', SN: '%s'}, + documentation_url($sysinfo{model}), $sysinfo{model}, $sysinfo{rev}, + $sysinfo{serial}; + } else { printf q{OK - System: '%s%s', SN: '%s'}, $sysinfo{model}, $sysinfo{rev}, $sysinfo{serial}; @@ -4839,11 +5302,16 @@ elsif ($exit_code == $E_OK && !$opt{debug}) { else { if ($opt{extinfo}) { print $linebreak; - if (defined $opt{htmlinfo}) { - printf '------ SYSTEM: %s%s, SN: %s', + if (defined $opt{htmlinfo} && !$opt{hide_servicetag}) { + printf '------ SYSTEM: %s%s, SN: %s', documentation_url($sysinfo{model}), $sysinfo{model}, $sysinfo{rev}, warranty_url($sysinfo{serial}), $sysinfo{serial}; } + elsif (defined $opt{htmlinfo} && $opt{hide_servicetag}) { + printf '------ SYSTEM: %s%s, SN: %s', + documentation_url($sysinfo{model}), $sysinfo{model}, $sysinfo{rev}, + $sysinfo{serial}; + } else { printf '------ SYSTEM: %s%s, SN: %s', $sysinfo{model}, $sysinfo{rev}, $sysinfo{serial}; @@ -4912,13 +5380,37 @@ if (defined $opt{perfdata} && !$opt{debug} && @perfdata) { $a->{label} cmp $b->{label}; } + # LEGACY sort routine for performance data + sub perfsort_legacy { + my %order = ( fan => 0, pwr => 1, tem => 2, enc => 3, ); + return ($order{(substr $a->{legacy}, 0, 3)} cmp $order{(substr $b->{legacy}, 0, 3)}) || + $a->{legacy} cmp $b->{legacy}; + } + # Print performance data sorted - my $type = $opt{perfdata} eq 'minimal' ? 'mini' : 'label'; - print join $lb, map { "$_->{type}$_->{id}_$_->{$type}=$_->{value}$_->{unit};$_->{warn};$_->{crit}" } sort perfsort @perfdata; + if ($opt{legacy_perfdata}) { + my $type = $opt{perfdata} eq 'minimal' ? 'mini' : 'legacy'; + print join $lb, map { "$_->{$type}=$_->{value};$_->{warn};$_->{crit}" } sort perfsort_legacy @perfdata; + } + else { + if ($opt{perfdata} eq 'minimal') { + print join $lb, map { "$_->{type}$_->{id}=$_->{value}$_->{unit};$_->{warn};$_->{crit}" } sort perfsort @perfdata; + } + else { + print join $lb, map { "$_->{type}$_->{id}_$_->{label}=$_->{value}$_->{unit};$_->{warn};$_->{crit}" } sort perfsort @perfdata; + } + } } -# Print a linebreak at the end -print "\n" if !$opt{debug}; +# Wrapping up and finishing +if ($opt{debug}) { + # Exit with value 3 (unknown) if debug + exit $E_UNKNOWN; +} +else { + # Print a linebreak at the end if we have a TTY + isatty(*STDOUT) && print "\n"; -# Exit with proper exit code -exit $exit_code; + # Exit with proper exit code + exit $exit_code; +}