]> git.uio.no Git - check_openmanage.git/blame - check_openmanage
* version 3.5.10-beta10
[check_openmanage.git] / check_openmanage
CommitLineData
669797e1 1#!/usr/bin/perl
2#
3# Nagios plugin
4#
5# Monitor Dell server hardware status using Dell OpenManage Server
6# Administrator, either locally via NRPE, or remotely via SNMP.
7#
8# $Id$
9#
f1728beb 10# Copyright (C) 2010 Trond H. Amundsen
669797e1 11#
12# This program is free software: you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation, either version 3 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful, but
18# WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20# General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program. If not, see <http://www.gnu.org/licenses/>.
24#
25
26require 5.006; # Perl v5.6.0 or newer is required
27use strict;
28use warnings;
a38cf844 29use POSIX qw(isatty ceil);
c76b83db 30use Getopt::Long qw(:config no_ignore_case);
669797e1 31
32# Global (package) variables used throughout the code
33use vars qw( $NAME $VERSION $AUTHOR $CONTACT $E_OK $E_WARNING $E_CRITICAL
34 $E_UNKNOWN $FW_LOCK $USAGE $HELP $LICENSE
35 $snmp_session $snmp_error $omreport $globalstatus $global
36 $linebreak $omopt_chassis $omopt_system $blade
7c03958b 37 $exit_code $snmp
48aeec0b 38 %check %opt %reverse_exitcode %status2nagios
669797e1 39 %snmp_status %snmp_probestatus %probestatus2nagios %sysinfo
b1f48712 40 %blacklist %nagios_alert_count %count %snmp_enclosure %snmp_controller
48aeec0b 41 @perl_warnings @controllers @enclosures @perfdata
669797e1 42 @report_storage @report_chassis @report_other
43 );
44
45#---------------------------------------------------------------------
46# Initialization and global variables
47#---------------------------------------------------------------------
48
70ec369c 49# Collect perl warnings in an array
50$SIG{__WARN__} = sub { push @perl_warnings, [@_]; };
cbbc270f 51
669797e1 52# Version and similar info
53$NAME = 'check_openmanage';
355299d9 54$VERSION = '3.5.10-beta10';
669797e1 55$AUTHOR = 'Trond H. Amundsen';
56$CONTACT = 't.h.amundsen@usit.uio.no';
57
58# Exit codes
59$E_OK = 0;
60$E_WARNING = 1;
61$E_CRITICAL = 2;
62$E_UNKNOWN = 3;
63
64# Firmware update lock file [FIXME: location on Windows?]
65$FW_LOCK = '/var/lock/.spsetup'; # default on Linux
66
67# Usage text
68$USAGE = <<"END_USAGE";
69Usage: $NAME [OPTION]...
70END_USAGE
71
72# Help text
73$HELP = <<'END_HELP';
74
75GENERAL OPTIONS:
76
77 -p, --perfdata Output performance data
78 -t, --timeout Plugin timeout in seconds
79 -c, --critical Customise temperature critical limits
80 -w, --warning Customise temperature warning limits
81 -d, --debug Debug output, reports everything
82 -h, --help Display this help text
83 -V, --version Display version info
84
85SNMP OPTIONS:
86
a43b0da8 87 -H, --hostname Hostname or IP (required for SNMP)
669797e1 88 -C, --community SNMP community string
89 -P, --protocol SNMP protocol version
90 --port SNMP port number
91
92OUTPUT OPTIONS:
93
94 -i, --info Prefix any alerts with the service tag
95 -e, --extinfo Append system info to alerts
96 -s, --state Prefix alerts with alert state
057193f5 97 -S, --short-state Prefix alerts with alert state (abbreviated)
669797e1 98 -o, --okinfo Verbosity when check result is OK
bee55928 99 -I, --htmlinfo HTML output with clickable links
669797e1 100
101CHECK CONTROL AND BLACKLISTING:
102
103 -a, --all Check everything, even log content
104 -b, --blacklist Blacklist missing and/or failed components
105 --only Only check a certain component or alert type
106 --check Fine-tune which components are checked
107
108For more information and advanced options, see the manual page or URL:
109 http://folk.uio.no/trondham/software/check_openmanage.html
110END_HELP
111
112# Version and license text
113$LICENSE = <<"END_LICENSE";
114$NAME $VERSION
f1728beb 115Copyright (C) 2010 $AUTHOR
669797e1 116License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
117This is free software: you are free to change and redistribute it.
118There is NO WARRANTY, to the extent permitted by law.
119
120Written by $AUTHOR <$CONTACT>
121END_LICENSE
122
123# Options with default values
397acff6 124%opt = ( 'blacklist' => [], # blacklisting
125 'check' => [], # check control
126 'critical' => [], # temperature critical limits
127 'warning' => [], # temperature warning limits
128 'timeout' => 30, # default timeout is 30 seconds
129 'debug' => 0, # debugging / verbose output
130 'help' => 0, # display help output
131 'perfdata' => undef, # output performance data
132 'info' => 0, # display servicetag
133 'extinfo' => 0, # display extra info
134 'htmlinfo' => undef, # html tags in output
135 'postmsg' => undef, # post message
136 'state' => 0, # display alert type
137 'short-state' => 0, # display alert type (short)
138 'okinfo' => 0, # default "ok" output level
139 'linebreak' => undef, # specify linebreak
140 'version' => 0, # plugin version info
141 'all' => 0, # check everything
142 'only' => undef, # only one component
143 'omreport' => undef, # omreport path
144 'port' => 161, # default SNMP port
145 'hostname' => undef, # hostname or IP
146 'community' => 'public', # SMNP v1 or v2c
147 'protocol' => 2, # default SNMP protocol 2c
148 'username' => undef, # SMNP v3
149 'authpassword' => undef, # SMNP v3
150 'authkey' => undef, # SMNP v3
151 'authprotocol' => undef, # SMNP v3
152 'privpassword' => undef, # SMNP v3
153 'privkey' => undef, # SMNP v3
154 'privprotocol' => undef, # SMNP v3
155 'use_get_table' => 0, # hack for SNMPv3 on Windows with net-snmp
669797e1 156 );
157
158# Get options
159GetOptions('b|blacklist=s' => \@{ $opt{blacklist} },
160 'check=s' => \@{ $opt{check} },
161 'c|critical=s' => \@{ $opt{critical} },
162 'w|warning=s' => \@{ $opt{warning} },
163 't|timeout=i' => \$opt{timeout},
164 'd|debug' => \$opt{debug},
165 'h|help' => \$opt{help},
166 'V|version' => \$opt{version},
167 'p|perfdata:s' => \$opt{perfdata},
168 'i|info' => \$opt{info},
169 'e|extinfo' => \$opt{extinfo},
bee55928 170 'I|htmlinfo:s' => \$opt{htmlinfo},
669797e1 171 'postmsg=s' => \$opt{postmsg},
172 's|state' => \$opt{state},
057193f5 173 'S|short-state' => \$opt{shortstate},
669797e1 174 'o|ok-info=i' => \$opt{okinfo},
da64c4d4 175 'linebreak=s' => \$opt{linebreak},
669797e1 176 'a|all' => \$opt{all},
177 'only=s' => \$opt{only},
9ed0700c 178 'omreport=s' => \$opt{omreport},
669797e1 179 'port=i' => \$opt{port},
180 'H|hostname=s' => \$opt{hostname},
181 'C|community=s' => \$opt{community},
182 'P|protocol=i' => \$opt{protocol},
183 'U|username=s' => \$opt{username},
184 'authpassword=s' => \$opt{authpassword},
185 'authkey=s' => \$opt{authkey},
186 'authprotocol=s' => \$opt{authprotocol},
187 'privpassword=s' => \$opt{privpassword},
188 'privkey=s' => \$opt{privkey},
189 'privprotocol=s' => \$opt{privprotocol},
4cabd748 190 'use-get_table' => \$opt{use_get_table},
669797e1 191 ) or do { print $USAGE; exit $E_UNKNOWN };
192
193# If user requested help
194if ($opt{help}) {
195 print $USAGE, $HELP;
196 exit $E_OK;
197}
198
199# If user requested version info
200if ($opt{version}) {
201 print $LICENSE;
202 exit $E_OK;
203}
204
205# Setting timeout
206$SIG{ALRM} = sub {
0ae24325 207 print "PLUGIN TIMEOUT: $NAME timed out after $opt{timeout} seconds\n";
669797e1 208 exit $E_UNKNOWN;
209};
210alarm $opt{timeout};
211
212# If we're using SNMP
213$snmp = defined $opt{hostname} ? 1 : 0;
214
215# SNMP session variables
216$snmp_session = undef;
217$snmp_error = undef;
218
219# The omreport command
220$omreport = undef;
221
222# Check flags, override available with the --check option
223%check = ( 'storage' => 1, # check storage subsystem
224 'memory' => 1, # check memory (dimms)
225 'fans' => 1, # check fan status
226 'power' => 1, # check power supplies
227 'temp' => 1, # check temperature
228 'cpu' => 1, # check processors
229 'voltage' => 1, # check voltage
230 'batteries' => 1, # check battery probes
231 'amperage' => 1, # check power consumption
232 'intrusion' => 1, # check intrusion detection
233 'alertlog' => 0, # check the alert log
234 'esmlog' => 0, # check the ESM log (hardware log)
235 'esmhealth' => 1, # check the ESM log overall health
236 );
237
238# Default line break
51e99613 239$linebreak = isatty(*STDOUT) ? "\n" : '<br/>';
669797e1 240
241# Line break from option
242if (defined $opt{linebreak}) {
243 if ($opt{linebreak} eq 'REG') {
244 $linebreak = "\n";
245 }
246 elsif ($opt{linebreak} eq 'HTML') {
247 $linebreak = '<br/>';
248 }
249 else {
250 $linebreak = $opt{linebreak};
251 }
252}
253
254# Exit with status=UNKNOWN if there is firmware upgrade in progress
255if (!$snmp && -f $FW_LOCK) {
256 print "MONITORING DISABLED - Firmware update in progress ($FW_LOCK exists)\n";
257 exit $E_UNKNOWN;
258}
259
260# List of controllers and enclosures
261@controllers = (); # controllers
262@enclosures = (); # enclosures
b1f48712 263%snmp_enclosure = (); # enclosures
669797e1 264
265# Messages
266@report_storage = (); # messages with associated nagios level (storage)
267@report_chassis = (); # messages with associated nagios level (chassis)
268@report_other = (); # messages with associated nagios level (other)
269
270# Counters for everything
271%count
272 = (
273 'pdisk' => 0, # number of physical disks
274 'vdisk' => 0, # number of logical drives (virtual disks)
275 'temp' => 0, # number of temperature probes
276 'volt' => 0, # number of voltage probes
277 'amp' => 0, # number of amperage probes
278 'intr' => 0, # number of intrusion probes
279 'dimm' => 0, # number of memory modules
14e95f92 280 'mem' => 0, # total memory
669797e1 281 'fan' => 0, # number of fan probes
282 'cpu' => 0, # number of CPUs
283 'bat' => 0, # number of batteries
284 'power' => 0, # number of power supplies
285 'esm' => {
286 'Critical' => 0, # critical entries in ESM log
287 'Non-Critical' => 0, # warning entries in ESM log
288 'Ok' => 0, # ok entries in ESM log
289 },
290 'alert' => {
291 'Critical' => 0, # critical entries in alert log
292 'Non-Critical' => 0, # warning entries in alert log
293 'Ok' => 0, # ok entries in alert log
294 },
295 );
296
297# Performance data
48aeec0b 298@perfdata = ();
669797e1 299
300# Global health status
301$global = 1; # default is to check global status
302$globalstatus = $E_OK; # default global health status is "OK"
303
304# Nagios error levels reversed
305%reverse_exitcode
306 = (
307 $E_OK => 'OK',
308 $E_WARNING => 'WARNING',
309 $E_CRITICAL => 'CRITICAL',
310 $E_UNKNOWN => 'UNKNOWN',
311 );
312
313# OpenManage (omreport) and SNMP error levels
314%status2nagios
315 = (
316 'Unknown' => $E_CRITICAL,
317 'Critical' => $E_CRITICAL,
318 'Non-Critical' => $E_WARNING,
319 'Ok' => $E_OK,
320 'Non-Recoverable' => $E_CRITICAL,
321 'Other' => $E_CRITICAL,
322 );
323
324# Status via SNMP
325%snmp_status
326 = (
327 1 => 'Other',
328 2 => 'Unknown',
329 3 => 'Ok',
330 4 => 'Non-Critical',
331 5 => 'Critical',
332 6 => 'Non-Recoverable',
333 );
334
335# Probe Status via SNMP
336%snmp_probestatus
337 = (
338 1 => 'Other', # probe status is not one of the following:
339 2 => 'Unknown', # probe status is unknown (not known or monitored)
340 3 => 'Ok', # probe is reporting a value within the thresholds
341 4 => 'nonCriticalUpper', # probe has crossed upper noncritical threshold
342 5 => 'criticalUpper', # probe has crossed upper critical threshold
343 6 => 'nonRecoverableUpper', # probe has crossed upper non-recoverable threshold
344 7 => 'nonCriticalLower', # probe has crossed lower noncritical threshold
345 8 => 'criticalLower', # probe has crossed lower critical threshold
346 9 => 'nonRecoverableLower', # probe has crossed lower non-recoverable threshold
347 10 => 'failed', # probe is not functional
348 );
349
350# Probe status translated to Nagios alarm levels
351%probestatus2nagios
352 = (
353 'Other' => $E_CRITICAL,
354 'Unknown' => $E_CRITICAL,
355 'Ok' => $E_OK,
356 'nonCriticalUpper' => $E_WARNING,
357 'criticalUpper' => $E_CRITICAL,
358 'nonRecoverableUpper' => $E_CRITICAL,
359 'nonCriticalLower' => $E_WARNING,
360 'criticalLower' => $E_CRITICAL,
361 'nonRecoverableLower' => $E_CRITICAL,
362 'failed' => $E_CRITICAL,
363 );
364
365# System information gathered
366%sysinfo
367 = (
368 'bios' => 'N/A', # BIOS version
369 'biosdate' => 'N/A', # BIOS release date
370 'serial' => 'N/A', # serial number (service tag)
371 'model' => 'N/A', # system model
372 'osname' => 'N/A', # OS name
373 'osver' => 'N/A', # OS version
374 'om' => 'N/A', # OMSA version
375 'bmc' => 0, # HAS baseboard management controller (BMC)
376 'rac' => 0, # HAS remote access controller (RAC)
377 'rac_name' => 'N/A', # remote access controller (RAC)
378 'bmc_fw' => 'N/A', # BMC firmware
379 'rac_fw' => 'N/A', # RAC firmware
380 );
381
382# Adjust which checks to perform
383adjust_checks() if defined $opt{check};
384
385# Blacklisted components
386%blacklist = defined $opt{blacklist} ? %{ get_blacklist() } : ();
387
388# If blacklisting is in effect, don't check global health status
389if (scalar keys %blacklist > 0) {
390 $global = 0;
391}
392
393# Take into account new hardware and blades
394$omopt_chassis = 'chassis'; # default "chassis" option to omreport
395$omopt_system = 'system'; # default "system" option to omreport
396$blade = 0; # if this is a blade system
397
398# Some initializations and checking before we begin
399if ($snmp) {
400 snmp_initialize(); # initialize SNMP
401 snmp_check(); # check that SNMP works
402 snmp_detect_blade(); # detect blade via SNMP
403}
404else {
405 # Find the omreport binary
406 find_omreport();
407 # Check help output from omreport, see which options are available.
408 # Also detecting blade via omreport.
409 check_omreport_options();
410}
411
412
413#---------------------------------------------------------------------
414# Helper functions
415#---------------------------------------------------------------------
416
417#
418# Store a message in one of the message arrays
419#
420sub report {
421 my ($type, $msg, $exval, $id) = @_;
422 defined $id or $id = q{};
423
424 my %type2array
425 = (
426 'storage' => \@report_storage,
427 'chassis' => \@report_chassis,
428 'other' => \@report_other,
429 );
430
431 return push @{ $type2array{$type} }, [ $msg, $exval, $id ];
432}
433
434
435#
436# Run command, put resulting output lines in an array and return a
437# pointer to that array
438#
439sub run_command {
440 my $command = shift;
441
442 open my $CMD, '-|', $command
443 or do { report('other', "Couldn't run command '$command': $!", $E_UNKNOWN)
444 and return [] };
445 my @lines = <$CMD>;
446 close $CMD
447 or do { report('other', "Couldn't close filehandle for command '$command': $!", $E_UNKNOWN)
448 and return \@lines };
449 return \@lines;
450}
451
452#
453# Run command, put resulting output in a string variable and return it
454#
455sub slurp_command {
456 my $command = shift;
457
458 open my $CMD, '-|', $command
459 or do { report('other', "Couldn't run command '$command': $!", $E_UNKNOWN) and return };
460 my $rawtext = do { local $/ = undef; <$CMD> }; # slurping
461 close $CMD;
462
463 # NOTE: We don't check the return value of close() since omreport
464 # does something weird sometimes.
465
466 return $rawtext;
467}
468
469#
470# Initialize SNMP
471#
472sub snmp_initialize {
473 # Legal SNMP v3 protocols
474 my $snmp_v3_privprotocol = qr{\A des|aes|aes128|3des|3desde \z}xms;
475 my $snmp_v3_authprotocol = qr{\A md5|sha \z}xms;
476
477 # Parameters to Net::SNMP->session()
478 my %param
479 = (
480 '-port' => $opt{port},
481 '-hostname' => $opt{hostname},
482 '-version' => $opt{protocol},
483 );
484
485 # Parameters for SNMP v3
486 if ($opt{protocol} == 3) {
487
488 # Username is mandatory
489 if (defined $opt{username}) {
490 $param{'-username'} = $opt{username};
491 }
492 else {
493 print "SNMP ERROR: With SNMPv3 the username must be specified\n";
494 exit $E_UNKNOWN;
495 }
496
497 # Authpassword is optional
498 if (defined $opt{authpassword}) {
499 $param{'-authpassword'} = $opt{authpassword};
500 }
501
502 # Authkey is optional
503 if (defined $opt{authkey}) {
504 $param{'-authkey'} = $opt{authkey};
505 }
506
507 # Privpassword is optional
508 if (defined $opt{privpassword}) {
509 $param{'-privpassword'} = $opt{privpassword};
510 }
511
512 # Privkey is optional
513 if (defined $opt{privkey}) {
514 $param{'-privkey'} = $opt{privkey};
515 }
516
517 # Privprotocol is optional
518 if (defined $opt{privprotocol}) {
519 if ($opt{privprotocol} =~ m/$snmp_v3_privprotocol/xms) {
520 $param{'-privprotocol'} = $opt{privprotocol};
521 }
522 else {
523 print "SNMP ERROR: Unknown privprotocol '$opt{privprotocol}', "
524 . "must be one of [des|aes|aes128|3des|3desde]\n";
525 exit $E_UNKNOWN;
526 }
527 }
528
529 # Authprotocol is optional
530 if (defined $opt{authprotocol}) {
531 if ($opt{authprotocol} =~ m/$snmp_v3_authprotocol/xms) {
532 $param{'-authprotocol'} = $opt{authprotocol};
533 }
534 else {
535 print "SNMP ERROR: Unknown authprotocol '$opt{authprotocol}', "
536 . "must be one of [md5|sha]\n";
537 exit $E_UNKNOWN;
538 }
539 }
540 }
541 # Parameters for SNMP v2c or v1
542 elsif ($opt{protocol} == 2 or $opt{protocol} == 1) {
543 $param{'-community'} = $opt{community};
544 }
545 else {
546 print "SNMP ERROR: Unknown SNMP version '$opt{protocol}'\n";
547 exit $E_UNKNOWN;
548 }
549
550 # Try to initialize the SNMP session
551 if ( eval { require Net::SNMP; 1 } ) {
552 ($snmp_session, $snmp_error) = Net::SNMP->session( %param );
553 if (!defined $snmp_session) {
554 printf "SNMP: %s\n", $snmp_error;
555 exit $E_UNKNOWN;
556 }
557 }
558 else {
0ae24325 559 print "ERROR: You need perl module Net::SNMP to run $NAME in SNMP mode\n";
669797e1 560 exit $E_UNKNOWN;
561 }
562 return;
563}
564
565#
566# Checking if SNMP works by probing for "chassisModelName", which all
567# servers should have
568#
569sub snmp_check {
570 my $chassisModelName = '1.3.6.1.4.1.674.10892.1.300.10.1.9.1';
571 my $result = $snmp_session->get_request(-varbindlist => [$chassisModelName]);
572
573 # Typically if remote host isn't responding
574 if (!defined $result) {
0ae24325 575 printf "SNMP CRITICAL: %s\n", $snmp_session->error;
669797e1 576 exit $E_CRITICAL;
577 }
578
579 # If OpenManage isn't installed or is not working
580 if ($result->{$chassisModelName} =~ m{\A noSuch (Instance|Object) \z}xms) {
0ae24325 581 print "ERROR: (SNMP) OpenManage is not installed or is not working correctly\n";
669797e1 582 exit $E_UNKNOWN;
583 }
584 return;
585}
586
587#
588# Detecting blade via SNMP
589#
590sub snmp_detect_blade {
591 my $DellBaseBoardType = '1.3.6.1.4.1.674.10892.1.300.80.1.7.1.1';
592 my $result = $snmp_session->get_request(-varbindlist => [$DellBaseBoardType]);
593
594 # Identify blade. Older models (4th and 5th gen models) and/or old
595 # OMSA (4.x) don't have this OID. If we get "noSuchInstance" or
596 # similar, we assume that this isn't a blade
5c370da3 597 if (exists $result->{$DellBaseBoardType} && $result->{$DellBaseBoardType} eq '3') {
669797e1 598 $blade = 1;
599 }
600 return;
601}
602
603#
604# Locate the omreport binary
605#
606sub find_omreport {
ac760e0d 607 # If user has specified path to omreport
608 if (defined $opt{omreport} and -x $opt{omreport}) {
60994ca4 609 $omreport = qq{"$opt{omreport}"};
ac760e0d 610 return;
611 }
612
669797e1 613 # Possible full paths for omreport
614 my @omreport_paths
615 = (
616 '/usr/bin/omreport', # default on Linux
6a050646 617 '/opt/dell/srvadmin/bin/omreport', # default on Linux with OMSA 6.2.0
669797e1 618 '/opt/dell/srvadmin/oma/bin/omreport.sh', # alternate on Linux
619 '/opt/dell/srvadmin/oma/bin/omreport', # alternate on Linux
9025e83f 620 'C:\Program Files (x86)\Dell\SysMgt\oma\bin\omreport.exe', # default on Windows x64
621 'C:\Program Files\Dell\SysMgt\oma\bin\omreport.exe', # default on Windows x32
421b6c77 622 'c:\progra~1\dell\sysmgt\oma\bin\omreport.exe', # 8bit legacy default on Windows x32
623 'c:\progra~2\dell\sysmgt\oma\bin\omreport.exe', # 8bit legacy default on Windows x64
669797e1 624 );
625
626 # Find the one to use
627 OMREPORT_PATH:
628 foreach my $bin (@omreport_paths) {
629 if (-x $bin) {
60347693 630 $omreport = qq{"$bin"};
669797e1 631 last OMREPORT_PATH;
632 }
633 }
634
635 # Exit with status=UNKNOWN if OM is not installed, or we don't
636 # have permission to execute the binary
637 if (!defined $omreport) {
0ae24325 638 print "ERROR: Dell OpenManage Server Administrator (OMSA) is not installed\n";
669797e1 639 exit $E_UNKNOWN;
640 }
641 return;
642}
643
644#
645# Checks output from 'omreport -?' and searches for arguments to
646# omreport, to accommodate deprecated options "chassis" and "system"
647# (on newer hardware), as well as blade servers.
648#
649sub check_omreport_options {
650 foreach (@{ run_command("$omreport -? 2>&1") }) {
651 if (m/\A servermodule /xms) {
652 # If "servermodule" argument to omreport exists, use it
653 # instead of argument "system"
654 $omopt_system = 'servermodule';
655 }
656 elsif (m/\A mainsystem /xms) {
657 # If "mainsystem" argument to omreport exists, use it
658 # instead of argument "chassis"
659 $omopt_chassis = 'mainsystem';
660 }
661 elsif (m/\A modularenclosure /xms) {
662 # If "modularenclusure" argument to omreport exists, assume
663 # that this is a blade
664 $blade = 1;
665 }
666 }
667 return;
668}
669
670#
671# Read the blacklist option and return a hash containing the
672# blacklisted components
673#
674sub get_blacklist {
675 my @bl = ();
676 my %blacklist = ();
677
678 if (scalar @{ $opt{blacklist} } >= 0) {
679 foreach my $black (@{ $opt{blacklist} }) {
680 my $tmp = q{};
681 if (-f $black) {
682 open my $BL, '<', $black
683 or do { report('other', "Couldn't open blacklist file $black: $!", $E_UNKNOWN)
684 and return {} };
685 $tmp = <$BL>;
686 close $BL;
687 chomp $tmp;
688 }
689 else {
690 $tmp = $black;
691 }
692 push @bl, $tmp;
693 }
694 }
695
696 return {} if $#bl < 0;
697
698 # Parse blacklist string, put in hash
699 foreach my $black (@bl) {
700 my @comps = split m{/}xms, $black;
701 foreach my $c (@comps) {
702 next if $c !~ m/=/xms;
703 my ($key, $val) = split /=/xms, $c;
704 my @vals = split /,/xms, $val;
705 $blacklist{$key} = \@vals;
706 }
707 }
708
709 return \%blacklist;
710}
711
712#
713# Read the check option and adjust the hash %check, which is a rough
714# list of components to be checked
715#
716sub adjust_checks {
717 my @cl = ();
718
719 # Adjust checking based on the '--all' option
720 if ($opt{all}) {
721 # Check option usage
722 if (defined $opt{only} and $opt{only} !~ m{\A critical|warning \z}xms) {
723 print qq{ERROR: Wrong simultaneous usage of the "--all" and "--only" options\n};
724 exit $E_UNKNOWN;
725 }
726 if (scalar @{ $opt{check} } > 0) {
727 print qq{ERROR: Wrong simultaneous usage of the "--all" and "--check" options\n};
728 exit $E_UNKNOWN;
729 }
730
731 # set the check hash to check everything
732 map { $_ = 1 } values %check;
733
734 return;
735 }
736
737 # Adjust checking based on the '--only' option
738 if (defined $opt{only} and $opt{only} !~ m{\A critical|warning \z}xms) {
739 # Check option usage
740 if (scalar @{ $opt{check} } > 0) {
741 print qq{ERROR: Wrong simultaneous usage of the "--only" and "--check" options\n};
742 exit $E_UNKNOWN;
743 }
a2bbb2c1 744 if (! exists $check{$opt{only}} && $opt{only} ne 'chassis') {
669797e1 745 print qq{ERROR: "$opt{only}" is not a known keyword for the "--only" option\n};
746 exit $E_UNKNOWN;
747 }
748
749 # reset the check hash
750 map { $_ = 0 } values %check;
751
752 # adjust the check hash
753 if ($opt{only} eq 'chassis') {
754 map { $check{$_} = 1 } qw(memory fans power temp cpu voltage
755 batteries amperage intrusion esmhealth);
756 }
757 else {
758 $check{$opt{only}} = 1;
759 }
760
761 return;
762 }
763
764 # Adjust checking based on the '--check' option
765 if (scalar @{ $opt{check} } >= 0) {
766 foreach my $check (@{ $opt{check} }) {
767 my $tmp = q{};
768 if (-f $check) {
769 open my $CL, '<', $check
770 or do { report('other', "Couldn't open check file $check: $!", $E_UNKNOWN) and return };
771 $tmp = <$CL>;
772 close $CL;
773 }
774 else {
775 $tmp = $check;
776 }
777 push @cl, $tmp;
778 }
779 }
780
781 return if $#cl < 0;
782
783 # Parse checklist string, put in hash
784 foreach my $check (@cl) {
785 my @checks = split /,/xms, $check;
786 foreach my $c (@checks) {
787 next if $c !~ m/=/xms;
788 my ($key, $val) = split /=/xms, $c;
789 $check{$key} = $val;
790 }
791 }
792
793 # Check if we should check global health status
794 CHECK_KEY:
795 foreach (keys %check) {
796 next CHECK_KEY if $_ eq 'esmlog'; # not part of global status
797 next CHECK_KEY if $_ eq 'alertlog'; # not part of global status
798
799 if ($check{$_} == 0) { # found something with checking turned off
800 $global = 0;
801 last CHECK_KEY;
802 }
803 }
804
805 return;
806}
807
808#
809# Runs omreport and returns an array of anonymous hashes containing
810# the output.
811# Takes one argument: string containing parameters to omreport
812#
813sub run_omreport {
814 my $command = shift;
815 my @output = ();
816 my @keys = ();
817
818 # Errors that are OK. Some low-end poweredge (and blades) models
819 # don't have RAID controllers, intrusion detection sensor, or
820 # redundant/instrumented power supplies etc.
821 my $ok_errors
822 = qr{
823 Intrusion\sinformation\sis\snot\sfound\sfor\sthis\ssystem # No intrusion probe
824 | No\sinstrumented\spower\ssupplies\sfound\son\sthis\ssystem # No instrumented PS (blades/low-end)
825 | No\scontrollers\sfound # No RAID controller
826 | No\sbattery\sprobes\sfound\son\sthis\ssystem # No battery probes
827 | Invalid\scommand:\spwrmonitoring # Older OMSAs lack this command(?)
9df480be 828# | Current\sprobes\snot\sfound # OMSA + RHEL5.4 bug
669797e1 829 }xms;
830
831 # Errors that are OK on blade servers
832 my $ok_blade_errors
833 = qr{
834 No\sfan\sprobes\sfound\son\sthis\ssystem # No fan probes
835 }xms;
836
837 # Run omreport and fetch output
838 my $rawtext = slurp_command("$omreport $command -fmt ssv 2>&1");
839 return [] if !defined $rawtext;
840
841 # Workaround for Openmanage BUG introduced in OMSA 5.5.0
4a4baf82 842 $rawtext =~ s{\n;}{;}gxms if $command eq 'storage controller';
843
844 # Openmanage sometimes puts a linebreak between "Error" and the
845 # actual error text
49a51b07 846 $rawtext =~ s{^Error\s*\n}{Error: }xms;
669797e1 847
848 # Parse output, store in array
4a4baf82 849 for ((split m{\n}xms, $rawtext)) {
850 if (m{\AError}xms) {
669797e1 851 next if m{$ok_errors}xms;
852 next if ($blade and m{$ok_blade_errors}xms);
853 report('other', "Problem running 'omreport $command': $_", $E_UNKNOWN);
854 }
855
856 next if !m/(.*?;){2}/xms; # ignore lines with less than 3 fields
857 my @vals = split /;/xms;
8ce893fd 858 if ($vals[0] =~ m/\A (Index|ID|Severity|Processor|Current\sSpeed) \z/xms) {
669797e1 859 @keys = @vals;
860 }
861 else {
862 my $i = 0;
863 push @output, { map { $_ => $vals[$i++] } @keys };
864 }
865
866 }
867
868 # Finally, return the collected information
869 return \@output;
870}
871
872
873#
874# Checks if a component is blacklisted. Returns 1 if the component is
875# blacklisted, 0 otherwise. Takes two arguments:
876# arg1: component name
877# arg2: component id or index
878#
879sub blacklisted {
880 my $name = shift; # component name
881 my $id = shift; # component id
882 my $ret = 0; # return value
883
884 if (defined $blacklist{$name}) {
885 foreach my $comp (@{ $blacklist{$name} }) {
d4c27ad8 886 if (defined $id and ($comp eq $id or uc($comp) eq 'ALL')) {
669797e1 887 $ret = 1;
888 }
889 }
890 }
891
892 return $ret;
893}
894
895# Converts the NexusID from SNMP to our version
896sub convert_nexus {
897 my $nexus = shift;
898 $nexus =~ s{\A \\}{}xms;
899 $nexus =~ s{\\}{:}gxms;
900 return $nexus;
901}
902
903# Sets custom temperature thresholds based on user supplied options
904sub custom_temperature_thresholds {
905 my $type = shift; # type of threshold, either w (warning) or c (critical)
906 my %thres = (); # will contain the thresholds
907 my @limits = (); # holds the input
908
909 my @opt = $type eq 'w' ? @{ $opt{warning} } : @{ $opt{critical} };
910
911 if (scalar @opt >= 0) {
912 foreach my $t (@opt) {
913 my $tmp = q{};
914 if (-f $t) {
915 open my $F, '<', $t
916 or do { report('other', "Couldn't open temperature threshold file $t: $!",
917 $E_UNKNOWN) and return {} };
918 $tmp = <$F>;
919 close $F;
920 }
921 else {
922 $tmp = $t;
923 }
924 push @limits, $tmp;
925 }
926 }
927
928 # Parse checklist string, put in hash
929 foreach my $th (@limits) {
930 my @tmp = split m{,}xms, $th;
931 foreach my $t (@tmp) {
932 next if $t !~ m{=}xms;
933 my ($key, $val) = split m{=}xms, $t;
934 if ($val =~ m{/}xms) {
935 my ($max, $min) = split m{/}xms, $val;
936 $thres{$key}{max} = $max;
937 $thres{$key}{min} = $min;
938 }
939 else {
940 $thres{$key}{max} = $val;
941 }
942 }
943 }
944
945 return \%thres;
946}
947
948
949# Gets the output from SNMP result according to the OIDs checked
950sub get_snmp_output {
951 my ($result,$oidref) = @_;
b0e15fc9 952 my @temp = ();
669797e1 953 my @output = ();
954
955 foreach my $oid (keys %{ $result }) {
b0e15fc9 956 my $short = $oid;
f47687c4 957 $short =~ s{\s}{}gxms; # remove whitespace
958 $short =~ s{\A (.+) \. (\d+) \z}{$1}xms; # remove last number
b0e15fc9 959 my $id = $2;
960 if (exists $oidref->{$short}) {
961 $temp[$id]{$oidref->{$short}} = $result->{$oid};
669797e1 962 }
963 }
b0e15fc9 964
965 # Remove any empty indexes
966 foreach my $out (@temp) {
967 if (defined $out) {
968 push @output, $out;
969 }
970 }
971
669797e1 972 return \@output;
973}
974
975
976# Map the controller or other item in-place
977sub map_item {
978 my ($key, $val, $list) = @_;
979
980 foreach my $lst (@{ $list }) {
981 if (!exists $lst->{$key}) {
982 $lst->{$key} = $val;
983 }
984 }
985 return;
986}
987
988# Return the URL for official Dell documentation for a specific
989# PowerEdge server
990sub documentation_url {
991 my $model = shift;
992
993 # create model short form, e.g. "r710"
994 $model =~ s{\A PowerEdge \s (.+?) \z}{lc($1)}exms;
995
996 # special case for blades (e.g. M600, M710), they have common
997 # documentation
998 $model =~ s{\A m\d+ \z}{m}xms;
999
1000 return 'http://support.dell.com/support/edocs/systems/pe' . $model . '/';
1001}
1002
1003# Return the URL for warranty information for a server with a given
1004# serial number (servicetag)
1005sub warranty_url {
1006 my $tag = shift;
1007
1008 # Dell support sites for different parts of the world
1009 my %supportsite
1010 = (
1011 'emea' => 'http://support.euro.dell.com/support/topics/topic.aspx/emea/shared/support/my_systems_info/',
1012 'ap' => 'http://supportapj.dell.com/support/topics/topic.aspx/ap/shared/support/my_systems_info/en/details?',
1013 'glob' => 'http://support.dell.com/support/topics/global.aspx/support/my_systems_info/details?',
1014 );
1015
1016 # warranty URLs for different country codes
1017 my %url
1018 = (
1019 # EMEA
1020 'at' => $supportsite{emea} . 'de/details?c=at&l=de&ServiceTag=', # Austria
1021 'be' => $supportsite{emea} . 'nl/details?c=be&l=nl&ServiceTag=', # Belgium
1022 'cz' => $supportsite{emea} . 'cs/details?c=cz&l=cs&ServiceTag=', # Czech Republic
1023 'de' => $supportsite{emea} . 'de/details?c=de&l=de&ServiceTag=', # Germany
1024 'dk' => $supportsite{emea} . 'da/details?c=dk&l=da&ServiceTag=', # Denmark
1025 'es' => $supportsite{emea} . 'es/details?c=es&l=es&ServiceTag=', # Spain
1026 'fi' => $supportsite{emea} . 'fi/details?c=fi&l=fi&ServiceTag=', # Finland
1027 'fr' => $supportsite{emea} . 'fr/details?c=fr&l=fr&ServiceTag=', # France
1028 'gr' => $supportsite{emea} . 'en/details?c=gr&l=el&ServiceTag=', # Greece
1029 'it' => $supportsite{emea} . 'it/details?c=it&l=it&ServiceTag=', # Italy
1030 'il' => $supportsite{emea} . 'en/details?c=il&l=en&ServiceTag=', # Israel
1031 'me' => $supportsite{emea} . 'en/details?c=me&l=en&ServiceTag=', # Middle East
1032 'no' => $supportsite{emea} . 'no/details?c=no&l=no&ServiceTag=', # Norway
1033 'nl' => $supportsite{emea} . 'nl/details?c=nl&l=nl&ServiceTag=', # The Netherlands
1034 'pl' => $supportsite{emea} . 'pl/details?c=pl&l=pl&ServiceTag=', # Poland
1035 'pt' => $supportsite{emea} . 'en/details?c=pt&l=pt&ServiceTag=', # Portugal
1036 'ru' => $supportsite{emea} . 'ru/details?c=ru&l=ru&ServiceTag=', # Russia
1037 'se' => $supportsite{emea} . 'sv/details?c=se&l=sv&ServiceTag=', # Sweden
1038 'uk' => $supportsite{emea} . 'en/details?c=uk&l=en&ServiceTag=', # United Kingdom
1039 'za' => $supportsite{emea} . 'en/details?c=za&l=en&ServiceTag=', # South Africa
1040 # America
1041 'br' => $supportsite{glob} . 'c=br&l=pt&ServiceTag=', # Brazil
1042 'ca' => $supportsite{glob} . 'c=ca&l=en&ServiceTag=', # Canada
1043 'mx' => $supportsite{glob} . 'c=mx&l=es&ServiceTag=', # Mexico
1044 'us' => $supportsite{glob} . 'c=us&l=en&ServiceTag=', # USA
1045 # Asia/Pacific
1046 'au' => $supportsite{ap} . 'c=au&l=en&ServiceTag=', # Australia
1047 'cn' => $supportsite{ap} . 'c=cn&l=zh&ServiceTag=', # China
1048 'in' => $supportsite{ap} . 'c=in&l=en&ServiceTag=', # India
1049 # default fallback
1050 'XX' => $supportsite{glob} . 'ServiceTag=', # default
1051 );
1052
1053 if (exists $url{$opt{htmlinfo}}) {
1054 return $url{$opt{htmlinfo}} . $tag;
1055 }
1056 else {
1057 return $url{XX} . $tag;
1058 }
1059}
1060
1061
912d8679 1062# This helper function returns the corresponding value of a hash key,
1063# but takes into account that the key may not exist
1064sub get_hashval {
1065 my $key = shift || return undef;
1066 my $hash = shift;
1067 return exists $hash->{$key} ? $hash->{$key} : "Undefined value $key";
1068}
1069
1070
669797e1 1071
1072#---------------------------------------------------------------------
1073# Check functions
1074#---------------------------------------------------------------------
1075
1076#-----------------------------------------
1077# Check global health status
1078#-----------------------------------------
1079sub check_global {
1080 my $health = $E_OK;
1081
1082 if ($snmp) {
1083 #
1084 # Checks global status, i.e. both storage and chassis
1085 #
1086 my $systemStateGlobalSystemStatus = '1.3.6.1.4.1.674.10892.1.200.10.1.2.1';
1087 my $result = $snmp_session->get_request(-varbindlist => [$systemStateGlobalSystemStatus]);
1088 if (!defined $result) {
98b224a3 1089 printf "SNMP ERROR [global]: %s\n", $snmp_error;
669797e1 1090 exit $E_UNKNOWN;
1091 }
1092 $health = $status2nagios{$snmp_status{$result->{$systemStateGlobalSystemStatus}}};
1093 }
1094 else {
1095 #
1096 # NB! This does not check storage, only chassis...
1097 #
1098 foreach (@{ run_command("$omreport $omopt_system -fmt ssv") }) {
1099 next if !m/;/xms;
1100 next if m/\A SEVERITY;COMPONENT/xms;
1101 if (m/\A (.+?);Main\sSystem(\sChassis)? /xms) {
1102 $health = $status2nagios{$1};
1103 last;
1104 }
1105 }
1106 }
1107
1108 # Return the status
1109 return $health;
1110}
1111
1112
1113#-----------------------------------------
1114# STORAGE: Check controllers
1115#-----------------------------------------
1116sub check_controllers {
5ecf578c 1117 return if blacklisted('ctrl', 'all');
1118
669797e1 1119 my $id = undef;
1120 my $nexus = undef;
1121 my $name = undef;
1122 my $state = undef;
1123 my $status = undef;
1124 my $minfw = undef;
1125 my $mindr = undef;
1126 my $firmware = undef;
1127 my $driver = undef;
9df480be 1128 my $minstdr = undef; # Minimum required Storport driver version
1129 my $stdr = undef; # Storport driver version
669797e1 1130 my @output = ();
1131
1132 if ($snmp) {
1133 my %ctrl_oid
1134 = (
1135 '1.3.6.1.4.1.674.10893.1.20.130.1.1.1' => 'controllerNumber',
1136 '1.3.6.1.4.1.674.10893.1.20.130.1.1.2' => 'controllerName',
1137 '1.3.6.1.4.1.674.10893.1.20.130.1.1.5' => 'controllerState',
1138 '1.3.6.1.4.1.674.10893.1.20.130.1.1.8' => 'controllerFWVersion',
1139 '1.3.6.1.4.1.674.10893.1.20.130.1.1.38' => 'controllerComponentStatus',
1140 '1.3.6.1.4.1.674.10893.1.20.130.1.1.39' => 'controllerNexusID',
1141 '1.3.6.1.4.1.674.10893.1.20.130.1.1.41' => 'controllerDriverVersion',
1142 '1.3.6.1.4.1.674.10893.1.20.130.1.1.44' => 'controllerMinFWVersion',
1143 '1.3.6.1.4.1.674.10893.1.20.130.1.1.45' => 'controllerMinDriverVersion',
1b3f1f77 1144 '1.3.6.1.4.1.674.10893.1.20.130.1.1.55' => 'controllerStorportDriverVersion',
1145 '1.3.6.1.4.1.674.10893.1.20.130.1.1.56' => 'controllerMinRequiredStorportVer',
669797e1 1146 );
ba199ee0 1147
1148 # We use get_table() here for the odd case where a server has
1149 # two or more controllers, and where some OIDs are missing on
1150 # one of the controllers.
1151 my $controllerTable = '1.3.6.1.4.1.674.10893.1.20.130.1';
1152 my $result = $snmp_session->get_table(-baseoid => $controllerTable);
669797e1 1153
1154 # No controllers is OK
1155 return if !defined $result;
1156
1157 @output = @{ get_snmp_output($result, \%ctrl_oid) };
1158 }
1159 else {
1160 @output = @{ run_omreport('storage controller') };
1161 }
1162
1163 my %ctrl_state
1164 = (
1165 0 => 'Unknown',
1166 1 => 'Ready',
1167 2 => 'Failed',
1168 3 => 'Online',
1169 4 => 'Offline',
1170 6 => 'Degraded',
1171 );
1172
1173 CTRL:
1174 foreach my $out (@output) {
1175 if ($snmp) {
b1f48712 1176# $id = $out->{controllerNumber} - 1;
08c259f3 1177 $name = $out->{controllerName};
912d8679 1178 $state = get_hashval($out->{controllerState}, \%ctrl_state);
08c259f3 1179 $status = $snmp_status{$out->{controllerComponentStatus}};
1180 $minfw = exists $out->{controllerMinFWVersion}
1181 ? $out->{controllerMinFWVersion} : undef;
1182 $mindr = exists $out->{controllerMinDriverVersion}
1183 ? $out->{controllerMinDriverVersion} : undef;
669797e1 1184 $firmware = exists $out->{controllerFWVersion}
1185 ? $out->{controllerFWVersion} : 'N/A';
1186 $driver = exists $out->{controllerDriverVersion}
1187 ? $out->{controllerDriverVersion} : 'N/A';
1b3f1f77 1188 $minstdr = exists $out->{'controllerMinRequiredStorportVer'}
1189 ? $out->{controllerMinRequiredStorportVer} : undef;
1190 $stdr = exists $out->{controllerStorportDriverVersion}
1191 ? $out->{controllerStorportDriverVersion} : undef;
669797e1 1192 $nexus = convert_nexus($out->{controllerNexusID});
b1f48712 1193 $id = $nexus;
669797e1 1194 }
1195 else {
1196 $id = $out->{ID};
1197 $name = $out->{Name};
1198 $state = $out->{State};
1199 $status = $out->{Status};
1200 $minfw = $out->{'Minimum Required Firmware Version'} ne 'Not Applicable'
1201 ? $out->{'Minimum Required Firmware Version'} : undef;
1202 $mindr = $out->{'Minimum Required Driver Version'} ne 'Not Applicable'
1203 ? $out->{'Minimum Required Driver Version'} : undef;
1204 $firmware = $out->{'Firmware Version'} ne 'Not Applicable'
1205 ? $out->{'Firmware Version'} : 'N/A';
1206 $driver = $out->{'Driver Version'} ne 'Not Applicable'
1207 ? $out->{'Driver Version'} : 'N/A';
f86e57b8 1208 $minstdr = (exists $out->{'Minimum Required Storport Driver Version'}
1209 and $out->{'Minimum Required Storport Driver Version'} ne 'Not Applicable')
08c259f3 1210 ? $out->{'Minimum Required Storport Driver Version'} : undef;
f86e57b8 1211 $stdr = (exists $out->{'Storport Driver Version'}
1212 and $out->{'Storport Driver Version'} ne 'Not Applicable')
956cf4d1 1213 ? $out->{'Storport Driver Version'} : undef;
669797e1 1214 $nexus = $id;
1215 }
1216
1217 $name =~ s{\s+\z}{}xms; # remove trailing whitespace
1218 push @controllers, $id;
1219
1220 # Collecting some storage info
1221 $sysinfo{'controller'}{$id}{'id'} = $nexus;
1222 $sysinfo{'controller'}{$id}{'name'} = $name;
1223 $sysinfo{'controller'}{$id}{'driver'} = $driver;
1224 $sysinfo{'controller'}{$id}{'firmware'} = $firmware;
956cf4d1 1225 $sysinfo{'controller'}{$id}{'storport'} = $stdr;
669797e1 1226
b1f48712 1227 # TEST
1228 if ($snmp) {
1229 $snmp_controller{$out->{controllerNumber}} = $nexus;
1230 }
1231
669797e1 1232 next CTRL if blacklisted('ctrl', $nexus);
1233
1234 # Special case: old firmware
1235 if (!blacklisted('ctrl_fw', $id) && defined $minfw) {
1236 chomp $firmware;
98b224a3 1237 my $msg = sprintf q{Controller %d [%s]: Firmware '%s' is out of date},
669797e1 1238 $id, $name, $firmware;
1239 report('storage', $msg, $E_WARNING, $nexus);
1240 }
1241 # Special case: old driver
1242 if (!blacklisted('ctrl_driver', $id) && defined $mindr) {
1243 chomp $driver;
98b224a3 1244 my $msg = sprintf q{Controller %d [%s]: Driver '%s' is out of date},
669797e1 1245 $id, $name, $driver;
1246 report('storage', $msg, $E_WARNING, $nexus);
1247 }
08c259f3 1248 # Special case: old storport driver
1249 if (!blacklisted('ctrl_stdr', $id) && defined $minstdr) {
1250 chomp $stdr;
1251 my $msg = sprintf q{Controller %d [%s]: Storport driver '%s' is out of date},
1252 $id, $name, $stdr;
1253 report('storage', $msg, $E_WARNING, $nexus);
1254 }
669797e1 1255 # Ok
1256 if ($status eq 'Ok' or ($status eq 'Non-Critical'
babe647a 1257 and (defined $minfw or defined $mindr or defined $minstdr))) {
98b224a3 1258 my $msg = sprintf 'Controller %d [%s] is %s',
669797e1 1259 $id, $name, $state;
1260 report('storage', $msg, $E_OK, $nexus);
1261 }
1262 # Default
1263 else {
98b224a3 1264 my $msg = sprintf 'Controller %d [%s] needs attention: %s',
669797e1 1265 $id, $name, $state;
1266 report('storage', $msg, $status2nagios{$status}, $nexus);
1267 }
1268 }
1269 return;
1270}
1271
1272
1273#-----------------------------------------
1274# STORAGE: Check physical drives
1275#-----------------------------------------
1276sub check_physical_disks {
1277 return if $#controllers == -1;
c8eb5019 1278 return if blacklisted('pdisk', 'all');
669797e1 1279
1280 my $id = undef;
1281 my $nexus = undef;
1282 my $name = undef;
1283 my $state = undef;
1284 my $status = undef;
1285 my $fpred = undef;
1286 my $progr = undef;
1287 my $ctrl = undef;
1288 my $vendor = undef; # disk vendor
1289 my $product = undef; # product ID
1290 my $capacity = undef; # disk length (size) in bytes
ac93da95 1291 my $media = undef; # media type (e.g. HDD, SSD)
1292 my $bus = undef; # bus protocol (e.g. SAS, SATA)
e26aa120 1293 my $spare = undef; # spare state (e.g. global hotspare)
669797e1 1294 my @output = ();
1295
1296 if ($snmp) {
1297 my %pdisk_oid
1298 = (
1299 '1.3.6.1.4.1.674.10893.1.20.130.4.1.1' => 'arrayDiskNumber',
1300 '1.3.6.1.4.1.674.10893.1.20.130.4.1.2' => 'arrayDiskName',
1301 '1.3.6.1.4.1.674.10893.1.20.130.4.1.3' => 'arrayDiskVendor',
1302 '1.3.6.1.4.1.674.10893.1.20.130.4.1.4' => 'arrayDiskState',
1303 '1.3.6.1.4.1.674.10893.1.20.130.4.1.6' => 'arrayDiskProductID',
1304 '1.3.6.1.4.1.674.10893.1.20.130.4.1.9' => 'arrayDiskEnclosureID',
1305 '1.3.6.1.4.1.674.10893.1.20.130.4.1.10' => 'arrayDiskChannel',
1306 '1.3.6.1.4.1.674.10893.1.20.130.4.1.11' => 'arrayDiskLengthInMB',
1307 '1.3.6.1.4.1.674.10893.1.20.130.4.1.15' => 'arrayDiskTargetID',
ac93da95 1308 '1.3.6.1.4.1.674.10893.1.20.130.4.1.21' => 'arrayDiskBusType',
e26aa120 1309 '1.3.6.1.4.1.674.10893.1.20.130.4.1.22' => 'arrayDiskSpareState',
669797e1 1310 '1.3.6.1.4.1.674.10893.1.20.130.4.1.24' => 'arrayDiskComponentStatus',
1311 '1.3.6.1.4.1.674.10893.1.20.130.4.1.26' => 'arrayDiskNexusID',
1312 '1.3.6.1.4.1.674.10893.1.20.130.4.1.31' => 'arrayDiskSmartAlertIndication',
ac93da95 1313 '1.3.6.1.4.1.674.10893.1.20.130.4.1.35' => 'arrayDiskMediaType',
669797e1 1314 '1.3.6.1.4.1.674.10893.1.20.130.5.1.7' => 'arrayDiskEnclosureConnectionControllerNumber',
c11849d6 1315 '1.3.6.1.4.1.674.10893.1.20.130.6.1.7' => 'arrayDiskChannelConnectionControllerNumber',
669797e1 1316 );
4cabd748 1317 my $result = undef;
1318 if ($opt{use_get_table}) {
1319 my $arrayDiskTable = '1.3.6.1.4.1.674.10893.1.20.130.4';
1320 my $arrayDiskEnclosureConnectionControllerNumber = '1.3.6.1.4.1.674.10893.1.20.130.5.1.7';
1321 my $arrayDiskChannelConnectionControllerNumber = '1.3.6.1.4.1.674.10893.1.20.130.6.1.7';
1322
1323 $result = $snmp_session->get_table(-baseoid => $arrayDiskTable);
1324 my $ext1 = $snmp_session->get_table(-baseoid => $arrayDiskEnclosureConnectionControllerNumber);
1325 my $ext2 = $snmp_session->get_table(-baseoid => $arrayDiskChannelConnectionControllerNumber);
1326
1327 if (defined $result) {
1328 defined $ext1 && map { $$result{$_} = $$ext1{$_} } keys %{ $ext1 };
1329 defined $ext2 && map { $$result{$_} = $$ext2{$_} } keys %{ $ext2 };
1330 }
1331 }
1332 else {
1333 $result = $snmp_session->get_entries(-columns => [keys %pdisk_oid]);
1334 }
669797e1 1335
1336 if (!defined $result) {
98b224a3 1337 printf "SNMP ERROR [storage / pdisk]: %s.\n", $snmp_session->error;
669797e1 1338 $snmp_session->close;
1339 exit $E_UNKNOWN;
1340 }
1341
1342 @output = @{ get_snmp_output($result, \%pdisk_oid) };
1343 }
1344 else {
1345 foreach my $c (@controllers) {
74177368 1346 # This blacklists disks with broken firmware, which includes
1347 # illegal XML characters that makes openmanage choke on itself
1348 next if blacklisted('ctrl_pdisk', $c);
1349
669797e1 1350 push @output, @{ run_omreport("storage pdisk controller=$c") };
1351 map_item('ctrl', $c, \@output);
1352 }
1353 }
1354
e26aa120 1355 my %spare_state
1356 = (
1357 1 => 'VD member', # disk is a member of a virtual disk
1358 2 => 'DG member', # disk is a member of a disk group
1359 3 => 'Global HS', # disk is a global hot spare
1360 4 => 'Dedicated HS', # disk is a dedicated hot spare
1361 5 => 'no', # not a spare
1362 99 => 'n/a', # not applicable
1363 );
1364
ac93da95 1365 my %media_type
1366 = (
1367 1 => 'unknown',
1368 2 => 'HDD',
1369 3 => 'SSD',
1370 );
1371
1372 my %bus_type
1373 = (
1374 1 => 'SCSI',
1375 2 => 'IDE',
1376 3 => 'Fibre Channel',
1377 4 => 'SSA',
1378 6 => 'USB',
1379 7 => 'SATA',
1380 8 => 'SAS',
1381 );
1382
669797e1 1383 my %pdisk_state
1384 = (
1385 0 => 'Unknown',
1386 1 => 'Ready',
1387 2 => 'Failed',
1388 3 => 'Online',
1389 4 => 'Offline',
1390 6 => 'Degraded',
1391 7 => 'Recovering',
1392 11 => 'Removed',
1393 15 => 'Resynching',
e26aa120 1394 22 => 'Replacing', # FIXME: this one is not defined in the OMSA MIBs
669797e1 1395 24 => 'Rebuilding',
1396 25 => 'No Media',
1397 26 => 'Formatting',
1398 28 => 'Diagnostics',
1399 34 => 'Predictive failure',
1400 35 => 'Initializing',
1401 39 => 'Foreign',
1402 40 => 'Clear',
1403 41 => 'Unsupported',
1404 53 => 'Incompatible',
1405 );
1406
1407 # Check physical disks on each of the controllers
1408 PDISK:
1409 foreach my $out (@output) {
1410 if ($snmp) {
1411 $name = $out->{arrayDiskName};
07d224b2 1412 if (exists $out->{arrayDiskEnclosureID}) {
669797e1 1413 $id = join q{:}, ($out->{arrayDiskChannel}, $out->{arrayDiskEnclosureID},
07d224b2 1414 $out->{arrayDiskTargetID});
669797e1 1415 }
1416 else {
1417 $id = join q{:}, ($out->{arrayDiskChannel}, $out->{arrayDiskTargetID});
1418 }
912d8679 1419 $state = get_hashval($out->{arrayDiskState}, \%pdisk_state);
1420 $status = $snmp_status{$out->{arrayDiskComponentStatus}};
355299d9 1421 $fpred = exists $out->{arrayDiskSmartAlertIndication}
1422 && $out->{arrayDiskSmartAlertIndication} == 2 ? 1 : 0;
669797e1 1423 $progr = q{};
669797e1 1424 $nexus = convert_nexus($out->{arrayDiskNexusID});
1425 $vendor = $out->{arrayDiskVendor};
1426 $product = $out->{arrayDiskProductID};
e26aa120 1427 $spare = get_hashval($out->{arrayDiskSpareState}, \%spare_state);
ac93da95 1428 $bus = exists $out->{arrayDiskBusType}
1429 ? get_hashval($out->{arrayDiskBusType}, \%bus_type) : undef;
1430 $media = exists $out->{arrayDiskMediaType}
1431 ? get_hashval($out->{arrayDiskMediaType}, \%media_type) : undef;
669797e1 1432 $capacity = $out->{arrayDiskLengthInMB} * 1024**2;
995447d0 1433
1434 # try to find the controller where the disk belongs
c11849d6 1435 if (exists $out->{arrayDiskEnclosureConnectionControllerNumber}) {
995447d0 1436 # for disks that are attached to an enclosure
b1f48712 1437 $ctrl = $snmp_controller{$out->{arrayDiskEnclosureConnectionControllerNumber}};
c11849d6 1438 }
1439 elsif (exists $out->{arrayDiskChannelConnectionControllerNumber}) {
995447d0 1440 # for disks that are not attached to an enclosure
b1f48712 1441 $ctrl = $snmp_controller{$out->{arrayDiskChannelConnectionControllerNumber}};
c11849d6 1442 }
1443 else {
995447d0 1444 # last resort... use the nexus id (old/broken hardware)
b1f48712 1445 $ctrl = $nexus;
1446 $ctrl =~ s{\A (\d+) : .* \z}{$1}xms;
c11849d6 1447 }
669797e1 1448 }
1449 else {
1450 $id = $out->{'ID'};
1451 $name = $out->{'Name'};
1452 $state = $out->{'State'};
1453 $status = $out->{'Status'};
1454 $fpred = lc($out->{'Failure Predicted'}) eq 'yes' ? 1 : 0;
1455 $progr = ' [' . $out->{'Progress'} . ']';
1456 $ctrl = $out->{'ctrl'};
1457 $nexus = join q{:}, $out->{ctrl}, $id;
1458 $vendor = $out->{'Vendor ID'};
1459 $product = $out->{'Product ID'};
ac93da95 1460 $media = $out->{'Media'};
e26aa120 1461 $spare = $out->{'Hot Spare'};
ac93da95 1462 $bus = $out->{'Bus Protocol'};
669797e1 1463 $capacity = $out->{'Capacity'};
1464 $capacity =~ s{\A .*? \((\d+) \s bytes\) \z}{$1}xms;
1465 }
1466
1467 next PDISK if blacklisted('pdisk', $nexus);
1468 $count{pdisk}++;
1469
1470 $vendor =~ s{\s+\z}{}xms; # remove trailing whitespace
1471 $product =~ s{\s+\z}{}xms; # remove trailing whitespace
1472
e26aa120 1473 # Hot spare stuff
1474 if ($spare eq 'Global') { $spare = 'Global HS'; }
1475 elsif ($spare eq 'Dedicated') { $spare = 'Dedicated HS'; }
1476 elsif ($spare !~ m{\A Global|Dedicated}xms) { $spare = undef; }
1477
669797e1 1478 # Calculate human readable capacity
1479 $capacity = ceil($capacity / 1000**3) >= 1000
49bf41a5 1480 ? sprintf '%.1fTB', ($capacity / 1000**4)
1481 : sprintf '%.0fGB', ($capacity / 1000**3);
1482 $capacity = '450GB' if $capacity eq '449GB'; # quick fix for 450GB disks
1483 $capacity = '300GB' if $capacity eq '299GB'; # quick fix for 300GB disks
1484 $capacity = '146GB' if $capacity eq '147GB'; # quick fix for 146GB disks
f6a98b09 1485 $capacity = '100GB' if $capacity eq '99GB'; # quick fix for 100GB disks
669797e1 1486
1487 # Capitalize only the first letter of the vendor name
1488 $vendor = (substr $vendor, 0, 1) . lc (substr $vendor, 1, length $vendor);
1489
1490 # Remove unnecessary trademark rubbish from vendor name
1491 $vendor =~ s{\(tm\)\z}{}xms;
1492
ac93da95 1493 # bus and media aren't always defined
1494 my $busmedia = q{};
1495 if (defined $bus && defined $media) { $busmedia = "$bus-$media "; }
1496 elsif (defined $bus && ! defined $media) { $busmedia = "$bus "; }
1497 elsif (! defined $bus && defined $media) { $busmedia = "$media "; }
1498
a8b24907 1499 # Special case: Failure predicted
1500 if ($fpred) {
ea0b94b8 1501 my $msg = sprintf '%s [%s %s, %s] on ctrl %d needs attention: Failure Predicted',
1502 $name, $vendor, $product, $capacity, $ctrl;
f2f69da2 1503 $msg .= " ($state)" if $state ne 'Predictive failure';
1504 report('storage', $msg,
1505 ($status2nagios{$status} == $E_CRITICAL ? $E_CRITICAL : $E_WARNING), $nexus);
ea0b94b8 1506 }
c5c69973 1507 # Special case: Rebuilding / Replacing
ea0b94b8 1508 elsif ($state =~ m{\A Rebuilding|Replacing \z}xms) {
ddeae63c 1509 my $msg = sprintf '%s [%s %s, %s] on ctrl %d is %s%s',
1510 $name, $vendor, $product, $capacity, $ctrl, $state, $progr;
669797e1 1511 report('storage', $msg, $E_WARNING, $nexus);
1512 }
1513 # Default
1514 elsif ($status ne 'Ok') {
c11849d6 1515 my $msg = sprintf '%s [%s %s, %s] on ctrl %d needs attention: %s',
1516 $name, $vendor, $product, $capacity, $ctrl, $state;
669797e1 1517 report('storage', $msg, $status2nagios{$status}, $nexus);
1518 }
1519 # Ok
1520 else {
ac93da95 1521 my $msg = sprintf '%s [%s%s] on ctrl %d is %s',
1522 $name, $busmedia, $capacity, $ctrl, $state;
e26aa120 1523 if (defined $spare) { $msg .= " ($spare)"; }
669797e1 1524 report('storage', $msg, $E_OK, $nexus);
1525 }
1526 }
1527 return;
1528}
1529
1530
1531#-----------------------------------------
1532# STORAGE: Check logical drives
1533#-----------------------------------------
1534sub check_virtual_disks {
1535 return if $#controllers == -1;
5ecf578c 1536 return if blacklisted('vdisk', 'all');
669797e1 1537
1538 my $id = undef;
25d04c34 1539 my $name = undef;
669797e1 1540 my $nexus = undef;
1541 my $dev = undef;
1542 my $state = undef;
1543 my $status = undef;
1544 my $layout = undef;
1545 my $size = undef;
1546 my $progr = undef;
25d04c34 1547 my $ctrl = undef;
669797e1 1548 my @output = ();
1549
1550 if ($snmp) {
1551 my %vdisk_oid
1552 = (
669797e1 1553 '1.3.6.1.4.1.674.10893.1.20.140.1.1.3' => 'virtualDiskDeviceName',
1554 '1.3.6.1.4.1.674.10893.1.20.140.1.1.4' => 'virtualDiskState',
1555 '1.3.6.1.4.1.674.10893.1.20.140.1.1.6' => 'virtualDiskLengthInMB',
1556 '1.3.6.1.4.1.674.10893.1.20.140.1.1.13' => 'virtualDiskLayout',
25d04c34 1557 '1.3.6.1.4.1.674.10893.1.20.140.1.1.17' => 'virtualDiskTargetID',
669797e1 1558 '1.3.6.1.4.1.674.10893.1.20.140.1.1.20' => 'virtualDiskComponentStatus',
1559 '1.3.6.1.4.1.674.10893.1.20.140.1.1.21' => 'virtualDiskNexusID',
1560 );
4cabd748 1561 my $result = undef;
1562 if ($opt{use_get_table}) {
1563 my $virtualDiskTable = '1.3.6.1.4.1.674.10893.1.20.140.1';
1564 $result = $snmp_session->get_table(-baseoid => $virtualDiskTable);
1565 }
1566 else {
1567 $result = $snmp_session->get_entries(-columns => [keys %vdisk_oid]);
1568 }
669797e1 1569
1570 # No logical drives is OK
1571 return if !defined $result;
1572
1573 @output = @{ get_snmp_output($result, \%vdisk_oid) };
1574 }
1575 else {
1576 foreach my $c (@controllers) {
1577 push @output, @{ run_omreport("storage vdisk controller=$c") };
1578 map_item('ctrl', $c, \@output);
1579 }
1580 }
1581
1582 my %vdisk_state
1583 = (
1584 0 => 'Unknown',
1585 1 => 'Ready',
1586 2 => 'Failed',
1587 3 => 'Online',
1588 4 => 'Offline',
1589 6 => 'Degraded',
1590 15 => 'Resynching',
1591 16 => 'Regenerating',
1592 24 => 'Rebuilding',
1593 26 => 'Formatting',
1594 32 => 'Reconstructing',
1595 35 => 'Initializing',
1596 36 => 'Background Initialization',
1597 38 => 'Resynching Paused',
1598 52 => 'Permanently Degraded',
1599 54 => 'Degraded Redundancy',
1600 );
1601
1602 my %vdisk_layout
1603 = (
1604 1 => 'Concatenated',
1605 2 => 'RAID-0',
1606 3 => 'RAID-1',
1607 7 => 'RAID-5',
1608 8 => 'RAID-6',
1609 10 => 'RAID-10',
1610 12 => 'RAID-50',
9113fb39 1611 19 => 'Concatenated RAID-1',
669797e1 1612 24 => 'RAID-60',
1613 );
1614
1615 # Check virtual disks on each of the controllers
1616 VDISK:
1617 foreach my $out (@output) {
1618 if ($snmp) {
25d04c34 1619 $id = $out->{virtualDiskTargetID};
669797e1 1620 $dev = $out->{virtualDiskDeviceName};
912d8679 1621 $state = get_hashval($out->{virtualDiskState}, \%vdisk_state);
1622 $layout = get_hashval($out->{virtualDiskLayout}, \%vdisk_layout);
669797e1 1623 $status = $snmp_status{$out->{virtualDiskComponentStatus}};
669797e1 1624 $size = sprintf '%.2f GB', $out->{virtualDiskLengthInMB} / 1024;
1625 $progr = q{}; # can't get this from SNMP(?)
1626 $nexus = convert_nexus($out->{virtualDiskNexusID});
1627 }
1628 else {
1629 $id = $out->{ID};
1630 $dev = $out->{'Device Name'};
1631 $state = $out->{State};
1632 $status = $out->{Status};
1633 $layout = $out->{Layout};
1634 $size = $out->{Size};
1635 $progr = ' [' . $out->{Progress} . ']';
1636 $size =~ s{\A (.*GB).* \z}{$1}xms;
1637 $nexus = join q{:}, $out->{ctrl}, $id;
25d04c34 1638 $ctrl = $out->{ctrl};
669797e1 1639 }
1640
1641 next VDISK if blacklisted('vdisk', $nexus);
1642 $count{vdisk}++;
1643
04b0f13b 1644 # The device name is undefined sometimes
1645 $dev = q{} if !defined $dev;
1646
669797e1 1647 # Special case: Regenerating
1648 if ($state eq 'Regenerating') {
cad6434b 1649 my $msg = sprintf q{Logical Drive '%s' [%s, %s] is %s%s},
44b3048a 1650 $dev, $layout, $size, $state, $progr;
669797e1 1651 report('storage', $msg, $E_WARNING, $nexus);
1652 }
1653 # Default
1654 elsif ($status ne 'Ok') {
cad6434b 1655 my $msg = sprintf q{Logical Drive '%s' [%s, %s] needs attention: %s},
44b3048a 1656 $dev, $layout, $size, $state;
669797e1 1657 report('storage', $msg, $status2nagios{$status}, $nexus);
1658 }
1659 # Ok
1660 else {
cad6434b 1661 my $msg = sprintf q{Logical Drive '%s' [%s, %s] is %s},
44b3048a 1662 $dev, $layout, $size, $state;
669797e1 1663 report('storage', $msg, $E_OK, $nexus);
1664 }
1665 }
1666 return;
1667}
1668
1669
1670#-----------------------------------------
1671# STORAGE: Check cache batteries
1672#-----------------------------------------
1673sub check_cache_battery {
1674 return if $#controllers == -1;
5ecf578c 1675 return if blacklisted('bat', 'all');
669797e1 1676
1677 my $id = undef;
1678 my $nexus = undef;
1679 my $state = undef;
1680 my $status = undef;
1681 my $ctrl = undef;
1682 my $learn = undef; # learn state
1683 my $pred = undef; # battery's ability to be charged
1684 my @output = ();
1685
1686 if ($snmp) {
1687 my %bat_oid
1688 = (
669797e1 1689 '1.3.6.1.4.1.674.10893.1.20.130.15.1.4' => 'batteryState',
1690 '1.3.6.1.4.1.674.10893.1.20.130.15.1.6' => 'batteryComponentStatus',
1691 '1.3.6.1.4.1.674.10893.1.20.130.15.1.9' => 'batteryNexusID',
1692 '1.3.6.1.4.1.674.10893.1.20.130.15.1.10' => 'batteryPredictedCapacity',
1693 '1.3.6.1.4.1.674.10893.1.20.130.15.1.12' => 'batteryLearnState',
1694 '1.3.6.1.4.1.674.10893.1.20.130.16.1.5' => 'batteryConnectionControllerNumber',
1695 );
4cabd748 1696 my $result = undef;
1697 if ($opt{use_get_table}) {
1698 my $batteryTable = '1.3.6.1.4.1.674.10893.1.20.130.15';
c849fd4c 1699 my $batteryConnectionTable = '1.3.6.1.4.1.674.10893.1.20.130.16';
1700
4cabd748 1701 $result = $snmp_session->get_table(-baseoid => $batteryTable);
c849fd4c 1702 my $ext = $snmp_session->get_table(-baseoid => $batteryConnectionTable);
1703
1704 if (defined $result) {
1705 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
1706 }
4cabd748 1707 }
1708 else {
1709 $result = $snmp_session->get_entries(-columns => [keys %bat_oid]);
1710 }
669797e1 1711
1712 # No cache battery is OK
1713 return if !defined $result;
1714
1715 @output = @{ get_snmp_output($result, \%bat_oid) };
1716 }
1717 else {
1718 foreach my $c (@controllers) {
1719 push @output, @{ run_omreport("storage battery controller=$c") };
1720 map_item('ctrl', $c, \@output);
1721 }
1722 }
1723
1724 my %bat_state
1725 = (
1726 0 => 'Unknown',
1727 1 => 'Ready',
1728 2 => 'Failed',
1729 6 => 'Degraded',
1730 7 => 'Reconditioning',
1731 9 => 'High',
1732 10 => 'Power Low',
1733 12 => 'Charging',
1734 21 => 'Missing',
1735 36 => 'Learning',
1736 );
1737
a49bcfe8 1738 # Specifies the learn state activity of the battery
669797e1 1739 my %bat_learn_state
1740 = (
1741 1 => 'Failed',
1742 2 => 'Active',
1743 4 => 'Timed out',
1744 8 => 'Requested',
1745 16 => 'Idle',
1746 );
1747
a49bcfe8 1748 # This property displays the battery's ability to be charged
669797e1 1749 my %bat_pred_cap
1750 = (
1751 1 => 'Failed', # The battery cannot be charged and needs to be replaced
1752 2 => 'Ready', # The battery can be charged to full capacity
1753 4 => 'Unknown', # The battery is completing a Learn cycle. The charge capacity of the
1754 # battery cannot be determined until the Learn cycle is complete
1755 );
1756
1757 # Check battery on each of the controllers
1758 BATTERY:
1759 foreach my $out (@output) {
1760 if ($snmp) {
669797e1 1761 $status = $snmp_status{$out->{batteryComponentStatus}};
912d8679 1762 $state = get_hashval($out->{batteryState}, \%bat_state);
1763 $learn = get_hashval($out->{batteryLearnState}, \%bat_learn_state);
1764 $pred = get_hashval($out->{batteryPredictedCapacity}, \%bat_pred_cap);
669797e1 1765 $ctrl = $out->{batteryConnectionControllerNumber} - 1;
1766 $nexus = convert_nexus($out->{batteryNexusID});
25d04c34 1767 $id = $nexus;
1768 $id =~ s{\A \d+:(\d+) \z}{$1}xms;
669797e1 1769 }
1770 else {
1771 $id = $out->{'ID'};
1772 $state = $out->{'State'};
1773 $status = $out->{'Status'};
1774 $learn = $out->{'Learn State'};
1775 $pred = $out->{'Predicted Capacity Status'};
1776 $ctrl = $out->{'ctrl'};
1777 $nexus = join q{:}, $out->{ctrl}, $id;
1778 }
1779
1780 next BATTERY if blacklisted('bat', $nexus);
1781
1782 # Special case: Charging
1783 if ($state eq 'Charging') {
50d6bc4a 1784 if ($pred eq 'Failed') {
cad6434b 1785 my $msg = sprintf 'Cache Battery %d in controller %d is %s (%s) [replace battery]',
50d6bc4a 1786 $id, $ctrl, $state, $pred;
1787 report('storage', $msg, $E_CRITICAL, $nexus);
1788 }
1789 else {
1790 next BATTERY if blacklisted('bat_charge', $nexus);
cad6434b 1791 my $msg = sprintf 'Cache Battery %d in controller %d is %s (%s) [probably harmless]',
50d6bc4a 1792 $id, $ctrl, $state, $pred;
1793 report('storage', $msg, $E_WARNING, $nexus);
1794 }
669797e1 1795 }
1796 # Special case: Learning (battery learns its capacity)
1797 elsif ($state eq 'Learning') {
50d6bc4a 1798 if ($learn eq 'Failed') {
cad6434b 1799 my $msg = sprintf 'Cache Battery %d in controller %d is %s (%s)',
50d6bc4a 1800 $id, $ctrl, $state, $learn;
1801 report('storage', $msg, $E_CRITICAL, $nexus);
1802 }
1803 else {
1804 next BATTERY if blacklisted('bat_charge', $nexus);
cad6434b 1805 my $msg = sprintf 'Cache Battery %d in controller %d is %s (%s) [probably harmless]',
50d6bc4a 1806 $id, $ctrl, $state, $learn;
1807 report('storage', $msg, $E_WARNING, $nexus);
1808 }
669797e1 1809 }
1810 # Special case: Power Low (first part of recharge cycle)
1811 elsif ($state eq 'Power Low') {
5a28cf7f 1812 next BATTERY if blacklisted('bat_charge', $nexus);
cad6434b 1813 my $msg = sprintf 'Cache Battery %d in controller %d is %s [probably harmless]',
669797e1 1814 $id, $ctrl, $state;
1815 report('storage', $msg, $E_WARNING, $nexus);
1816 }
5a28cf7f 1817 # Special case: Degraded and Non-Critical (usually part of recharge cycle)
1818 elsif ($state eq 'Degraded' && $status eq 'Non-Critical') {
1819 next BATTERY if blacklisted('bat_charge', $nexus);
cad6434b 1820 my $msg = sprintf 'Cache Battery %d in controller %d is %s (%s) [probably harmless]',
5a28cf7f 1821 $id, $ctrl, $state, $status;
1822 report('storage', $msg, $E_WARNING, $nexus);
1823 }
669797e1 1824 # Default
1825 elsif ($status ne 'Ok') {
cad6434b 1826 my $msg = sprintf 'Cache Battery %d in controller %d needs attention: %s (%s)',
669797e1 1827 $id, $ctrl, $state, $status;
1828 report('storage', $msg, $status2nagios{$status}, $nexus);
1829 }
1830 # Ok
1831 else {
cad6434b 1832 my $msg = sprintf 'Cache Battery %d in controller %d is %s',
669797e1 1833 $id, $ctrl, $state;
1834 report('storage', $msg, $E_OK, $nexus);
1835 }
1836 }
1837 return;
1838}
1839
1840
1841#-----------------------------------------
1842# STORAGE: Check connectors (channels)
1843#-----------------------------------------
1844sub check_connectors {
1845 return if $#controllers == -1;
5ecf578c 1846 return if blacklisted('conn', 'all');
669797e1 1847
1848 my $id = undef;
1849 my $nexus = undef;
1850 my $name = undef;
1851 my $state = undef;
1852 my $status = undef;
1853 my $type = undef;
1854 my $ctrl = undef;
1855 my @output = ();
1856
1857 if ($snmp) {
1858 my %conn_oid
1859 = (
1860 '1.3.6.1.4.1.674.10893.1.20.130.2.1.1' => 'channelNumber',
1861 '1.3.6.1.4.1.674.10893.1.20.130.2.1.2' => 'channelName',
1862 '1.3.6.1.4.1.674.10893.1.20.130.2.1.3' => 'channelState',
1863 '1.3.6.1.4.1.674.10893.1.20.130.2.1.8' => 'channelComponentStatus',
1864 '1.3.6.1.4.1.674.10893.1.20.130.2.1.9' => 'channelNexusID',
1865 '1.3.6.1.4.1.674.10893.1.20.130.2.1.11' => 'channelBusType',
1866 );
4cabd748 1867 my $result = undef;
1868 if ($opt{use_get_table}) {
1869 my $channelTable = '1.3.6.1.4.1.674.10893.1.20.130.2';
1870 $result = $snmp_session->get_table(-baseoid => $channelTable);
1871 }
1872 else {
1873 $result = $snmp_session->get_entries(-columns => [keys %conn_oid]);
1874 }
669797e1 1875
1876 if (!defined $result) {
98b224a3 1877 printf "SNMP ERROR [storage / channel]: %s.\n", $snmp_session->error;
669797e1 1878 $snmp_session->close;
1879 exit $E_UNKNOWN;
1880 }
1881
1882 @output = @{ get_snmp_output($result, \%conn_oid) };
1883 }
1884 else {
1885 foreach my $c (@controllers) {
1886 push @output, @{ run_omreport("storage connector controller=$c") };
1887 map_item('ctrl', $c, \@output);
1888 }
1889 }
1890
1891 my %conn_state
1892 = (
1893 0 => 'Unknown',
1894 1 => 'Ready',
1895 2 => 'Failed',
1896 3 => 'Online',
1897 4 => 'Offline',
1898 6 => 'Degraded',
1899 );
1900
1901 my %conn_bustype
1902 = (
1903 1 => 'SCSI',
1904 2 => 'IDE',
1905 3 => 'Fibre Channel',
1906 4 => 'SSA',
1907 6 => 'USB',
1908 7 => 'SATA',
1909 8 => 'SAS',
1910 );
1911
1912 # Check connectors on each of the controllers
1913 CHANNEL:
1914 foreach my $out (@output) {
1915 if ($snmp) {
1916 $id = $out->{channelNumber} - 1;
1917 $name = $out->{channelName};
669797e1 1918 $status = $snmp_status{$out->{channelComponentStatus}};
912d8679 1919 $state = get_hashval($out->{channelState}, \%conn_state);
1920 $type = get_hashval($out->{channelBusType}, \%conn_bustype);
669797e1 1921 $nexus = convert_nexus($out->{channelNexusID});
1922 $ctrl = $nexus;
1923 $ctrl =~ s{(\d+):\d+}{$1}xms;
1924 }
1925 else {
1926 $id = $out->{'ID'};
1927 $name = $out->{'Name'};
1928 $state = $out->{'State'};
1929 $status = $out->{'Status'};
1930 $type = $out->{'Connector Type'};
1931 $ctrl = $out->{ctrl};
1932 $nexus = join q{:}, $out->{ctrl}, $id;
1933 }
1934
1935 next CHANNEL if blacklisted('conn', $nexus);
1936
355299d9 1937 # workaround for ancient OMSA versions
1938 if (! defined $type) { $type = 'unknown'; }
1939
98b224a3 1940 my $msg = sprintf '%s [%s] on controller %d is %s',
669797e1 1941 $name, $type, $ctrl, $state;
1942 report('storage', $msg, $status2nagios{$status}, $nexus);
1943 }
1944 return;
1945}
1946
1947
1948#-----------------------------------------
1949# STORAGE: Check enclosures
1950#-----------------------------------------
1951sub check_enclosures {
5ecf578c 1952 return if blacklisted('encl', 'all');
1953
669797e1 1954 my $id = undef;
1955 my $nexus = undef;
1956 my $name = undef;
1957 my $state = undef;
1958 my $status = undef;
1959 my $firmware = undef;
25d04c34 1960 my $ctrl = undef;
3fc06a4b 1961 my $occupied_slots = undef; # number of occupied slots
1962 my $total_slots = undef; # number of total slots
669797e1 1963 my @output = ();
1964
1965 if ($snmp) {
1966 my %encl_oid
1967 = (
1968 '1.3.6.1.4.1.674.10893.1.20.130.3.1.1' => 'enclosureNumber',
1969 '1.3.6.1.4.1.674.10893.1.20.130.3.1.2' => 'enclosureName',
1970 '1.3.6.1.4.1.674.10893.1.20.130.3.1.4' => 'enclosureState',
1971 '1.3.6.1.4.1.674.10893.1.20.130.3.1.19' => 'enclosureChannelNumber',
1972 '1.3.6.1.4.1.674.10893.1.20.130.3.1.24' => 'enclosureComponentStatus',
1973 '1.3.6.1.4.1.674.10893.1.20.130.3.1.25' => 'enclosureNexusID',
1974 '1.3.6.1.4.1.674.10893.1.20.130.3.1.26' => 'enclosureFirmwareVersion',
3fc06a4b 1975 '1.3.6.1.4.1.674.10893.1.20.130.3.1.31' => 'enclosureOccupiedSlotCount', # new in OMSA 6.3.0
1976 '1.3.6.1.4.1.674.10893.1.20.130.3.1.32' => 'enclosureTotalSlots', # new in OMSA 6.3.0
669797e1 1977 );
4cabd748 1978 my $result = undef;
1979 if ($opt{use_get_table}) {
1980 my $enclosureTable = '1.3.6.1.4.1.674.10893.1.20.130.3';
1981 $result = $snmp_session->get_table(-baseoid => $enclosureTable);
1982 }
1983 else {
1984 $result = $snmp_session->get_entries(-columns => [keys %encl_oid]);
1985 }
669797e1 1986
1987 # No enclosures is OK
1988 return if !defined $result;
1989
1990 @output = @{ get_snmp_output($result, \%encl_oid) };
1991 }
1992 else {
1993 foreach my $c (@controllers) {
1994 push @output, @{ run_omreport("storage enclosure controller=$c") };
1995 map_item('ctrl', $c, \@output);
1996 }
1997 }
1998
1999 my %encl_state
2000 = (
2001 0 => 'Unknown',
2002 1 => 'Ready',
2003 2 => 'Failed',
2004 3 => 'Online',
2005 4 => 'Offline',
2006 6 => 'Degraded',
2007 );
2008
2009 ENCLOSURE:
2010 foreach my $out (@output) {
2011 if ($snmp) {
912d8679 2012 $id = $out->{enclosureNumber} - 1;
2013 $name = $out->{enclosureName};
2014 $state = get_hashval($out->{enclosureState}, \%encl_state);
2015 $status = $snmp_status{$out->{enclosureComponentStatus}};
669797e1 2016 $firmware = exists $out->{enclosureFirmwareVersion}
2017 ? $out->{enclosureFirmwareVersion} : 'N/A';
2018 $nexus = convert_nexus($out->{enclosureNexusID});
25d04c34 2019 $ctrl = $nexus;
2020 $ctrl =~ s{\A (\d+):.* \z}{$1}xms;
3fc06a4b 2021 # for the next two, a value of 9999 means feature not available
2022 $occupied_slots = exists $out->{enclosureOccupiedSlotCount}
2023 && $out->{enclosureOccupiedSlotCount} != 9999
2024 ? $out->{enclosureOccupiedSlotCount} : undef;
2025 $total_slots = exists $out->{enclosureTotalSlots}
2026 && $out->{enclosureTotalSlots} != 9999
2027 ? $out->{enclosureTotalSlots} : undef;
669797e1 2028 }
2029 else {
2030 $id = $out->{ID};
2031 $name = $out->{Name};
2032 $state = $out->{State};
2033 $status = $out->{Status};
2034 $firmware = $out->{'Firmware Version'} ne 'Not Applicable'
2035 ? $out->{'Firmware Version'} : 'N/A';
2036 $nexus = join q{:}, $out->{ctrl}, $id;
25d04c34 2037 $ctrl = $out->{ctrl};
669797e1 2038 }
2039
2040 $name =~ s{\s+\z}{}xms; # remove trailing whitespace
2041 $firmware =~ s{\s+\z}{}xms; # remove trailing whitespace
2042
2043 # store enclosure data for future use
b1f48712 2044 if ($snmp) {
2045 $snmp_enclosure{$out->{enclosureNumber}}{id} = $id;
2046 $snmp_enclosure{$out->{enclosureNumber}}{name} = $name;
2047 $snmp_enclosure{$out->{enclosureNumber}}{nexus} = $nexus;
2048 }
661c2c5e 2049 else {
2050 push @enclosures, { 'id' => $id,
2051 'ctrl' => $out->{ctrl},
2052 'name' => $name };
2053 }
669797e1 2054
2055 # Collecting some storage info
2056 $sysinfo{'enclosure'}{$nexus}{'id'} = $nexus;
2057 $sysinfo{'enclosure'}{$nexus}{'name'} = $name;
2058 $sysinfo{'enclosure'}{$nexus}{'firmware'} = $firmware;
2059
2060 next ENCLOSURE if blacklisted('encl', $nexus);
2061
3fc06a4b 2062 my $msg = q{};
2063 if (defined $occupied_slots && defined $total_slots) {
cad6434b 2064 $msg = sprintf 'Enclosure %s [%s, %d/%d slots occupied] on ctrl %d is %s',
3fc06a4b 2065 $nexus, $name, $occupied_slots, $total_slots, $ctrl, $state;
2066 }
2067 else {
2068 $msg = sprintf 'Enclosure %s [%s] on controller %d is %s',
2069 $nexus, $name, $ctrl, $state;
2070 }
669797e1 2071 report('storage', $msg, $status2nagios{$status}, $nexus);
2072 }
2073 return;
2074}
2075
2076
2077#-----------------------------------------
2078# STORAGE: Check enclosure fans
2079#-----------------------------------------
2080sub check_enclosure_fans {
2081 return if $#controllers == -1;
5ecf578c 2082 return if blacklisted('encl_fan', 'all');
669797e1 2083
2084 my $id = undef;
2085 my $nexus = undef;
2086 my $name = undef;
2087 my $state = undef;
2088 my $status = undef;
2089 my $speed = undef;
2090 my $encl_id = undef;
2091 my $encl_name = undef;
2092 my @output = ();
2093
2094 if ($snmp) {
2095 my %fan_oid
2096 = (
2097 '1.3.6.1.4.1.674.10893.1.20.130.7.1.1' => 'fanNumber',
2098 '1.3.6.1.4.1.674.10893.1.20.130.7.1.2' => 'fanName',
2099 '1.3.6.1.4.1.674.10893.1.20.130.7.1.4' => 'fanState',
2100 '1.3.6.1.4.1.674.10893.1.20.130.7.1.11' => 'fanProbeCurrValue',
2101 '1.3.6.1.4.1.674.10893.1.20.130.7.1.15' => 'fanComponentStatus',
2102 '1.3.6.1.4.1.674.10893.1.20.130.7.1.16' => 'fanNexusID',
2103 '1.3.6.1.4.1.674.10893.1.20.130.8.1.4' => 'fanConnectionEnclosureName',
2104 '1.3.6.1.4.1.674.10893.1.20.130.8.1.5' => 'fanConnectionEnclosureNumber',
2105 );
4cabd748 2106 my $result = undef;
2107 if ($opt{use_get_table}) {
2108 my $fanTable = '1.3.6.1.4.1.674.10893.1.20.130.7';
c849fd4c 2109 my $fanConnectionTable = '1.3.6.1.4.1.674.10893.1.20.130.8';
2110
4cabd748 2111 $result = $snmp_session->get_table(-baseoid => $fanTable);
c849fd4c 2112 my $ext = $snmp_session->get_table(-baseoid => $fanConnectionTable);
2113
2114 if (defined $result) {
2115 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
2116 }
4cabd748 2117 }
2118 else {
2119 $result = $snmp_session->get_entries(-columns => [keys %fan_oid]);
2120 }
669797e1 2121
2122 # No enclosure fans is OK
2123 return if !defined $result;
2124
2125 @output = @{ get_snmp_output($result, \%fan_oid) };
2126 }
2127 else {
2128 foreach my $enc (@enclosures) {
2129 push @output, @{ run_omreport("storage enclosure controller=$enc->{ctrl} enclosure=$enc->{id} info=fans") };
2130 map_item('ctrl', $enc->{ctrl}, \@output);
2131 map_item('encl_id', $enc->{id}, \@output);
2132 map_item('encl_name', $enc->{name}, \@output);
2133 }
2134 }
2135
2136 my %fan_state
2137 = (
2138 0 => 'Unknown',
2139 1 => 'Ready',
2140 2 => 'Failed',
2141 3 => 'Online',
2142 4 => 'Offline',
2143 6 => 'Degraded',
2144 21 => 'Missing',
2145 );
2146
2147 # Check fans on each of the enclosures
2148 FAN:
2149 foreach my $out (@output) {
2150 if ($snmp) {
2151 $id = $out->{fanNumber} - 1;
2152 $name = $out->{fanName};
912d8679 2153 $state = get_hashval($out->{fanState}, \%fan_state);
669797e1 2154 $status = $snmp_status{$out->{fanComponentStatus}};
2155 $speed = $out->{fanProbeCurrValue};
669797e1 2156 $encl_name = $out->{fanConnectionEnclosureName};
b1f48712 2157 $encl_id = $snmp_enclosure{$out->{fanConnectionEnclosureNumber}}{nexus};
669797e1 2158 $nexus = convert_nexus($out->{fanNexusID});
2159 }
2160 else {
2161 $id = $out->{'ID'};
2162 $name = $out->{'Name'};
2163 $state = $out->{'State'};
2164 $status = $out->{'Status'};
2165 $speed = $out->{'Speed'};
2166 $encl_id = join q{:}, $out->{ctrl}, $out->{'encl_id'};
2167 $encl_name = $out->{encl_name};
2168 $nexus = join q{:}, $out->{ctrl}, $out->{'encl_id'}, $id;
2169 }
2170
2171 next FAN if blacklisted('encl_fan', $nexus);
2172
2173 # Default
2174 if ($status ne 'Ok') {
98b224a3 2175 my $msg = sprintf '%s in enclosure %s [%s] needs attention: %s',
669797e1 2176 $name, $encl_id, $encl_name, $state;
2177 report('storage', $msg, $status2nagios{$status}, $nexus);
2178 }
2179 # Ok
2180 else {
98b224a3 2181 my $msg = sprintf '%s in enclosure %s [%s] is %s (speed=%s)',
669797e1 2182 $name, $encl_id, $encl_name, $state, $speed;
2183 report('storage', $msg, $E_OK, $nexus);
2184 }
2185 }
2186 return;
2187}
2188
2189
2190#-----------------------------------------
2191# STORAGE: Check enclosure power supplies
2192#-----------------------------------------
2193sub check_enclosure_pwr {
2194 return if $#controllers == -1;
5ecf578c 2195 return if blacklisted('encl_ps', 'all');
669797e1 2196
2197 my $id = undef;
2198 my $nexus = undef;
2199 my $name = undef;
2200 my $state = undef;
2201 my $status = undef;
2202 my $encl_id = undef;
2203 my $encl_name = undef;
2204 my @output = ();
2205
2206 if ($snmp) {
2207 my %ps_oid
2208 = (
2209 '1.3.6.1.4.1.674.10893.1.20.130.9.1.1' => 'powerSupplyNumber',
2210 '1.3.6.1.4.1.674.10893.1.20.130.9.1.2' => 'powerSupplyName',
2211 '1.3.6.1.4.1.674.10893.1.20.130.9.1.4' => 'powerSupplyState',
2212 '1.3.6.1.4.1.674.10893.1.20.130.9.1.9' => 'powerSupplyComponentStatus',
2213 '1.3.6.1.4.1.674.10893.1.20.130.9.1.10' => 'powerSupplyNexusID',
2214 '1.3.6.1.4.1.674.10893.1.20.130.10.1.4' => 'powerSupplyConnectionEnclosureName',
2215 '1.3.6.1.4.1.674.10893.1.20.130.10.1.5' => 'powerSupplyConnectionEnclosureNumber',
2216 );
4cabd748 2217 my $result = undef;
2218 if ($opt{use_get_table}) {
2219 my $powerSupplyTable = '1.3.6.1.4.1.674.10893.1.20.130.9';
c849fd4c 2220 my $powerSupplyConnectionTable = '1.3.6.1.4.1.674.10893.1.20.130.10';
2221
4cabd748 2222 $result = $snmp_session->get_table(-baseoid => $powerSupplyTable);
c849fd4c 2223 my $ext = $snmp_session->get_table(-baseoid => $powerSupplyConnectionTable);
2224
2225 if (defined $result) {
2226 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
2227 }
4cabd748 2228 }
2229 else {
2230 $result = $snmp_session->get_entries(-columns => [keys %ps_oid]);
2231 }
669797e1 2232
2233 # No enclosure power supplies is OK
2234 return if !defined $result;
2235
2236 @output = @{ get_snmp_output($result, \%ps_oid) };
2237 }
2238 else {
2239 foreach my $enc (@enclosures) {
2240 push @output, @{ run_omreport("storage enclosure controller=$enc->{ctrl} enclosure=$enc->{id} info=pwrsupplies") };
2241 map_item('ctrl', $enc->{ctrl}, \@output);
2242 map_item('encl_id', $enc->{id}, \@output);
2243 map_item('encl_name', $enc->{name}, \@output);
2244 }
2245 }
2246
2247 my %ps_state
2248 = (
2249 0 => 'Unknown',
2250 1 => 'Ready',
2251 2 => 'Failed',
2252 5 => 'Not Installed',
2253 6 => 'Degraded',
2254 11 => 'Removed',
2255 21 => 'Missing',
2256 );
2257
2258 # Check power supplies on each of the enclosures
2259 PS:
2260 foreach my $out (@output) {
2261 if ($snmp) {
2262 $id = $out->{powerSupplyNumber};
2263 $name = $out->{powerSupplyName};
912d8679 2264 $state = get_hashval($out->{powerSupplyState}, \%ps_state);
669797e1 2265 $status = $snmp_status{$out->{powerSupplyComponentStatus}};
b1f48712 2266 $encl_id = $snmp_enclosure{$out->{powerSupplyConnectionEnclosureNumber}}{nexus};
669797e1 2267 $encl_name = $out->{powerSupplyConnectionEnclosureName};
2268 $nexus = convert_nexus($out->{powerSupplyNexusID});
2269 }
2270 else {
2271 $id = $out->{'ID'};
2272 $name = $out->{'Name'};
2273 $state = $out->{'State'};
2274 $status = $out->{'Status'};
2275 $encl_id = join q{:}, $out->{ctrl}, $out->{'encl_id'};
2276 $encl_name = $out->{encl_name};
2277 $nexus = join q{:}, $out->{ctrl}, $out->{'encl_id'}, $id;
2278 }
2279
2280 next PS if blacklisted('encl_ps', $nexus);
2281
2282 # Default
2283 if ($status ne 'Ok') {
98b224a3 2284 my $msg = sprintf '%s in enclosure %s [%s] needs attention: %s',
669797e1 2285 $name, $encl_id, $encl_name, $state;
2286 report('storage', $msg, $status2nagios{$status}, $nexus);
2287 }
2288 # Ok
2289 else {
98b224a3 2290 my $msg = sprintf '%s in enclosure %s [%s] is %s',
669797e1 2291 $name, $encl_id, $encl_name, $state;
2292 report('storage', $msg, $E_OK, $nexus);
2293 }
2294 }
2295 return;
2296}
2297
2298
2299#-----------------------------------------
2300# STORAGE: Check enclosure temperatures
2301#-----------------------------------------
2302sub check_enclosure_temp {
2303 return if $#controllers == -1;
5ecf578c 2304 return if blacklisted('encl_temp', 'all');
669797e1 2305
2306 my $id = undef;
2307 my $nexus = undef;
2308 my $name = undef;
2309 my $state = undef;
2310 my $status = undef;
2311 my $reading = undef;
2312 my $unit = undef;
2313 my $max_warn = undef;
2314 my $max_crit = undef;
a0c9fa40 2315 my $min_warn = undef;
2316 my $min_crit = undef;
669797e1 2317 my $encl_id = undef;
2318 my $encl_name = undef;
2319 my @output = ();
2320
2321 if ($snmp) {
2322 my %temp_oid
2323 = (
2324 '1.3.6.1.4.1.674.10893.1.20.130.11.1.1' => 'temperatureProbeNumber',
2325 '1.3.6.1.4.1.674.10893.1.20.130.11.1.2' => 'temperatureProbeName',
2326 '1.3.6.1.4.1.674.10893.1.20.130.11.1.4' => 'temperatureProbeState',
2327 '1.3.6.1.4.1.674.10893.1.20.130.11.1.6' => 'temperatureProbeUnit',
a0c9fa40 2328 '1.3.6.1.4.1.674.10893.1.20.130.11.1.7' => 'temperatureProbeMinWarning',
2329 '1.3.6.1.4.1.674.10893.1.20.130.11.1.8' => 'temperatureProbeMinCritical',
669797e1 2330 '1.3.6.1.4.1.674.10893.1.20.130.11.1.9' => 'temperatureProbeMaxWarning',
2331 '1.3.6.1.4.1.674.10893.1.20.130.11.1.10' => 'temperatureProbeMaxCritical',
2332 '1.3.6.1.4.1.674.10893.1.20.130.11.1.11' => 'temperatureProbeCurValue',
2333 '1.3.6.1.4.1.674.10893.1.20.130.11.1.13' => 'temperatureProbeComponentStatus',
2334 '1.3.6.1.4.1.674.10893.1.20.130.11.1.14' => 'temperatureProbeNexusID',
2335 '1.3.6.1.4.1.674.10893.1.20.130.12.1.4' => 'temperatureConnectionEnclosureName',
2336 '1.3.6.1.4.1.674.10893.1.20.130.12.1.5' => 'temperatureConnectionEnclosureNumber',
2337 );
4cabd748 2338 my $result = undef;
2339 if ($opt{use_get_table}) {
2340 my $temperatureProbeTable = '1.3.6.1.4.1.674.10893.1.20.130.11';
c849fd4c 2341 my $temperatureConnectionTable = '1.3.6.1.4.1.674.10893.1.20.130.12';
2342
4cabd748 2343 $result = $snmp_session->get_table(-baseoid => $temperatureProbeTable);
c849fd4c 2344 my $ext = $snmp_session->get_table(-baseoid => $temperatureConnectionTable);
2345
2346 if (defined $result) {
2347 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
2348 }
4cabd748 2349 }
2350 else {
2351 $result = $snmp_session->get_entries(-columns => [keys %temp_oid]);
2352 }
669797e1 2353
2354 # No enclosure temperature probes is OK
2355 return if !defined $result;
2356
2357 @output = @{ get_snmp_output($result, \%temp_oid) };
2358 }
2359 else {
2360 foreach my $enc (@enclosures) {
2361 push @output, @{ run_omreport("storage enclosure controller=$enc->{ctrl} enclosure=$enc->{id} info=temps") };
2362 map_item('ctrl', $enc->{ctrl}, \@output);
2363 map_item('encl_id', $enc->{id}, \@output);
2364 map_item('encl_name', $enc->{name}, \@output);
2365 }
2366 }
2367
2368 my %temp_state
2369 = (
2370 0 => 'Unknown',
2371 1 => 'Ready',
2372 2 => 'Failed',
2373 4 => 'Offline',
2374 6 => 'Degraded',
2375 9 => 'Inactive',
2376 21 => 'Missing',
2377 );
2378
2379 # Check temperature probes on each of the enclosures
2380 TEMP:
2381 foreach my $out (@output) {
2382 if ($snmp) {
2383 $id = $out->{temperatureProbeNumber} - 1;
2384 $name = $out->{temperatureProbeName};
912d8679 2385 $state = get_hashval($out->{temperatureProbeState}, \%temp_state);
669797e1 2386 $status = $snmp_status{$out->{temperatureProbeComponentStatus}};
2387 $unit = $out->{temperatureProbeUnit};
a0c9fa40 2388 $reading = exists $out->{temperatureProbeCurValue}
2389 ? $out->{temperatureProbeCurValue} : '[N/A]';
2390 $max_warn = exists $out->{temperatureProbeMaxWarning}
2391 ? $out->{temperatureProbeMaxWarning} : '[N/A]';
2392 $max_crit = exists $out->{temperatureProbeMaxCritical}
2393 ? $out->{temperatureProbeMaxCritical} : '[N/A]';
2394 $min_warn = exists $out->{temperatureProbeMinWarning}
2395 ? $out->{temperatureProbeMinWarning} : '[N/A]';
2396 $min_crit = exists $out->{temperatureProbeMinCritical}
2397 ? $out->{temperatureProbeMinCritical} : '[N/A]';
b1f48712 2398 $encl_id = $snmp_enclosure{$out->{temperatureConnectionEnclosureNumber}}{nexus};
669797e1 2399 $encl_name = $out->{temperatureConnectionEnclosureName};
2400 $nexus = convert_nexus($out->{temperatureProbeNexusID});
2401 }
2402 else {
2403 $id = $out->{'ID'};
2404 $name = $out->{'Name'};
2405 $state = $out->{'State'};
2406 $status = $out->{'Status'};
2407 $unit = 'FIXME';
a8e3dcc4 2408 $reading = $out->{'Reading'};
2409 $max_warn = $out->{'Maximum Warning Threshold'};
2410 $max_crit = $out->{'Maximum Failure Threshold'};
2411 $min_warn = $out->{'Minimum Warning Threshold'};
2412 $min_crit = $out->{'Minimum Failure Threshold'};
669797e1 2413 $encl_id = join q{:}, $out->{ctrl}, $out->{'encl_id'};
2414 $encl_name = $out->{encl_name};
2415 $nexus = join q{:}, $out->{ctrl}, $out->{'encl_id'}, $id;
2416 }
2417
2418 next TEMP if blacklisted('encl_temp', $nexus);
2419
a0c9fa40 2420 # Make sure these values are integers
2421 $reading =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $reading = '[N/A]';
2422 $max_warn =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $max_warn = '[N/A]';
2423 $max_crit =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $max_crit = '[N/A]';
2424 $min_warn =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $min_warn = '[N/A]';
2425 $min_crit =~ s{\A \s* (-?\d+) \s* C? \s* \z}{$1}xms or $min_crit = '[N/A]';
2426
2c1daec8 2427 # Inactive temp probes
2428 if ($status eq 'Unknown' and $state eq 'Inactive') {
2429 my $msg = sprintf '%s in enclosure %s [%s] is %s',
2430 $name, $encl_id, $encl_name, $state;
2431 report('storage', $msg, $E_OK, $nexus);
2432 }
a0c9fa40 2433 elsif ($status ne 'Ok' and $max_crit ne '[N/A]' and $reading > $max_crit) {
2434 my $msg = sprintf '%s in enclosure %s [%s] is critically high at %d C',
2435 $name, $encl_id, $encl_name, $reading;
2436 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2437 report('chassis', $msg, $err, $nexus);
2438 }
2439 elsif ($status ne 'Ok' and $max_warn ne '[N/A]' and $reading > $max_warn) {
2440 my $msg = sprintf '%s in enclosure %s [%s] is too high at %d C',
2441 $name, $encl_id, $encl_name, $reading;
2442 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2443 report('chassis', $msg, $err, $nexus);
2444 }
2445 elsif ($status ne 'Ok' and $min_crit ne '[N/A]' and $reading < $min_crit) {
2446 my $msg = sprintf '%s in enclosure %s [%s] is critically low at %d C',
2447 $name, $encl_id, $encl_name, $reading;
2448 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2449 report('chassis', $msg, $err, $nexus);
2450 }
2451 elsif ($status ne 'Ok' and $min_warn ne '[N/A]' and $reading < $min_warn) {
2452 my $msg = sprintf '%s in enclosure %s [%s] is too low at %d C',
2453 $name, $encl_id, $encl_name, $reading;
2454 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2455 report('chassis', $msg, $err, $nexus);
2456 }
669797e1 2457 # Default
2c1daec8 2458 elsif ($status ne 'Ok') {
2459 my $msg = sprintf '%s in enclosure %s [%s] is %s',
2460 $name, $encl_id, $encl_name, $state;
a38cf844 2461 if (defined $reading && $reading =~ m{\A -?\d+ \z}xms) {
2c1daec8 2462 # take into account that with certain states the
2463 # reading doesn't exist or is not an integer
a0c9fa40 2464 $msg .= sprintf ' at %s C', $reading;
2465 if ($min_warn eq '[N/A]' or $min_crit eq '[N/A]') {
2466 $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit;
2467 }
2468 else {
2469 $msg .= sprintf ' (min=%s/%s, max=%s/%s)',
2470 $min_warn, $min_crit, $max_warn, $max_crit;
2471 }
2c1daec8 2472 }
a0c9fa40 2473 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2474 report('storage', $msg, $err, $nexus);
669797e1 2475 }
2476 # Ok
2477 else {
a0c9fa40 2478 my $msg = sprintf '%s in enclosure %s [%s]',
2479 $name, $encl_id, $encl_name;
2480 if (defined $reading && $reading ne '[N/A]') {
2481 # take into account that with certain states the
2482 # reading doesn't exist or is not an integer
2483 $msg .= sprintf ' reads %d C', $reading;
2484 if ($min_warn eq '[N/A]' or $min_crit eq '[N/A]') {
2485 $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit;
2486 }
2487 else {
2488 $msg .= sprintf ' (min=%s/%s, max=%s/%s)',
2489 $min_warn, $min_crit, $max_warn, $max_crit;
2490 }
2491 }
2492 else {
2493 $msg .= sprintf ' is %s', $state;
2494 }
669797e1 2495 report('storage', $msg, $E_OK, $nexus);
2496 }
2497
2498 # Collect performance data
a0c9fa40 2499 if (defined $opt{perfdata} && $reading ne '[N/A]') {
669797e1 2500 $name =~ s{\A Temperature\sProbe\s(\d+) \z}{temp_$1}gxms;
48aeec0b 2501 my $label = "enclosure_${encl_id}_${name}";
2502 my $mini = $label;
2503 $mini =~ s{enclosure_(.+?)_temp_(.+?)}{e$1t$2}xms;
2504 push @perfdata, {
2505 label => $label,
2506 mini => $mini,
2507 value => $reading,
48aeec0b 2508 warn => $max_warn,
2509 crit => $max_crit,
2510 };
669797e1 2511 }
2512 }
2513 return;
2514}
2515
2516
2517#-----------------------------------------
2518# STORAGE: Check enclosure management modules (EMM)
2519#-----------------------------------------
2520sub check_enclosure_emms {
2521 return if $#controllers == -1;
5ecf578c 2522 return if blacklisted('encl_emm', 'all');
669797e1 2523
2524 my $id = undef;
2525 my $nexus = undef;
2526 my $name = undef;
2527 my $state = undef;
2528 my $status = undef;
2529 my $encl_id = undef;
2530 my $encl_name = undef;
2531 my @output = ();
2532
2533 if ($snmp) {
2534 my %emms_oid
2535 = (
2536 '1.3.6.1.4.1.674.10893.1.20.130.13.1.1' => 'enclosureManagementModuleNumber',
2537 '1.3.6.1.4.1.674.10893.1.20.130.13.1.2' => 'enclosureManagementModuleName',
2538 '1.3.6.1.4.1.674.10893.1.20.130.13.1.4' => 'enclosureManagementModuleState',
2539 '1.3.6.1.4.1.674.10893.1.20.130.13.1.11' => 'enclosureManagementModuleComponentStatus',
2540 '1.3.6.1.4.1.674.10893.1.20.130.13.1.12' => 'enclosureManagementModuleNexusID',
2541 '1.3.6.1.4.1.674.10893.1.20.130.14.1.4' => 'enclosureManagementModuleConnectionEnclosureName',
2542 '1.3.6.1.4.1.674.10893.1.20.130.14.1.5' => 'enclosureManagementModuleConnectionEnclosureNumber',
2543 );
4cabd748 2544 my $result = undef;
2545 if ($opt{use_get_table}) {
2546 my $enclosureManagementModuleTable = '1.3.6.1.4.1.674.10893.1.20.130.13';
c849fd4c 2547 my $enclosureManagementModuleConnectionTable = '1.3.6.1.4.1.674.10893.1.20.130.14';
2548
4cabd748 2549 $result = $snmp_session->get_table(-baseoid => $enclosureManagementModuleTable);
c849fd4c 2550 my $ext = $snmp_session->get_table(-baseoid => $enclosureManagementModuleConnectionTable);
2551
2552 if (defined $result) {
2553 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
2554 }
4cabd748 2555 }
2556 else {
2557 $result = $snmp_session->get_entries(-columns => [keys %emms_oid]);
2558 }
669797e1 2559
2560 # No enclosure EMMs is OK
2561 return if !defined $result;
2562
2563 @output = @{ get_snmp_output($result, \%emms_oid) };
2564 }
2565 else {
2566 foreach my $enc (@enclosures) {
2567 push @output, @{ run_omreport("storage enclosure controller=$enc->{ctrl} enclosure=$enc->{id} info=emms") };
2568 map_item('ctrl', $enc->{ctrl}, \@output);
2569 map_item('encl_id', $enc->{id}, \@output);
2570 map_item('encl_name', $enc->{name}, \@output);
2571 }
2572 }
2573
2574 my %emms_state
2575 = (
2576 0 => 'Unknown',
2577 1 => 'Ready',
2578 2 => 'Failed',
2579 3 => 'Online',
2580 4 => 'Offline',
2581 5 => 'Not Installed',
2582 6 => 'Degraded',
2583 21 => 'Missing',
2584 );
2585
a0c9fa40 2586 # Check EMMs on each of the enclosures
669797e1 2587 EMM:
2588 foreach my $out (@output) {
2589 if ($snmp) {
2590 $id = $out->{enclosureManagementModuleNumber} - 1;
2591 $name = $out->{enclosureManagementModuleName};
912d8679 2592 $state = get_hashval($out->{enclosureManagementModuleState}, \%emms_state);
669797e1 2593 $status = $snmp_status{$out->{enclosureManagementModuleComponentStatus}};
b1f48712 2594 $encl_id = $snmp_enclosure{$out->{enclosureManagementModuleConnectionEnclosureNumber}}{nexus};
669797e1 2595 $encl_name = $out->{enclosureManagementModuleConnectionEnclosureName};
2596 $nexus = convert_nexus($out->{enclosureManagementModuleNexusID});
2597 }
2598 else {
2599 $id = $out->{'ID'};
2600 $name = $out->{'Name'};
2601 $state = $out->{'State'};
2602 $status = $out->{'Status'};
2603 $encl_id = join q{:}, $out->{ctrl}, $out->{'encl_id'};
2604 $encl_name = $out->{encl_name};
2605 $nexus = join q{:}, $out->{ctrl}, $out->{'encl_id'}, $id;
2606 }
2607
2608 next EMM if blacklisted('encl_emm', $nexus);
2609
2c1daec8 2610 # Not installed
a0c9fa40 2611 if ($status =~ m{\A Other|Unknown \z}xms and $state eq 'Not Installed') {
2c1daec8 2612 my $msg = sprintf '%s in enclosure %s [%s] is %s',
2613 $name, $encl_id, $encl_name, $state;
2614 report('storage', $msg, $E_OK, $nexus);
2615 }
669797e1 2616 # Default
2c1daec8 2617 elsif ($status ne 'Ok') {
98b224a3 2618 my $msg = sprintf '%s in enclosure %s [%s] needs attention: %s',
669797e1 2619 $name, $encl_id, $encl_name, $state;
2620 report('storage', $msg, $status2nagios{$status}, $nexus);
2621 }
2622 # Ok
2623 else {
98b224a3 2624 my $msg = sprintf '%s in enclosure %s [%s] is %s',
669797e1 2625 $name, $encl_id, $encl_name, $state;
2626 report('storage', $msg, $E_OK, $nexus);
2627 }
2628 }
2629 return;
2630}
2631
2632
2633#-----------------------------------------
2634# CHASSIS: Check memory modules
2635#-----------------------------------------
2636sub check_memory {
5ecf578c 2637 return if blacklisted('dimm', 'all');
2638
669797e1 2639 my $index = undef;
2640 my $status = undef;
2641 my $location = undef;
2642 my $size = undef;
2643 my $modes = undef;
2644 my @failures = ();
2645 my @output = ();
2646
2647 if ($snmp) {
2648 my %dimm_oid
2649 = (
2650 '1.3.6.1.4.1.674.10892.1.1100.50.1.2.1' => 'memoryDeviceIndex',
2651 '1.3.6.1.4.1.674.10892.1.1100.50.1.5.1' => 'memoryDeviceStatus',
2652 '1.3.6.1.4.1.674.10892.1.1100.50.1.8.1' => 'memoryDeviceLocationName',
2653 '1.3.6.1.4.1.674.10892.1.1100.50.1.14.1' => 'memoryDeviceSize',
2654 '1.3.6.1.4.1.674.10892.1.1100.50.1.20.1' => 'memoryDeviceFailureModes',
2655 );
4cabd748 2656 my $result = undef;
2657 if ($opt{use_get_table}) {
2658 my $memoryDeviceTable = '1.3.6.1.4.1.674.10892.1.1100.50.1';
2659 $result = $snmp_session->get_table(-baseoid => $memoryDeviceTable);
2660 }
2661 else {
2662 $result = $snmp_session->get_entries(-columns => [keys %dimm_oid]);
2663 }
669797e1 2664
2665 if (!defined $result) {
98b224a3 2666 printf "SNMP ERROR [memory]: %s.\n", $snmp_session->error;
669797e1 2667 $snmp_session->close;
2668 exit $E_UNKNOWN;
2669 }
2670
2671 @output = @{ get_snmp_output($result, \%dimm_oid) };
2672 }
2673 else {
2674 @output = @{ run_omreport("$omopt_chassis memory") };
2675 }
2676
2677 # Note: These values are bit masks, so combination values are
2678 # possible. If value is 0 (zero), memory device has no faults.
2679 my %failure_mode
2680 = (
2681 1 => 'ECC single bit correction warning rate exceeded',
2682 2 => 'ECC single bit correction failure rate exceeded',
2683 4 => 'ECC multibit fault encountered',
2684 8 => 'ECC single bit correction logging disabled',
2685 16 => 'device disabled because of spare activation',
2686 );
2687
2688 DIMM:
2689 foreach my $out (@output) {
2690 @failures = (); # Initialize
2691 if ($snmp) {
2692 $index = $out->{memoryDeviceIndex};
2693 $status = $snmp_status{$out->{memoryDeviceStatus}};
2694 $location = $out->{memoryDeviceLocationName};
2695 $size = sprintf '%d MB', $out->{memoryDeviceSize}/1024;
2696 $modes = $out->{memoryDeviceFailureModes};
2697 if ($modes > 0) {
2698 foreach my $mask (sort keys %failure_mode) {
2699 if (($modes & $mask) != 0) { push @failures, $failure_mode{$mask}; }
2700 }
2701 }
2702 }
2703 else {
2704 $index = $out->{'Type'} eq '[Not Occupied]' ? undef : $out->{'Index'};
2705 $status = $out->{'Status'};
2706 $location = $out->{'Connector Name'};
2707 $size = $out->{'Size'};
2708 if (defined $size) {
2709 $size =~ s{\s\s}{ }gxms;
2710 }
2711 # Run 'omreport chassis memory index=X' to get the failures
2712 if ($status ne 'Ok' && defined $index) {
2713 foreach (@{ run_command("$omreport $omopt_chassis memory index=$index -fmt ssv") }) {
2714 if (m/\A Failures; (.+?) \z/xms) {
2715 chop(my $fail = $1);
2716 push @failures, split m{\.}xms, $fail;
2717 }
2718 }
2719 }
2720 }
2721 $location =~ s{\A \s*(.*?)\s* \z}{$1}xms;
2722
14e95f92 2723 # calculate total memory
717be848 2724 my $msize = defined $size ? $size : 0;
14e95f92 2725 $msize =~ s{\A (\d+) \s MB}{$1}xms;
2726 $count{mem} += $msize;
2727
669797e1 2728 next DIMM if blacklisted('dimm', $index);
2729
2730 # Ignore empty memory slots
2731 next DIMM if !defined $index;
2732 $count{dimm}++;
2733
2734 if ($status ne 'Ok') {
2735 my $msg = undef;
2736 if (scalar @failures == 0) {
98b224a3 2737 $msg = sprintf 'Memory module %d [%s, %s] needs attention (%s)',
669797e1 2738 $index, $location, $size, $status;
2739 }
2740 else {
98b224a3 2741 $msg = sprintf 'Memory module %d [%s, %s] needs attention: %s',
669797e1 2742 $index, $location, $size, (join q{, }, @failures);
2743 }
2744
2745 report('chassis', $msg, $status2nagios{$status}, $index);
2746 }
2747 # Ok
2748 else {
98b224a3 2749 my $msg = sprintf 'Memory module %d [%s, %s] is %s',
669797e1 2750 $index, $location, $size, $status;
2751 report('chassis', $msg, $E_OK, $index);
2752 }
2753 }
7b5c99ff 2754 return;
669797e1 2755}
2756
2757
2758#-----------------------------------------
2759# CHASSIS: Check fans
2760#-----------------------------------------
2761sub check_fans {
5ecf578c 2762 return if blacklisted('fan', 'all');
2763
669797e1 2764 my $index = undef;
2765 my $status = undef;
2766 my $reading = undef;
2767 my $location = undef;
2768 my $max_crit = undef;
2769 my $max_warn = undef;
2770 my @output = ();
2771
2772 if ($snmp) {
2773 my %cool_oid
2774 = (
2775 '1.3.6.1.4.1.674.10892.1.700.12.1.2.1' => 'coolingDeviceIndex',
2776 '1.3.6.1.4.1.674.10892.1.700.12.1.5.1' => 'coolingDeviceStatus',
2777 '1.3.6.1.4.1.674.10892.1.700.12.1.6.1' => 'coolingDeviceReading',
2778 '1.3.6.1.4.1.674.10892.1.700.12.1.8.1' => 'coolingDeviceLocationName',
2779 '1.3.6.1.4.1.674.10892.1.700.12.1.10.1' => 'coolingDeviceUpperCriticalThreshold',
2780 '1.3.6.1.4.1.674.10892.1.700.12.1.11.1' => 'coolingDeviceUpperNonCriticalThreshold',
2781 );
4cabd748 2782 my $result = undef;
2783 if ($opt{use_get_table}) {
2784 my $coolingDeviceTable = '1.3.6.1.4.1.674.10892.1.700.12.1';
2785 $result = $snmp_session->get_table(-baseoid => $coolingDeviceTable);
2786 }
2787 else {
2788 $result = $snmp_session->get_entries(-columns => [keys %cool_oid]);
2789 }
669797e1 2790
2791 if ($blade && !defined $result) {
2792 return 0;
2793 }
2794 elsif (!$blade && !defined $result) {
98b224a3 2795 printf "SNMP ERROR [cooling]: %s.\n", $snmp_session->error;
669797e1 2796 $snmp_session->close;
2797 exit $E_UNKNOWN;
2798 }
2799
2800 @output = @{ get_snmp_output($result, \%cool_oid) };
2801 }
2802 else {
2803 @output = @{ run_omreport("$omopt_chassis fans") };
2804 }
2805
2806 FAN:
2807 foreach my $out (@output) {
2808 if ($snmp) {
2809 $index = $out->{coolingDeviceIndex};
2810 $status = $snmp_probestatus{$out->{coolingDeviceStatus}};
2811 $reading = $out->{coolingDeviceReading};
2812 $location = $out->{coolingDeviceLocationName};
2813 $max_crit = exists $out->{coolingDeviceUpperCriticalThreshold}
2814 ? $out->{coolingDeviceUpperCriticalThreshold} : 0;
2815 $max_warn = exists $out->{coolingDeviceUpperNonCriticalThreshold}
2816 ? $out->{coolingDeviceUpperNonCriticalThreshold} : 0;
2817 }
2818 else {
2819 $index = $out->{'Index'};
2820 $status = $out->{'Status'};
2821 $reading = $out->{'Reading'};
2822 $location = $out->{'Probe Name'};
2823 $max_crit = $out->{'Maximum Failure Threshold'} ne '[N/A]'
2824 ? $out->{'Maximum Failure Threshold'} : 0;
2825 $max_warn = $out->{'Maximum Warning Threshold'} ne '[N/A]'
2826 ? $out->{'Maximum Warning Threshold'} : 0;
2827 $reading =~ s{\A (\d+).* \z}{$1}xms;
2828 $max_warn =~ s{\A (\d+).* \z}{$1}xms;
2829 $max_crit =~ s{\A (\d+).* \z}{$1}xms;
2830 }
2831
2832 next FAN if blacklisted('fan', $index);
2833 $count{fan}++;
2834
2835 if ($status ne 'Ok') {
98b224a3 2836 my $msg = sprintf 'Chassis fan %d [%s] needs attention: %s',
669797e1 2837 $index, $location, $status;
2838 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
2839 report('chassis', $msg, $err, $index);
2840 }
2841 else {
98b224a3 2842 my $msg = sprintf 'Chassis fan %d [%s]: %s',
669797e1 2843 $index, $location, $reading;
2844 report('chassis', $msg, $E_OK, $index);
2845 }
2846
2847 # Collect performance data
2848 if (defined $opt{perfdata}) {
2849 my $pname = lc $location;
2850 $pname =~ s{\s}{_}gxms;
2851 $pname =~ s{proc_}{cpu#}xms;
48aeec0b 2852 push @perfdata, {
60fd6896 2853 label => "fan_${index}_${pname}",
48aeec0b 2854 mini => "f$index",
2855 value => $reading,
48aeec0b 2856 warn => $max_warn,
2857 crit => $max_crit,
2858 };
669797e1 2859 }
2860 }
2861 return;
2862}
2863
2864
2865#-----------------------------------------
2866# CHASSIS: Check power supplies
2867#-----------------------------------------
2868sub check_powersupplies {
5ecf578c 2869 return if blacklisted('ps', 'all');
2870
669797e1 2871 my $index = undef;
2872 my $status = undef;
2873 my $type = undef;
2874 my $err_type = undef;
2875 my $state = undef;
2876 my @states = ();
2877 my @output = ();
2878
2879 if ($snmp) {
2880 my %ps_oid
2881 = (
2882 '1.3.6.1.4.1.674.10892.1.600.12.1.2.1' => 'powerSupplyIndex',
2883 '1.3.6.1.4.1.674.10892.1.600.12.1.5.1' => 'powerSupplyStatus',
2884 '1.3.6.1.4.1.674.10892.1.600.12.1.7.1' => 'powerSupplyType',
2885 '1.3.6.1.4.1.674.10892.1.600.12.1.11.1' => 'powerSupplySensorState',
2886 '1.3.6.1.4.1.674.10892.1.600.12.1.12.1' => 'powerSupplyConfigurationErrorType',
2887 );
4cabd748 2888 my $result = undef;
2889 if ($opt{use_get_table}) {
2890 my $powerDeviceTable = '1.3.6.1.4.1.674.10892.1.600.12.1';
2891 $result = $snmp_session->get_table(-baseoid => $powerDeviceTable);
2892 }
2893 else {
2894 $result = $snmp_session->get_entries(-columns => [keys %ps_oid]);
2895 }
669797e1 2896
2897 # No instrumented PSU is OK (blades, low-end servers)
2898 return 0 if !defined $result;
2899
2900 @output = @{ get_snmp_output($result, \%ps_oid) };
2901 }
2902 else {
2903 @output = @{ run_omreport("$omopt_chassis pwrsupplies") };
2904 }
2905
2906 my %ps_type
2907 = (
2908 1 => 'Other',
2909 2 => 'Unknown',
2910 3 => 'Linear',
2911 4 => 'Switching',
2912 5 => 'Battery',
2913 6 => 'Uninterruptible Power Supply',
2914 7 => 'Converter',
2915 8 => 'Regulator',
2916 9 => 'AC',
2917 10 => 'DC',
2918 11 => 'VRM',
2919 );
2920
2921 my %ps_state
2922 = (
2923 1 => 'Presence detected',
2924 2 => 'Failure detected',
2925 4 => 'Predictive Failure',
2926 8 => 'AC lost',
2927 16 => 'AC lost or out-of-range',
2928 32 => 'AC out-of-range but present',
2929 64 => 'Configuration error',
2930 );
2931
2932 my %ps_config_error_type
2933 = (
2934 1 => 'Vendor mismatch',
2935 2 => 'Revision mismatch',
2936 3 => 'Processor missing',
2937 );
2938
2939 PS:
2940 foreach my $out (@output) {
2941 if ($snmp) {
2942 @states = (); # contains states for the PS
2943
2944 $index = $out->{powerSupplyIndex} - 1;
2945 $status = $snmp_status{$out->{powerSupplyStatus}};
912d8679 2946 $type = get_hashval($out->{powerSupplyType}, \%ps_type);
669797e1 2947 $err_type = defined $out->{powerSupplyConfigurationErrorType}
2948 ? $ps_config_error_type{$out->{powerSupplyConfigurationErrorType}} : undef;
2949
2950 # get the combined state from the StatusReading OID
2951 foreach my $mask (sort keys %ps_state) {
2952 if (($out->{powerSupplySensorState} & $mask) != 0) {
2953 push @states, $ps_state{$mask};
2954 }
2955 }
2956
2957 # If configuration error, also include the error type
2958 if (defined $err_type) {
2959 push @states, $err_type;
2960 }
2961
2962 # Finally, construct the state string
2963 $state = join q{, }, @states;
2964 }
2965 else {
2966 $index = $out->{'Index'};
2967 $status = $out->{'Status'};
2968 $type = $out->{'Type'};
2969 $state = $out->{'Online Status'};
2970 }
2971
2972 next PS if blacklisted('ps', $index);
2973 $count{power}++;
2974
2975 if ($status ne 'Ok') {
98b224a3 2976 my $msg = sprintf 'Power Supply %d [%s] needs attention: %s',
669797e1 2977 $index, $type, $state;
2978 report('chassis', $msg, $status2nagios{$status}, $index);
2979 }
2980 else {
98b224a3 2981 my $msg = sprintf 'Power Supply %d [%s]: %s',
669797e1 2982 $index, $type, $state;
2983 report('chassis', $msg, $E_OK, $index);
2984 }
2985 }
2986 return;
2987}
2988
2989
2990#-----------------------------------------
2991# CHASSIS: Check temperatures
2992#-----------------------------------------
2993sub check_temperatures {
5ecf578c 2994 return if blacklisted('temp', 'all');
2995
669797e1 2996 my $index = undef;
2997 my $status = undef;
2998 my $reading = undef;
2999 my $location = undef;
3000 my $max_crit = undef;
3001 my $max_warn = undef;
3002 my $min_warn = undef;
3003 my $min_crit = undef;
3004 my $type = undef;
3005 my $discrete = undef;
3006 my @output = ();
3007
3008 # Getting custom temperature thresholds (user option)
3009 my %warn_threshold = %{ custom_temperature_thresholds('w') };
3010 my %crit_threshold = %{ custom_temperature_thresholds('c') };
3011
3012 if ($snmp) {
3013 my %temp_oid
3014 = (
3015 '1.3.6.1.4.1.674.10892.1.700.20.1.2.1' => 'temperatureProbeIndex',
3016 '1.3.6.1.4.1.674.10892.1.700.20.1.5.1' => 'temperatureProbeStatus',
3017 '1.3.6.1.4.1.674.10892.1.700.20.1.6.1' => 'temperatureProbeReading',
3018 '1.3.6.1.4.1.674.10892.1.700.20.1.7.1' => 'temperatureProbeType',
3019 '1.3.6.1.4.1.674.10892.1.700.20.1.8.1' => 'temperatureProbeLocationName',
3020 '1.3.6.1.4.1.674.10892.1.700.20.1.10.1' => 'temperatureProbeUpperCriticalThreshold',
3021 '1.3.6.1.4.1.674.10892.1.700.20.1.11.1' => 'temperatureProbeUpperNonCriticalThreshold',
3022 '1.3.6.1.4.1.674.10892.1.700.20.1.12.1' => 'temperatureProbeLowerNonCriticalThreshold',
3023 '1.3.6.1.4.1.674.10892.1.700.20.1.13.1' => 'temperatureProbeLowerCriticalThreshold',
3024 '1.3.6.1.4.1.674.10892.1.700.20.1.16.1' => 'temperatureProbeDiscreteReading',
3025 );
ba199ee0 3026 # this didn't work well for some reason
3027 #my $result = $snmp_session->get_entries(-columns => [keys %temp_oid]);
3028
3029 # Getting values using the table
3030 my $temperatureProbeTable = '1.3.6.1.4.1.674.10892.1.700.20';
3031 my $result = $snmp_session->get_table(-baseoid => $temperatureProbeTable);
669797e1 3032
3033 if (!defined $result) {
98b224a3 3034 printf "SNMP ERROR [temperatures]: %s.\n", $snmp_session->error;
669797e1 3035 $snmp_session->close;
3036 exit $E_UNKNOWN;
3037 }
3038
3039 @output = @{ get_snmp_output($result, \%temp_oid) };
3040 }
3041 else {
3042 @output = @{ run_omreport("$omopt_chassis temps") };
3043 }
3044
3045 my %probe_type
3046 = (
3047 1 => 'Other', # type is other than following values
3048 2 => 'Unknown', # type is unknown
3049 3 => 'AmbientESM', # type is Ambient Embedded Systems Management temperature probe
3050 16 => 'Discrete', # type is temperature probe with discrete reading
3051 );
3052
3053 TEMP:
3054 foreach my $out (@output) {
3055 if ($snmp) {
3056 $index = $out->{temperatureProbeIndex} - 1;
3057 $status = $snmp_probestatus{$out->{temperatureProbeStatus}};
669797e1 3058 $location = $out->{temperatureProbeLocationName};
7328e97b 3059 $reading = exists $out->{temperatureProbeReading}
3060 ? $out->{temperatureProbeReading} / 10 : '[N/A]';
3061 $max_crit = exists $out->{temperatureProbeUpperCriticalThreshold}
3062 ? $out->{temperatureProbeUpperCriticalThreshold} / 10 : '[N/A]';
3063 $max_warn = exists $out->{temperatureProbeUpperNonCriticalThreshold}
3064 ? $out->{temperatureProbeUpperNonCriticalThreshold} / 10 : '[N/A]';
669797e1 3065 $min_crit = exists $out->{temperatureProbeLowerCriticalThreshold}
3066 ? $out->{temperatureProbeLowerCriticalThreshold} / 10 : '[N/A]';
3067 $min_warn = exists $out->{temperatureProbeLowerNonCriticalThreshold}
3068 ? $out->{temperatureProbeLowerNonCriticalThreshold} / 10 : '[N/A]';
912d8679 3069 $type = get_hashval($out->{temperatureProbeType}, \%probe_type);
669797e1 3070 $discrete = exists $out->{temperatureProbeDiscreteReading}
7328e97b 3071 ? $out->{temperatureProbeDiscreteReading} : '[N/A]';
3072 # workaround for bad temp probes
3073 if ($type eq 'AmbientESM' and $reading !~ m{\A \d+(\.\d+)? \z}xms) {
3074 $type = 'Discrete';
3075 }
669797e1 3076 }
3077 else {
3078 $index = $out->{'Index'};
3079 $status = $out->{'Status'};
3080 $reading = $out->{'Reading'}; $reading =~ s{\.0\s+C}{}xms;
3081 $location = $out->{'Probe Name'};
3082 $max_crit = $out->{'Maximum Failure Threshold'}; $max_crit =~ s{\.0\s+C}{}xms;
3083 $max_warn = $out->{'Maximum Warning Threshold'}; $max_warn =~ s{\.0\s+C}{}xms;
3084 $min_crit = $out->{'Minimum Failure Threshold'}; $min_crit =~ s{\.0\s+C}{}xms;
3085 $min_warn = $out->{'Minimum Warning Threshold'}; $min_warn =~ s{\.0\s+C}{}xms;
3086 $type = $reading =~ m{\A\d+\z}xms ? 'AmbientESM' : 'Discrete';
3087 $discrete = $reading;
3088 }
3089
3090 next TEMP if blacklisted('temp', $index);
3091 $count{temp}++;
3092
3093 if ($type eq 'Discrete') {
7328e97b 3094 my $msg = sprintf 'Temperature probe %d [%s] is %s',
669797e1 3095 $index, $location, $discrete;
3096 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3097 report('chassis', $msg, $err, $index);
3098 }
3099 else {
3100 # First check according to custom thresholds
3101 if (exists $crit_threshold{$index}{max} and $reading > $crit_threshold{$index}{max}) {
3102 # Custom critical MAX
98b224a3 3103 my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom max=%d)',
669797e1 3104 $index, $location, $reading, $crit_threshold{$index}{max};
3105 report('chassis', $msg, $E_CRITICAL, $index);
3106 }
3107 elsif (exists $warn_threshold{$index}{max} and $reading > $warn_threshold{$index}{max}) {
3108 # Custom warning MAX
98b224a3 3109 my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom max=%d)',
669797e1 3110 $index, $location, $reading, $warn_threshold{$index}{max};
3111 report('chassis', $msg, $E_WARNING, $index);
3112 }
3113 elsif (exists $crit_threshold{$index}{min} and $reading < $crit_threshold{$index}{min}) {
3114 # Custom critical MIN
98b224a3 3115 my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom min=%d)',
669797e1 3116 $index, $location, $reading, $crit_threshold{$index}{min};
3117 report('chassis', $msg, $E_CRITICAL, $index);
3118 }
3119 elsif (exists $warn_threshold{$index}{min} and $reading < $warn_threshold{$index}{min}) {
3120 # Custom warning MIN
98b224a3 3121 my $msg = sprintf 'Temperature Probe %d [%s] reads %d C (custom min=%d)',
669797e1 3122 $index, $location, $reading, $warn_threshold{$index}{min};
3123 report('chassis', $msg, $E_WARNING, $index);
3124 }
3125 elsif ($status ne 'Ok' and $max_crit ne '[N/A]' and $reading > $max_crit) {
98b224a3 3126 my $msg = sprintf 'Temperature Probe %d [%s] is critically high at %d C',
669797e1 3127 $index, $location, $reading;
3128 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3129 report('chassis', $msg, $err, $index);
3130 }
3131 elsif ($status ne 'Ok' and $max_warn ne '[N/A]' and $reading > $max_warn) {
98b224a3 3132 my $msg = sprintf 'Temperature Probe %d [%s] is too high at %d C',
669797e1 3133 $index, $location, $reading;
3134 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3135 report('chassis', $msg, $err, $index);
3136 }
3137 elsif ($status ne 'Ok' and $min_crit ne '[N/A]' and $reading < $min_crit) {
98b224a3 3138 my $msg = sprintf 'Temperature Probe %d [%s] is critically low at %d C',
669797e1 3139 $index, $location, $reading;
3140 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3141 report('chassis', $msg, $err, $index);
3142 }
3143 elsif ($status ne 'Ok' and $min_warn ne '[N/A]' and $reading < $min_warn) {
98b224a3 3144 my $msg = sprintf 'Temperature Probe %d [%s] is too low at %d C',
669797e1 3145 $index, $location, $reading;
3146 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3147 report('chassis', $msg, $err, $index);
3148 }
3149 # Ok
3150 else {
304c4cba 3151 my $msg = sprintf 'Temperature Probe %d [%s] reads %d C',
3152 $index, $location, $reading;
3153 if ($min_warn eq '[N/A]' and $min_crit eq '[N/A]') {
3154 $msg .= sprintf ' (max=%s/%s)', $max_warn, $max_crit;
3155 }
3156 else {
3157 $msg .= sprintf ' (min=%s/%s, max=%s/%s)',
3158 $min_warn, $min_crit, $max_warn, $max_crit;
8ce893fd 3159 }
669797e1 3160 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3161 report('chassis', $msg, $err, $index);
3162 }
3163
3164 # Collect performance data
3165 if (defined $opt{perfdata}) {
3166 my $pname = lc $location;
3167 $pname =~ s{\s}{_}gxms;
3168 $pname =~ s{_temp\z}{}xms;
3169 $pname =~ s{proc_}{cpu#}xms;
48aeec0b 3170 push @perfdata, {
60fd6896 3171 label => "temp_${index}_${pname}",
48aeec0b 3172 mini => "t$index",
3173 value => $reading,
48aeec0b 3174 warn => $max_warn,
3175 crit => $max_crit,
3176 };
669797e1 3177 }
3178 }
3179 }
3180 return;
3181}
3182
3183
3184#-----------------------------------------
3185# CHASSIS: Check processors
3186#-----------------------------------------
3187sub check_processors {
5ecf578c 3188 return if blacklisted('cpu', 'all');
3189
669797e1 3190 my $index = undef;
3191 my $status = undef;
3192 my $state = undef;
8ce893fd 3193 my $brand = undef;
3194 my $family = undef;
3195 my $man = undef;
3196 my $speed = undef;
8ce893fd 3197 my @output = ();
669797e1 3198
3199 if ($snmp) {
3200
3201 # NOTE: For some reason, older models don't have the
8ce893fd 3202 # "Processor Device Status" OIDs. We check both the newer
3203 # (preferred) OIDs and the old ones.
669797e1 3204
8ce893fd 3205 my %cpu_oid
669797e1 3206 = (
8ce893fd 3207 '1.3.6.1.4.1.674.10892.1.1100.30.1.2.1' => 'processorDeviceIndex',
3208 '1.3.6.1.4.1.674.10892.1.1100.30.1.5.1' => 'processorDeviceStatus',
3209 '1.3.6.1.4.1.674.10892.1.1100.30.1.8.1' => 'processorDeviceManufacturerName',
3210 '1.3.6.1.4.1.674.10892.1.1100.30.1.9.1' => 'processorDeviceStatusState',
3211 '1.3.6.1.4.1.674.10892.1.1100.30.1.10.1' => 'processorDeviceFamily',
3212 '1.3.6.1.4.1.674.10892.1.1100.30.1.12.1' => 'processorDeviceCurrentSpeed',
3213 '1.3.6.1.4.1.674.10892.1.1100.30.1.23.1' => 'processorDeviceBrandName',
3214 '1.3.6.1.4.1.674.10892.1.1100.32.1.2.1' => 'processorDeviceStatusIndex',
3215 '1.3.6.1.4.1.674.10892.1.1100.32.1.5.1' => 'processorDeviceStatusStatus',
3216 '1.3.6.1.4.1.674.10892.1.1100.32.1.6.1' => 'processorDeviceStatusReading',
669797e1 3217 );
4cabd748 3218 my $result = undef;
3219 if ($opt{use_get_table}) {
3220 my $processorDeviceTable = '1.3.6.1.4.1.674.10892.1.1100.30.1';
3221 my $processorDeviceStatusTable = '1.3.6.1.4.1.674.10892.1.1100.32.1';
669797e1 3222
c849fd4c 3223 $result = $snmp_session->get_table(-baseoid => $processorDeviceTable);
3224 my $ext = $snmp_session->get_table(-baseoid => $processorDeviceStatusTable);
4cabd748 3225
c849fd4c 3226 defined $ext && map { $$result{$_} = $$ext{$_} } keys %{ $ext };
4cabd748 3227 }
3228 else {
3229 $result = $snmp_session->get_entries(-columns => [keys %cpu_oid]);
3230 }
669797e1 3231
3232 if (!defined $result) {
98b224a3 3233 printf "SNMP ERROR [processors]: %s.\n", $snmp_session->error;
669797e1 3234 $snmp_session->close;
3235 exit $E_UNKNOWN;
3236 }
3237
8ce893fd 3238 @output = @{ get_snmp_output($result, \%cpu_oid) };
669797e1 3239 }
3240 else {
3241 @output = @{ run_omreport("$omopt_chassis processors") };
3242 }
3243
3244 my %cpu_state
3245 = (
3246 1 => 'Other', # other than following values
3247 2 => 'Unknown', # unknown
3248 3 => 'Enabled', # enabled
3249 4 => 'User Disabled', # disabled by user via BIOS setup
3250 5 => 'BIOS Disabled', # disabled by BIOS (POST error)
3251 6 => 'Idle', # idle
3252 );
3253
3254 my %cpu_reading
3255 = (
3256 1 => 'Internal Error', # Internal Error
3257 2 => 'Thermal Trip', # Thermal Trip
3258 32 => 'Configuration Error', # Configuration Error
3259 128 => 'Present', # Processor Present
3260 256 => 'Disabled', # Processor Disabled
3261 512 => 'Terminator Present', # Terminator Present
3262 1024 => 'Throttled', # Processor Throttled
3263 );
3264
8ce893fd 3265 # Mapping between family numbers from SNMP and actual CPU family
3266 my %cpu_family
3267 = (
b221393e 3268 1 => 'Other', 2 => 'Unknown',
3269 3 => '8086', 4 => '80286',
3270 5 => '386', 6 => '486',
3271 7 => '8087', 8 => '80287',
3272 9 => '80387', 10 => '80487',
3273 11 => 'Pentium', 12 => 'Pentium Pro',
3274 13 => 'Pentium II', 14 => 'Pentium with MMX',
3275 15 => 'Celeron', 16 => 'Pentium II Xeon',
3276 17 => 'Pentium III', 18 => 'Pentium III Xeon',
3277 19 => 'Pentium III', 20 => 'Itanium',
3278 21 => 'Xeon', 22 => 'Pentium 4',
3279 23 => 'Xeon MP', 24 => 'Itanium 2',
3280 25 => 'K5', 26 => 'K6',
3281 27 => 'K6-2', 28 => 'K6-3',
3282 29 => 'Athlon', 30 => 'AMD2900',
3283 31 => 'K6-2+', 32 => 'Power PC',
3284 33 => 'Power PC 601', 34 => 'Power PC 603',
3285 35 => 'Power PC 603+', 36 => 'Power PC 604',
3286 37 => 'Power PC 620', 38 => 'Power PC x704',
3287 39 => 'Power PC 750', 40 => 'Core Duo',
3288 41 => 'Core Duo mobile', 42 => 'Core Solo mobile',
3289 43 => 'Intel Atom', 44 => undef,
3290 45 => undef, 46 => undef,
3291 47 => undef, 48 => 'Alpha',
3292 49 => 'Alpha 21064', 50 => 'Alpha 21066',
3293 51 => 'Alpha 21164', 52 => 'Alpha 21164PC',
3294 53 => 'Alpha 21164a', 54 => 'Alpha 21264',
3295 55 => 'Alpha 21364', 56 => 'Turion II Ultra Dual-Core Mobile M',
3296 57 => 'Turion II Dual-Core Mobile M', 58 => 'Athlon II Dual-Core Mobile M ',
9cc9fcac 3297 59 => 'Opteron 6100', 60 => 'Opteron 4100',
b221393e 3298 61 => undef, 62 => undef,
3299 63 => undef, 64 => 'MIPS',
3300 65 => 'MIPS R4000', 66 => 'MIPS R4200',
3301 67 => 'MIPS R4400', 68 => 'MIPS R4600',
3302 69 => 'MIPS R10000', 70 => undef,
3303 71 => undef, 72 => undef,
3304 73 => undef, 74 => undef,
3305 75 => undef, 76 => undef,
3306 77 => undef, 78 => undef,
3307 79 => undef, 80 => 'SPARC',
3308 81 => 'SuperSPARC', 82 => 'microSPARC II',
3309 83 => 'microSPARC IIep', 84 => 'UltraSPARC',
3310 85 => 'UltraSPARC II', 86 => 'UltraSPARC IIi',
3311 87 => 'UltraSPARC III', 88 => 'UltraSPARC IIIi',
3312 89 => undef, 90 => undef,
3313 91 => undef, 92 => undef,
3314 93 => undef, 94 => undef,
3315 95 => undef, 96 => '68040',
3316 97 => '68xxx', 98 => '68000',
3317 99 => '68010', 100 => '68020',
3318 101 => '68030', 102 => undef,
3319 103 => undef, 104 => undef,
3320 105 => undef, 106 => undef,
3321 107 => undef, 108 => undef,
3322 109 => undef, 110 => undef,
3323 111 => undef, 112 => 'Hobbit',
3324 113 => undef, 114 => undef,
3325 115 => undef, 116 => undef,
3326 117 => undef, 118 => undef,
3327 119 => undef, 120 => 'Crusoe TM5000',
3328 121 => 'Crusoe TM3000', 122 => 'Efficeon TM8000',
3329 123 => undef, 124 => undef,
3330 125 => undef, 126 => undef,
3331 127 => undef, 128 => 'Weitek',
3332 129 => undef, 130 => 'Celeron M',
3333 131 => 'Athlon 64', 132 => 'Opteron',
3334 133 => 'Sempron', 134 => 'Turion 64 Mobile',
3335 135 => 'Dual-Core Opteron', 136 => 'Athlon 64 X2 DC',
3336 137 => 'Turion 64 X2 M', 138 => 'Quad-Core Opteron',
3337 139 => '3rd gen Opteron', 140 => 'AMD Phenom FX Quad-Core',
3338 141 => 'AMD Phenom X4 Quad-Core', 142 => 'AMD Phenom X2 Dual-Core',
3339 143 => 'AMD Athlon X2 Dual-Core', 144 => 'PA-RISC',
3340 145 => 'PA-RISC 8500', 146 => 'PA-RISC 8000',
3341 147 => 'PA-RISC 7300LC', 148 => 'PA-RISC 7200',
3342 149 => 'PA-RISC 7100LC', 150 => 'PA-RISC 7100',
3343 151 => undef, 152 => undef,
3344 153 => undef, 154 => undef,
3345 155 => undef, 156 => undef,
3346 157 => undef, 158 => undef,
3347 159 => undef, 160 => 'V30',
3348 161 => 'Quad-Core Xeon 3200', 162 => 'Dual-Core Xeon 3000',
3349 163 => 'Quad-Core Xeon 5300', 164 => 'Dual-Core Xeon 5100',
3350 165 => 'Dual-Core Xeon 5000', 166 => 'Dual-Core Xeon LV',
3351 167 => 'Dual-Core Xeon ULV', 168 => 'Dual-Core Xeon 7100',
3352 169 => 'Quad-Core Xeon 5400', 170 => 'Quad-Core Xeon',
3353 171 => 'Dual-Core Xeon 5200', 172 => 'Dual-Core Xeon 7200',
3354 173 => 'Quad-Core Xeon 7300', 174 => 'Quad-Core Xeon 7400',
3355 175 => 'Multi-Core Xeon 7400', 176 => 'M1',
3356 177 => 'M2', 178 => undef,
3357 179 => 'Pentium 4 HT', 180 => 'AS400',
3358 181 => undef, 182 => 'Athlon XP',
3359 183 => 'Athlon MP', 184 => 'Duron',
3360 185 => 'Pentium M', 186 => 'Celeron D',
3361 187 => 'Pentium D', 188 => 'Pentium Extreme',
3362 189 => 'Core Solo', 190 => 'Core2',
3363 191 => 'Core2 Duo', 192 => 'Core2 Solo',
3364 193 => 'Core2 Extreme', 194 => 'Core2 Quad',
3365 195 => 'Core2 Extreme mobile', 196 => 'Core2 Duo mobile',
3366 197 => 'Core2 Solo mobile', 198 => 'Core i7',
3367 199 => 'Dual-Core Celeron', 200 => 'IBM390',
3368 201 => 'G4', 202 => 'G5',
3369 203 => 'ESA/390 G6', 204 => 'z/Architectur',
9cc9fcac 3370 205 => 'Core i5', 206 => 'Core i3',
b221393e 3371 207 => undef, 208 => undef,
3372 209 => undef, 210 => 'C7-M',
3373 211 => 'C7-D', 212 => 'C7',
3374 213 => 'Eden', 214 => 'Multi-Core Xeon',
3375 215 => 'Dual-Core Xeon 3xxx', 216 => 'Quad-Core Xeon 3xxx',
9cc9fcac 3376 217 => 'VIA Nano', 218 => 'Dual-Core Xeon 5xxx',
b221393e 3377 219 => 'Quad-Core Xeon 5xxx', 220 => undef,
3378 221 => 'Dual-Core Xeon 7xxx', 222 => 'Quad-Core Xeon 7xxx',
9cc9fcac 3379 223 => 'Multi-Core Xeon 7xxx', 224 => 'Multi-Core Xeon 3400',
b221393e 3380 225 => undef, 226 => undef,
3381 227 => undef, 228 => undef,
3382 229 => undef, 230 => 'Embedded AMD Opteron Quad-Core',
3383 231 => 'AMD Phenom Triple-Core', 232 => 'AMD Turion Ultra Dual-Core Mobile',
3384 233 => 'AMD Turion Dual-Core Mobile', 234 => 'AMD Athlon Dual-Core',
3385 235 => 'AMD Sempron SI', 236 => 'AMD Phenom II',
3386 237 => 'AMD Athlon II', 238 => 'Six-Core AMD Opteron',
3387 239 => 'AMD Sempron M', 240 => undef,
3388 241 => undef, 242 => undef,
3389 243 => undef, 244 => undef,
3390 245 => undef, 246 => undef,
3391 247 => undef, 248 => undef,
3392 249 => undef, 250 => 'i860',
3393 251 => 'i960',
8ce893fd 3394 );
669797e1 3395
3396 CPU:
3397 foreach my $out (@output) {
3398 if ($snmp) {
8ce893fd 3399 $index = exists $out->{processorDeviceStatusIndex}
3400 ? $out->{processorDeviceStatusIndex} - 1
3401 : $out->{processorDeviceIndex} - 1;
3402 $status = exists $out->{processorDeviceStatusStatus}
3403 ? $snmp_status{$out->{processorDeviceStatusStatus}}
3404 : $snmp_status{$out->{processorDeviceStatus}};
3405 if (exists $out->{processorDeviceStatusReading}) {
669797e1 3406 my @states = (); # contains states for the CPU
669797e1 3407
3408 # get the combined state from the StatusReading OID
3409 foreach my $mask (sort keys %cpu_reading) {
3410 if (($out->{processorDeviceStatusReading} & $mask) != 0) {
3411 push @states, $cpu_reading{$mask};
3412 }
3413 }
3414
3415 # Finally, create the state string
3416 $state = join q{, }, @states;
3417 }
3418 else {
912d8679 3419 $state = get_hashval($out->{processorDeviceStatusState}, \%cpu_state);
669797e1 3420 }
8ce893fd 3421 $man = $out->{processorDeviceManufacturerName};
87a0958c 3422 $family = (exists $out->{processorDeviceFamily}
04a878db 3423 and exists $cpu_family{$out->{processorDeviceFamily}})
3424 ? $cpu_family{$out->{processorDeviceFamily}} : undef;
8ce893fd 3425 $speed = $out->{processorDeviceCurrentSpeed};
3426 $brand = $out->{processorDeviceBrandName};
669797e1 3427 }
3428 else {
3429 $index = $out->{'Index'};
3430 $status = $out->{'Status'};
3431 $state = $out->{'State'};
8ce893fd 3432 $brand = exists $out->{'Processor Brand'} ? $out->{'Processor Brand'} : undef;
3433 $family = exists $out->{'Processor Family'} ? $out->{'Processor Family'} : undef;
3434 $man = exists $out->{'Processor Manufacturer'} ? $out->{'Processor Manufacturer'} : undef;
3435 $speed = exists $out->{'Current Speed'} ? $out->{'Current Speed'} : undef;
669797e1 3436 }
3437
3438 next CPU if blacklisted('cpu', $index);
3439
3440 # Ignore unoccupied CPU slots (omreport)
3441 next CPU if (defined $out->{'Processor Manufacturer'}
3442 and $out->{'Processor Manufacturer'} eq '[Not Occupied]')
3443 or (defined $out->{'Processor Brand'} and $out->{'Processor Brand'} eq '[Not Occupied]');
3444
3445 # Ignore unoccupied CPU slots (snmp)
3446 if ($snmp and exists $out->{processorDeviceStatusReading}
3447 and $out->{processorDeviceStatusReading} == 0) {
3448 next CPU;
3449 }
3450
3451 $count{cpu}++;
3452
8ce893fd 3453 if (defined $brand) {
3454 $brand =~ s{\s\s+}{ }gxms;
e7dc67d0 3455 $brand =~ s{\((R|tm)\)}{}gxms;
3456 $brand =~ s{\s(CPU|Processor)}{}xms;
8ce893fd 3457 $brand =~ s{\s\@}{}xms;
3458 }
3459 elsif (defined $family and defined $man and defined $speed) {
3460 $speed =~ s{\A (\d+) .*}{$1}xms;
49bf41a5 3461 $brand = sprintf '%s %s %.2fGHz', $man, $family, $speed / 1000;
8ce893fd 3462 }
3463 else {
3464 $brand = "unknown";
3465 }
3466
669797e1 3467 # Default
3468 if ($status ne 'Ok') {
0a0813de 3469 my $msg = sprintf 'Processor %d [%s] needs attention: %s',
8ce893fd 3470 $index, $brand, $state;
669797e1 3471 report('chassis', $msg, $status2nagios{$status}, $index);
3472 }
3473 # Ok
3474 else {
0a0813de 3475 my $msg = sprintf 'Processor %d [%s] is %s',
8ce893fd 3476 $index, $brand, $state;
669797e1 3477 report('chassis', $msg, $E_OK, $index);
3478 }
3479 }
3480 return;
3481}
3482
3483
3484#-----------------------------------------
3485# CHASSIS: Check voltage probes
3486#-----------------------------------------
3487sub check_volts {
5ecf578c 3488 return if blacklisted('volt', 'all');
3489
669797e1 3490 my $index = undef;
3491 my $status = undef;
3492 my $reading = undef;
3493 my $location = undef;
3494 my @output = ();
3495
3496 if ($snmp) {
3497 my %volt_oid
3498 = (
3499 '1.3.6.1.4.1.674.10892.1.600.20.1.2.1' => 'voltageProbeIndex',
3500 '1.3.6.1.4.1.674.10892.1.600.20.1.5.1' => 'voltageProbeStatus',
3501 '1.3.6.1.4.1.674.10892.1.600.20.1.6.1' => 'voltageProbeReading',
3502 '1.3.6.1.4.1.674.10892.1.600.20.1.8.1' => 'voltageProbeLocationName',
3503 '1.3.6.1.4.1.674.10892.1.600.20.1.16.1' => 'voltageProbeDiscreteReading',
3504 );
ba199ee0 3505
3506 my $voltageProbeTable = '1.3.6.1.4.1.674.10892.1.600.20.1';
3507 my $result = $snmp_session->get_table(-baseoid => $voltageProbeTable);
669797e1 3508
3509 if (!defined $result) {
98b224a3 3510 printf "SNMP ERROR [voltage]: %s.\n", $snmp_session->error;
669797e1 3511 $snmp_session->close;
3512 exit $E_UNKNOWN;
3513 }
3514
3515 @output = @{ get_snmp_output($result, \%volt_oid) };
3516 }
3517 else {
3518 @output = @{ run_omreport("$omopt_chassis volts") };
3519 }
3520
3521 my %volt_discrete_reading
3522 = (
3523 1 => 'Good',
3524 2 => 'Bad',
3525 );
3526
3527 VOLT:
3528 foreach my $out (@output) {
3529 if ($snmp) {
3530 $index = $out->{voltageProbeIndex} - 1;
ffa570fc 3531 $status = $snmp_probestatus{$out->{voltageProbeStatus}};
669797e1 3532 $reading = exists $out->{voltageProbeReading}
3533 ? sprintf('%.3f V', $out->{voltageProbeReading}/1000)
912d8679 3534 : get_hashval($out->{voltageProbeDiscreteReading}, \%volt_discrete_reading);
669797e1 3535 $location = $out->{voltageProbeLocationName};
3536 }
3537 else {
3538 $index = $out->{'Index'};
3539 $status = $out->{'Status'};
3540 $reading = $out->{'Reading'};
3541 $location = $out->{'Probe Name'};
3542 }
3543
3544 next VOLT if blacklisted('volt', $index);
3545 $count{volt}++;
3546
98b224a3 3547 my $msg = sprintf 'Voltage sensor %d [%s] is %s',
669797e1 3548 $index, $location, $reading;
3549 my $err = $snmp ? $probestatus2nagios{$status} : $status2nagios{$status};
3550 report('chassis', $msg, $err, $index);
3551 }
3552 return;
3553}
3554
3555
3556#-----------------------------------------
3557# CHASSIS: Check batteries
3558#-----------------------------------------
3559sub check_batteries {
5ecf578c 3560 return if blacklisted('bp', 'all');
3561
669797e1 3562 my $index = undef;
3563 my $status = undef;
3564 my $reading = undef;
3565 my $location = undef;
3566 my @output = ();
3567
3568 if ($snmp) {
3569 my %bat_oid
3570 = (
3571 '1.3.6.1.4.1.674.10892.1.600.50.1.2.1' => 'batteryIndex',
3572 '1.3.6.1.4.1.674.10892.1.600.50.1.5.1' => 'batteryStatus',
3573 '1.3.6.1.4.1.674.10892.1.600.50.1.6.1' => 'batteryReading',
3574 '1.3.6.1.4.1.674.10892.1.600.50.1.7.1' => 'batteryLocationName',
3575 );
4cabd748 3576 my $result = undef;
3577 if ($opt{use_get_table}) {
3578 my $batteryTable = '1.3.6.1.4.1.674.10892.1.600.50.1';
3579 $result = $snmp_session->get_table(-baseoid => $batteryTable);
3580 }
3581 else {
3582 $result = $snmp_session->get_entries(-columns => [keys %bat_oid]);
3583 }
669797e1 3584
3585 # No batteries is OK
3586 return 0 if !defined $result;
3587
3588 @output = @{ get_snmp_output($result, \%bat_oid) };
3589 }
3590 else {
3591 @output = @{ run_omreport("$omopt_chassis batteries") };
3592 }
3593
3594 my %bat_reading
3595 = (
3596 1 => 'Predictive Failure',
3597 2 => 'Failed',
3598 4 => 'Presence Detected',
3599 );
3600
3601 BATTERY:
3602 foreach my $out (@output) {
3603 if ($snmp) {
3604 $index = $out->{batteryIndex} - 1;
3605 $status = $snmp_status{$out->{batteryStatus}};
912d8679 3606 $reading = get_hashval($out->{batteryReading}, \%bat_reading);
669797e1 3607 $location = $out->{batteryLocationName};
3608 }
3609 else {
3610 $index = $out->{'Index'};
3611 $status = $out->{'Status'};
3612 $reading = $out->{'Reading'};
3613 $location = $out->{'Probe Name'};
3614 }
3615
3616 next BATTERY if blacklisted('bp', $index);
3617 $count{bat}++;
3618
98b224a3 3619 my $msg = sprintf 'Battery probe %d [%s] is %s',
669797e1 3620 $index, $location, $reading;
3621 report('chassis', $msg, $status2nagios{$status}, $index);
3622 }
3623 return;
3624}
3625
3626
3627#-----------------------------------------
3628# CHASSIS: Check amperage probes (power monitoring)
3629#-----------------------------------------
3630sub check_pwrmonitoring {
5ecf578c 3631 return if blacklisted('amp', 'all');
3632
669797e1 3633 my $index = undef;
3634 my $status = undef;
3635 my $reading = undef;
3636 my $location = undef;
3637 my $max_crit = undef;
3638 my $max_warn = undef;
3639 my $unit = undef;
3af78850 3640 my $type = undef;
669797e1 3641 my @output = ();
3642
3643 if ($snmp) {
3644 my %amp_oid
3645 = (
3646 '1.3.6.1.4.1.674.10892.1.600.30.1.2.1' => 'amperageProbeIndex',
3647 '1.3.6.1.4.1.674.10892.1.600.30.1.5.1' => 'amperageProbeStatus',
3648 '1.3.6.1.4.1.674.10892.1.600.30.1.6.1' => 'amperageProbeReading',
3649 '1.3.6.1.4.1.674.10892.1.600.30.1.7.1' => 'amperageProbeType',
3650 '1.3.6.1.4.1.674.10892.1.600.30.1.8.1' => 'amperageProbeLocationName',
3651 '1.3.6.1.4.1.674.10892.1.600.30.1.10.1' => 'amperageProbeUpperCriticalThreshold',
3652 '1.3.6.1.4.1.674.10892.1.600.30.1.11.1' => 'amperageProbeUpperNonCriticalThreshold',
3653 '1.3.6.1.4.1.674.10892.1.600.30.1.16.1' => 'amperageProbeDiscreteReading',
3654 );
4cabd748 3655 my $result = undef;
3656 if ($opt{use_get_table}) {
3657 my $amperageProbeTable = '1.3.6.1.4.1.674.10892.1.600.30.1';
3658 $result = $snmp_session->get_table(-baseoid => $amperageProbeTable);
3659 }
3660 else {
3661 $result = $snmp_session->get_entries(-columns => [keys %amp_oid]);
3662 }
669797e1 3663
3664 # No pwrmonitoring is OK
3665 return 0 if !defined $result;
3666
3667 @output = @{ get_snmp_output($result, \%amp_oid) };
3668 }
3669 else {
3670 @output = @{ run_omreport("$omopt_chassis pwrmonitoring") };
3671 }
3672
3673 my %amp_type # Amperage probe types
3674 = (
3675 1 => 'amperageProbeTypeIsOther', # other than following values
3676 2 => 'amperageProbeTypeIsUnknown', # unknown
3677 3 => 'amperageProbeTypeIs1Point5Volt', # 1.5 amperage probe
3678 4 => 'amperageProbeTypeIs3Point3volt', # 3.3 amperage probe
3679 5 => 'amperageProbeTypeIs5Volt', # 5 amperage probe
3680 6 => 'amperageProbeTypeIsMinus5Volt', # -5 amperage probe
3681 7 => 'amperageProbeTypeIs12Volt', # 12 amperage probe
3682 8 => 'amperageProbeTypeIsMinus12Volt', # -12 amperage probe
3683 9 => 'amperageProbeTypeIsIO', # I/O probe
3684 10 => 'amperageProbeTypeIsCore', # Core probe
3685 11 => 'amperageProbeTypeIsFLEA', # FLEA (standby) probe
3686 12 => 'amperageProbeTypeIsBattery', # Battery probe
3687 13 => 'amperageProbeTypeIsTerminator', # SCSI Termination probe
3688 14 => 'amperageProbeTypeIs2Point5Volt', # 2.5 amperage probe
3689 15 => 'amperageProbeTypeIsGTL', # GTL (ground termination logic) probe
3690 16 => 'amperageProbeTypeIsDiscrete', # amperage probe with discrete reading
3691 23 => 'amperageProbeTypeIsPowerSupplyAmps', # Power Supply probe with reading in Amps
3692 24 => 'amperageProbeTypeIsPowerSupplyWatts', # Power Supply probe with reading in Watts
3693 25 => 'amperageProbeTypeIsSystemAmps', # System probe with reading in Amps
3694 26 => 'amperageProbeTypeIsSystemWatts', # System probe with reading in Watts
3695 );
3696
3697 my %amp_discrete
3698 = (
3699 1 => 'Good',
3700 2 => 'Bad',
3701 );
3702
3703 my %amp_unit
3704 = (
3705 'amperageProbeTypeIsPowerSupplyAmps' => 'hA', # tenths of Amps
3706 'amperageProbeTypeIsSystemAmps' => 'hA', # tenths of Amps
3707 'amperageProbeTypeIsPowerSupplyWatts' => 'W', # Watts
3708 'amperageProbeTypeIsSystemWatts' => 'W', # Watts
3709 'amperageProbeTypeIsDiscrete' => q{}, # discrete reading, no unit
3710 );
3711
3712 AMP:
3713 foreach my $out (@output) {
3714 if ($snmp) {
3715 $index = $out->{amperageProbeIndex} - 1;
3716 $status = $snmp_status{$out->{amperageProbeStatus}};
3af78850 3717 $type = get_hashval($out->{amperageProbeType}, \%amp_type);
3718 $reading = $type eq 'amperageProbeTypeIsDiscrete'
912d8679 3719 ? get_hashval($out->{amperageProbeDiscreteReading}, \%amp_discrete)
669797e1 3720 : $out->{amperageProbeReading};
3721 $location = $out->{amperageProbeLocationName};
3722 $max_crit = exists $out->{amperageProbeUpperCriticalThreshold}
3723 ? $out->{amperageProbeUpperCriticalThreshold} : 0;
3724 $max_warn = exists $out->{amperageProbeUpperNonCriticalThreshold}
3725 ? $out->{amperageProbeUpperNonCriticalThreshold} : 0;
3726 $unit = exists $amp_unit{$amp_type{$out->{amperageProbeType}}}
3727 ? $amp_unit{$amp_type{$out->{amperageProbeType}}} : 'mA';
3af78850 3728
3729 # workaround for broken probes
3730 if (!defined $reading) {
3731 $type = 'amperageProbeTypeIsDiscrete';
3732 $reading = '[N/A]';
83f8d89f 3733 $unit = q{};
3af78850 3734 }
3735
3736 # calculate proper values and set unit for ampere probes
3737 if ($unit eq 'hA' and $type ne 'amperageProbeTypeIsDiscrete') {
669797e1 3738 $reading /= 10;
3739 $max_crit /= 10;
3740 $max_warn /= 10;
3741 $unit = 'A';
3742 }
3743 }
3744 else {
3745 $index = $out->{'Index'};
0be00f80 3746 next AMP if (!defined $index || $index !~ m/^\d+$/x);
669797e1 3747 $status = $out->{'Status'};
3748 $reading = $out->{'Reading'};
3749 $location = $out->{'Probe Name'};
3750 $max_crit = $out->{'Failure Threshold'} ne '[N/A]'
3751 ? $out->{'Failure Threshold'} : 0;
3752 $max_warn = $out->{'Warning Threshold'} ne '[N/A]'
3753 ? $out->{'Warning Threshold'} : 0;
3754 $reading =~ s{\A (\d+.*?)\s+([a-zA-Z]+) \s*\z}{$1}xms;
3755 $unit = $2;
3756 $max_warn =~ s{\A (\d+.*?)\s+[a-zA-Z]+ \s*\z}{$1}xms;
3757 $max_crit =~ s{\A (\d+.*?)\s+[a-zA-Z]+ \s*\z}{$1}xms;
3758 }
3759
78dbab97 3760 next AMP if blacklisted('amp', $index);
669797e1 3761 next AMP if $index !~ m{\A \d+ \z}xms;
3762 $count{amp}++;
3763
6a9a6fd1 3764 if (defined $type and $type eq 'amperageProbeTypeIsDiscrete') {
e8413daf 3765 my $msg = sprintf 'Amperage probe %d [%s] is %s',
3766 $index, $location, $reading;
3767 report('chassis', $msg, $status2nagios{$status}, $index);
3768 }
3769 else {
3770 my $msg = sprintf 'Amperage probe %d [%s] reads %s %s',
3771 $index, $location, $reading, $unit;
3772 report('chassis', $msg, $status2nagios{$status}, $index);
3773 }
669797e1 3774
3775 # Collect performance data
3776 if (defined $opt{perfdata}) {
3777 next AMP if $reading !~ m{\A \d+(\.\d+)? \z}xms; # discrete reading (not number)
3af78850 3778 #next AMP if $type eq 'amperageProbeTypeIsDiscrete';
48aeec0b 3779 my $label = join q{_}, 'pwr_mon', $index, lc $location;
3780 $label =~ s{\s}{_}gxms;
3781 push @perfdata, {
3782 label => $label,
aa629782 3783 mini => "p${index}" . lc $unit,
48aeec0b 3784 value => $reading,
48aeec0b 3785 warn => $max_warn,
3786 crit => $max_crit,
3787 };
669797e1 3788 }
3789 }
3790
3791 # Collect EXTRA performance data not found at first run. This is a
3792 # rather ugly hack
3793 if (defined $opt{perfdata} && !$snmp) {
3794 my $found = 0;
3795 my $index = 0;
3796 my %used = ();
3797
3798 # find used indexes
48aeec0b 3799 foreach (@perfdata) {
3800 if ($_->{label} =~ m/\A pwr_mon_(\d+)/xms) {
669797e1 3801 $used{$1} = 1;
3802 }
3803 }
3804
3805 AMP2:
3806 foreach my $line (@{ run_command("$omreport $omopt_chassis pwrmonitoring -fmt ssv") }) {
3807 chop $line;
3808 if ($line eq 'Location;Reading') {
3809 $found = 1;
3810 next AMP2;
3811 }
3812 if ($line eq q{}) {
3813 $found = 0;
3814 next AMP2;
3815 }
c000d734 3816 if ($found and $line =~ m/\A ([^;]+?) ; (\d*\.\d+) \s [AW] \z/xms) {
669797e1 3817 my $aname = lc $1;
3818 my $aval = $2;
669797e1 3819 $aname =~ s{\s}{_}gxms;
3820
3821 # don't use an existing index
3822 while (exists $used{$index}) { ++$index; }
3823
48aeec0b 3824 push @perfdata, {
3825 label => "pwr_mon_${index}_${aname}",
5e76352f 3826 mini => "p${index}a",
48aeec0b 3827 value => $aval,
48aeec0b 3828 warn => 0,
3829 crit => 0,
3830 };
669797e1 3831 ++$index;
3832 }
3833 }
3834 }
3835
3836 return;
3837}
3838
3839
3840#-----------------------------------------
3841# CHASSIS: Check intrusion
3842#-----------------------------------------
3843sub check_intrusion {
5ecf578c 3844 return if blacklisted('intr', 'all');
3845
669797e1 3846 my $index = undef;
3847 my $status = undef;
3848 my $reading = undef;
3849 my @output = ();
3850
3851 if ($snmp) {
3852 my %int_oid
3853 = (
3854 '1.3.6.1.4.1.674.10892.1.300.70.1.2.1' => 'intrusionIndex',
3855 '1.3.6.1.4.1.674.10892.1.300.70.1.5.1' => 'intrusionStatus',
3856 '1.3.6.1.4.1.674.10892.1.300.70.1.6.1' => 'intrusionReading',
3857 );
4cabd748 3858 my $result = undef;
3859 if ($opt{use_get_table}) {
3860 my $intrusionTable = '1.3.6.1.4.1.674.10892.1.300.70.1';
3861 $result = $snmp_session->get_table(-baseoid => $intrusionTable);
3862 }
3863 else {
3864 $result = $snmp_session->get_entries(-columns => [keys %int_oid]);
3865 }
669797e1 3866
3867 # No intrusion is OK
3868 return 0 if !defined $result;
3869
3870 @output = @{ get_snmp_output($result, \%int_oid) };
3871 }
3872 else {
3873 @output = @{ run_omreport("$omopt_chassis intrusion") };
3874 }
3875
3876 my %int_reading
3877 = (
3878 1 => 'Not Breached', # chassis not breached and no uncleared breaches
3879 2 => 'Breached', # chassis currently breached
3880 3 => 'Breached Prior', # chassis breached prior to boot and has not been cleared
3881 4 => 'Breach Sensor Failure', # intrusion sensor has failed
3882 );
3883
3884 INTRUSION:
3885 foreach my $out (@output) {
3886 if ($snmp) {
3887 $index = $out->{intrusionIndex} - 1;
3888 $status = $snmp_status{$out->{intrusionStatus}};
912d8679 3889 $reading = get_hashval($out->{intrusionReading}, \%int_reading);
669797e1 3890 }
3891 else {
3892 $index = $out->{'Index'};
3893 $status = $out->{'Status'};
3894 $reading = $out->{'State'};
3895 }
3896
3897 next INTRUSION if blacklisted('intr', $index);
3898 $count{intr}++;
3899
3900 if ($status ne 'Ok') {
3901 my $msg = sprintf 'Chassis intrusion %d detected: %s',
3902 $index, $reading;
3903 report('chassis', $msg, $E_WARNING, $index);
3904 }
3905 # Ok
3906 else {
3907 my $msg = sprintf 'Chassis intrusion %d detection: %s (%s)',
3908 $index, $status, $reading;
3909 report('chassis', $msg, $E_OK, $index);
3910 }
3911 }
3912 return;
3913}
3914
3915
3916#-----------------------------------------
3917# CHASSIS: Check alert log
3918#-----------------------------------------
3919sub check_alertlog {
3920 return if $snmp; # Not supported with SNMP
3921
3922 my @output = @{ run_omreport("$omopt_system alertlog") };
3923 foreach my $out (@output) {
3924 ++$count{alert}{$out->{Severity}};
3925 }
3926
3927 # Create error messages and set exit value if appropriate
3928 my $err = 0;
3929 if ($count{alert}{'Critical'} > 0) { $err = $E_CRITICAL; }
3930 elsif ($count{alert}{'Non-Critical'} > 0) { $err = $E_WARNING; }
3931
3932 my $msg = sprintf 'Alert log content: %d critical, %d non-critical, %d ok',
3933 $count{alert}{'Critical'}, $count{alert}{'Non-Critical'}, $count{alert}{'Ok'};
3934 report('other', $msg, $err);
3935
3936 return;
3937}
3938
3939#-----------------------------------------
3940# CHASSIS: Check ESM log overall health
3941#-----------------------------------------
3942sub check_esmlog_health {
3943 my $health = 'Ok';
3944
3945 if ($snmp) {
3946 my $systemStateEventLogStatus = '1.3.6.1.4.1.674.10892.1.200.10.1.41.1';
3947 my $result = $snmp_session->get_request(-varbindlist => [$systemStateEventLogStatus]);
3948 if (!defined $result) {
98b224a3 3949 my $msg = sprintf 'SNMP ERROR [esmhealth]: %s',
669797e1 3950 $snmp_session->error;
3951 report('other', $msg, $E_UNKNOWN);
3952 }
3953 $health = $snmp_status{$result->{$systemStateEventLogStatus}};
3954 }
3955 else {
3956 foreach (@{ run_command("$omreport $omopt_system esmlog -fmt ssv") }) {
3957 if (m/\A Health;(.+) \z/xms) {
3958 $health = $1;
3959 chop $health;
3960 last;
3961 }
3962 }
3963 }
3964
3965 # If the overall health of the ESM log is other than "Ok", the
3966 # fill grade of the log is more than 80% and the log should be
3967 # cleared
3968 if ($health eq 'Ok') {
af7c7f76 3969 my $msg = sprintf 'ESM log health is Ok (less than 80%% full)';
669797e1 3970 report('other', $msg, $E_OK);
3971 }
3972 elsif ($health eq 'Critical') {
328d0a74 3973 my $msg = sprintf 'ESM log is 100%% full';
669797e1 3974 report('other', $msg, $status2nagios{$health});
3975 }
3976 else {
3977 my $msg = sprintf 'ESM log is more than 80%% full';
3978 report('other', $msg, $status2nagios{$health});
3979 }
3980
3981 return;
3982}
3983
3984#-----------------------------------------
3985# CHASSIS: Check ESM log
3986#-----------------------------------------
3987sub check_esmlog {
3988 my @output = ();
3989
3990 if ($snmp) {
3991 my %esm_oid
3992 = (
3993 '1.3.6.1.4.1.674.10892.1.300.40.1.7.1' => 'eventLogSeverityStatus',
3994 );
3995 my $result = $snmp_session->get_entries(-columns => [keys %esm_oid]);
3996
3997 # No entries is OK
3998 return if !defined $result;
3999
4000 @output = @{ get_snmp_output($result, \%esm_oid) };
4001 foreach my $out (@output) {
4002 ++$count{esm}{$snmp_status{$out->{eventLogSeverityStatus}}};
4003 }
4004 }
4005 else {
4006 @output = @{ run_omreport("$omopt_system esmlog") };
4007 foreach my $out (@output) {
4008 ++$count{esm}{$out->{Severity}};
4009 }
4010 }
4011
4012 # Create error messages and set exit value if appropriate
4013 my $err = 0;
4014 if ($count{esm}{'Critical'} > 0) { $err = $E_CRITICAL; }
4015 elsif ($count{esm}{'Non-Critical'} > 0) { $err = $E_WARNING; }
4016
4017 my $msg = sprintf 'ESM log content: %d critical, %d non-critical, %d ok',
4018 $count{esm}{'Critical'}, $count{esm}{'Non-Critical'}, $count{esm}{'Ok'};
4019 report('other', $msg, $err);
4020
4021 return;
4022}
4023
4024#
4025# Handy function for checking all storage components
4026#
4027sub check_storage {
4028 check_controllers();
4029 check_physical_disks();
4030 check_virtual_disks();
4031 check_cache_battery();
4032 check_connectors();
4033 check_enclosures();
4034 check_enclosure_fans();
4035 check_enclosure_pwr();
4036 check_enclosure_temp();
4037 check_enclosure_emms();
4038 return;
4039}
4040
4041
4042
4043#---------------------------------------------------------------------
4044# Info functions
4045#---------------------------------------------------------------------
4046
4047#
4048# Fetch output from 'omreport chassis info', put in sysinfo hash
4049#
4050sub get_omreport_chassis_info {
4051 if (open my $INFO, '-|', "$omreport $omopt_chassis info -fmt ssv") {
4052 my @lines = <$INFO>;
4053 close $INFO;
4054 foreach (@lines) {
4055 next if !m/\A (Chassis\sModel|Chassis\sService\sTag|Model|Service\sTag)/xms;
4056 my ($key, $val) = split /;/xms;
4057 $key =~ s{\s+\z}{}xms; # remove trailing whitespace
4058 $val =~ s{\s+\z}{}xms; # remove trailing whitespace
4059 if ($key eq 'Chassis Model' or $key eq 'Model') {
4060 $sysinfo{model} = $val;
4061 }
4062 if ($key eq 'Chassis Service Tag' or $key eq 'Service Tag') {
4063 $sysinfo{serial} = $val;
4064 }
4065 }
4066 }
4067 return;
4068}
4069
4070#
4071# Fetch output from 'omreport chassis bios', put in sysinfo hash
4072#
4073sub get_omreport_chassis_bios {
4074 if (open my $BIOS, '-|', "$omreport $omopt_chassis bios -fmt ssv") {
4075 my @lines = <$BIOS>;
4076 close $BIOS;
4077 foreach (@lines) {
4078 next if !m/;/xms;
4079 my ($key, $val) = split /;/xms;
4080 $key =~ s{\s+\z}{}xms; # remove trailing whitespace
4081 $val =~ s{\s+\z}{}xms; # remove trailing whitespace
4082 $sysinfo{bios} = $val if $key eq 'Version';
4083 $sysinfo{biosdate} = $val if $key eq 'Release Date';
4084 }
4085 }
4086 return;
4087}
4088
4089#
4090# Fetch output from 'omreport system operatingsystem', put in sysinfo hash
4091#
4092sub get_omreport_system_operatingsystem {
4093 if (open my $VER, '-|', "$omreport $omopt_system operatingsystem -fmt ssv") {
4094 my @lines = <$VER>;
4095 close $VER;
4096 foreach (@lines) {
4097 next if !m/;/xms;
4098 my ($key, $val) = split /;/xms;
4099 $key =~ s{\s+\z}{}xms; # remove trailing whitespace
4100 $val =~ s{\s+\z}{}xms; # remove trailing whitespace
4101 if ($key eq 'Operating System') {
4102 $sysinfo{osname} = $val;
4103 }
4104 elsif ($key eq 'Operating System Version') {
4105 $sysinfo{osver} = $val;
4106 }
4107 }
4108 }
4109 return;
4110}
4111
4112#
4113# Fetch output from 'omreport about', put in sysinfo hash
4114#
4115sub get_omreport_about {
4116 if (open my $OM, '-|', "$omreport about -fmt ssv") {
4117 my @lines = <$OM>;
4118 close $OM;
4119 foreach (@lines) {
4120 if (m/\A Version;(.+) \z/xms) {
4121 $sysinfo{om} = $1;
4122 chomp $sysinfo{om};
4123 }
4124 }
4125 }
4126 return;
4127}
4128
4129#
4130# Fetch chassis info via SNMP, put in sysinfo hash
4131#
4132sub get_snmp_chassis_info {
4133 my %chassis_oid
4134 = (
4135 '1.3.6.1.4.1.674.10892.1.300.10.1.9.1' => 'chassisModelName',
4136 '1.3.6.1.4.1.674.10892.1.300.10.1.11.1' => 'chassisServiceTagName',
4137 );
4138
4139 my $chassisInformationTable = '1.3.6.1.4.1.674.10892.1.300.10.1';
4140 my $result = $snmp_session->get_table(-baseoid => $chassisInformationTable);
4141
4142 if (defined $result) {
4143 foreach my $oid (keys %{ $result }) {
4144 if (exists $chassis_oid{$oid} and $chassis_oid{$oid} eq 'chassisModelName') {
4145 $sysinfo{model} = $result->{$oid};
4146 $sysinfo{model} =~ s{\s+\z}{}xms; # remove trailing whitespace
4147 }
4148 elsif (exists $chassis_oid{$oid} and $chassis_oid{$oid} eq 'chassisServiceTagName') {
4149 $sysinfo{serial} = $result->{$oid};
4150 }
4151 }
4152 }
4153 else {
4154 my $msg = sprintf 'SNMP ERROR getting chassis info: %s',
4155 $snmp_session->error;
4156 report('other', $msg, $E_UNKNOWN);
4157 }
4158 return;
4159}
4160
4161#
4162# Fetch BIOS info via SNMP, put in sysinfo hash
4163#
4164sub get_snmp_chassis_bios {
4165 my %bios_oid
4166 = (
4167 '1.3.6.1.4.1.674.10892.1.300.50.1.7.1.1' => 'systemBIOSReleaseDateName',
4168 '1.3.6.1.4.1.674.10892.1.300.50.1.8.1.1' => 'systemBIOSVersionName',
4169 );
4170
4171 my $systemBIOSTable = '1.3.6.1.4.1.674.10892.1.300.50.1';
4172 my $result = $snmp_session->get_table(-baseoid => $systemBIOSTable);
4173
4174 if (defined $result) {
4175 foreach my $oid (keys %{ $result }) {
4176 if (exists $bios_oid{$oid} and $bios_oid{$oid} eq 'systemBIOSReleaseDateName') {
4177 $sysinfo{biosdate} = $result->{$oid};
4178 $sysinfo{biosdate} =~ s{\A (\d{4})(\d{2})(\d{2}).*}{$2/$3/$1}xms;
4179 }
4180 elsif (exists $bios_oid{$oid} and $bios_oid{$oid} eq 'systemBIOSVersionName') {
4181 $sysinfo{bios} = $result->{$oid};
4182 }
4183 }
4184 }
4185 else {
4186 my $msg = sprintf 'SNMP ERROR getting BIOS info: %s',
4187 $snmp_session->error;
4188 report('other', $msg, $E_UNKNOWN);
4189 }
4190 return;
4191}
4192
4193#
4194# Fetch OS info via SNMP, put in sysinfo hash
4195#
4196sub get_snmp_system_operatingsystem {
4197 my %os_oid
4198 = (
4199 '1.3.6.1.4.1.674.10892.1.400.10.1.6.1' => 'operatingSystemOperatingSystemName',
4200 '1.3.6.1.4.1.674.10892.1.400.10.1.7.1' => 'operatingSystemOperatingSystemVersionName',
4201 );
4202
4203 my $operatingSystemTable = '1.3.6.1.4.1.674.10892.1.400.10.1';
4204 my $result = $snmp_session->get_table(-baseoid => $operatingSystemTable);
4205
4206 if (defined $result) {
4207 foreach my $oid (keys %{ $result }) {
4208 if (exists $os_oid{$oid} and $os_oid{$oid} eq 'operatingSystemOperatingSystemName') {
4209 $sysinfo{osname} = ($result->{$oid});
4210 }
4211 elsif (exists $os_oid{$oid} and $os_oid{$oid} eq 'operatingSystemOperatingSystemVersionName') {
4212 $sysinfo{osver} = $result->{$oid};
4213 }
4214 }
4215 }
4216 else {
4217 my $msg = sprintf 'SNMP ERROR getting OS info: %s',
4218 $snmp_session->error;
4219 report('other', $msg, $E_UNKNOWN);
4220 }
4221 return;
4222}
4223
4224#
4225# Fetch OMSA version via SNMP, put in sysinfo hash
4226#
4227sub get_snmp_about {
00d4098a 4228 # systemManagementSoftwareGlobalVersionName
4229 my $oid = '1.3.6.1.4.1.674.10892.1.100.10.0';
4230 my $result = $snmp_session->get_request(-varbindlist => [$oid]);
4231
df0b121b 4232 if (defined $result) {
4233 $sysinfo{om} = exists $result->{$oid} && $result->{$oid} ne q{}
4234 ? $result->{$oid} : 'unknown';
669797e1 4235 }
4236 else {
df0b121b 4237 my $msg = sprintf 'SNMP ERROR: Getting OMSA version failed: %s', $snmp_session->error;
4238 report('other', $msg, $E_UNKNOWN);
669797e1 4239 }
4240 return;
4241}
4242
4243#
4244# Collects some information about the system
4245#
4246sub get_sysinfo
4247{
4248 # Get system model and serial number
4249 $snmp ? get_snmp_chassis_info() : get_omreport_chassis_info();
4250
4251 # Get BIOS information. Only if needed
4252 if ( $opt{okinfo} >= 1
4253 or $opt{debug}
4254 or (defined $opt{postmsg} and $opt{postmsg} =~ m/[%][bd]/xms) ) {
4255 $snmp ? get_snmp_chassis_bios() : get_omreport_chassis_bios();
4256 }
4257
f711f8c7 4258 # Get OMSA information. Only if needed
4259 if ($opt{okinfo} >= 3 or $opt{debug}) {
4260 $snmp ? get_snmp_about() : get_omreport_about();
4261 }
4262
669797e1 4263 # Return now if debug
4264 return if $opt{debug};
4265
4266 # Get OS information. Only if needed
4267 if (defined $opt{postmsg} and $opt{postmsg} =~ m/[%][or]/xms) {
4268 $snmp ? get_snmp_system_operatingsystem() : get_omreport_system_operatingsystem();
4269 }
4270
669797e1 4271 return;
4272}
4273
4274
4275# Helper function for running omreport when the results are strictly
4276# name=value pairs.
4277sub run_omreport_info {
4278 my $command = shift;
4279 my %output = ();
4280 my @keys = ();
4281
4282 # Run omreport and fetch output
4283 my $rawtext = slurp_command("$omreport $command -fmt ssv 2>&1");
4284
4285 # Parse output, store in array
4286 for ((split /\n/xms, $rawtext)) {
4287 if (m/\A Error/xms) {
4288 my $msg = "Problem running 'omreport $command': $_";
4289 report('other', $msg, $E_UNKNOWN);
4290 }
4291 next if !m/;/xms; # ignore lines with less than two fields
4292 my @vals = split m/;/xms;
4293 $output{$vals[0]} = $vals[1];
4294 }
4295
4296 # Finally, return the collected information
4297 return \%output;
4298}
4299
4300# Get various firmware information (BMC, RAC)
4301sub get_firmware_info {
4302 my @snmp_output = ();
4303 my %nrpe_output = ();
4304
4305 if ($snmp) {
4306 my %fw_oid
4307 = (
4308 '1.3.6.1.4.1.674.10892.1.300.60.1.7.1' => 'firmwareType',
4309 '1.3.6.1.4.1.674.10892.1.300.60.1.8.1' => 'firmwareTypeName',
4310 '1.3.6.1.4.1.674.10892.1.300.60.1.11.1' => 'firmwareVersionName',
4311 );
4312
4313 my $firmwareTable = '1.3.6.1.4.1.674.10892.1.300.60.1';
4314 my $result = $snmp_session->get_table(-baseoid => $firmwareTable);
4315
4316 # Some don't have this OID, this is ok
4317 if (!defined $result) {
4318 return;
4319 }
4320
4321 @snmp_output = @{ get_snmp_output($result, \%fw_oid) };
4322 }
4323 else {
4324 %nrpe_output = %{ run_omreport_info("$omopt_chassis info") };
4325 }
4326
4327 my %fw_type # Firmware types
4328 = (
4329 1 => 'other', # other than following values
4330 2 => 'unknown', # unknown
4331 3 => 'systemBIOS', # System BIOS
4332 4 => 'embeddedSystemManagementController', # Embedded System Management Controller
4333 5 => 'powerSupplyParallelingBoard', # Power Supply Paralleling Board
4334 6 => 'systemBackPlane', # System (Primary) Backplane
4335 7 => 'powerVault2XXSKernel', # PowerVault 2XXS Kernel
4336 8 => 'powerVault2XXSApplication', # PowerVault 2XXS Application
4337 9 => 'frontPanel', # Front Panel Controller
4338 10 => 'baseboardManagementController', # Baseboard Management Controller
4339 11 => 'hotPlugPCI', # Hot Plug PCI Controller
4340 12 => 'sensorData', # Sensor Data Records
4341 13 => 'peripheralBay', # Peripheral Bay Backplane
4342 14 => 'secondaryBackPlane', # Secondary Backplane for ESM 2 systems
4343 15 => 'secondaryBackPlaneESM3And4', # Secondary Backplane for ESM 3 and 4 systems
4344 16 => 'rac', # Remote Access Controller
4345 17 => 'imc' # Integrated Management Controller
4346 );
4347
4348
4349 if ($snmp) {
4350 foreach my $out (@snmp_output) {
4351 if ($fw_type{$out->{firmwareType}} eq 'baseboardManagementController') {
4352 $sysinfo{'bmc'} = 1;
4353 $sysinfo{'bmc_fw'} = $out->{firmwareVersionName};
4354 }
4355 elsif ($fw_type{$out->{firmwareType}} =~ m{\A rac|imc \z}xms) {
4356 my $name = $out->{firmwareTypeName}; $name =~ s/\s//gxms;
4357 $sysinfo{'rac'} = 1;
4358 $sysinfo{'rac_name'} = $name;
4359 $sysinfo{'rac_fw'} = $out->{firmwareVersionName};
4360 }
4361 }
4362 }
4363 else {
4364 foreach my $key (keys %nrpe_output) {
4365 next if !defined $nrpe_output{$key};
4366 if ($key eq 'BMC Version' or $key eq 'Baseboard Management Controller Version') {
4367 $sysinfo{'bmc'} = 1;
4368 $sysinfo{'bmc_fw'} = $nrpe_output{$key};
4369 }
4370 elsif ($key =~ m{\A (i?DRAC)\s*(\d?)\s+Version}xms) {
4371 my $name = "$1$2";
4372 $sysinfo{'rac'} = 1;
4373 $sysinfo{'rac_fw'} = $nrpe_output{$key};
4374 $sysinfo{'rac_name'} = $name;
4375 }
4376 }
4377 }
4378
4379 return;
4380}
4381
4382
4383
4384#=====================================================================
4385# Main program
4386#=====================================================================
4387
4388# Here we do the actual checking of components
4389# Check global status if applicable
4390if ($global) {
4391 $globalstatus = check_global();
4392}
4393
4394# Do multiple selected checks
4395if ($check{storage}) { check_storage(); }
4396if ($check{memory}) { check_memory(); }
4397if ($check{fans}) { check_fans(); }
4398if ($check{power}) { check_powersupplies(); }
4399if ($check{temp}) { check_temperatures(); }
4400if ($check{cpu}) { check_processors(); }
4401if ($check{voltage}) { check_volts(); }
4402if ($check{batteries}) { check_batteries(); }
4403if ($check{amperage}) { check_pwrmonitoring(); }
4404if ($check{intrusion}) { check_intrusion(); }
4405if ($check{alertlog}) { check_alertlog(); }
4406if ($check{esmlog}) { check_esmlog(); }
4407if ($check{esmhealth}) { check_esmlog_health(); }
4408
4409
4410#---------------------------------------------------------------------
4411# Finish up
4412#---------------------------------------------------------------------
4413
4414# Counter variable
4415%nagios_alert_count
4416 = (
4417 'OK' => 0,
4418 'WARNING' => 0,
4419 'CRITICAL' => 0,
4420 'UNKNOWN' => 0,
4421 );
4422
4423# Get system information
4424get_sysinfo();
4425
4426# Get firmware info if requested via option
4427if ($opt{okinfo} >= 1) {
4428 get_firmware_info();
4429}
4430
4431# Close SNMP session
4432if ($snmp) {
4433 $snmp_session->close;
4434}
4435
4436# Print messages
4437if ($opt{debug}) {
4438 print " System: $sysinfo{model}\n";
f711f8c7 4439 print " ServiceTag: $sysinfo{serial}";
4440 print q{ } x (25 - length $sysinfo{serial}), "OMSA version: $sysinfo{om}\n";
4441 print " BIOS/date: $sysinfo{bios} $sysinfo{biosdate}";
4442 print q{ } x (25 - length "$sysinfo{bios} $sysinfo{biosdate}"), "Plugin version: $VERSION\n";
669797e1 4443 if ($#report_storage >= 0) {
4444 print "-----------------------------------------------------------------------------\n";
4445 print " Storage Components \n";
4446 print "=============================================================================\n";
4447 print " STATE | ID | MESSAGE TEXT \n";
4448 print "---------+----------+--------------------------------------------------------\n";
4449 foreach (@report_storage) {
4450 my ($msg, $level, $nexus) = @{$_};
4451 print q{ } x (8 - length $reverse_exitcode{$level}) . "$reverse_exitcode{$level} | "
4452 . q{ } x (8 - length $nexus) . "$nexus | $msg\n";
4453 $nagios_alert_count{$reverse_exitcode{$level}}++;
4454 }
4455 }
4456 if ($#report_chassis >= 0) {
4457 print "-----------------------------------------------------------------------------\n";
4458 print " Chassis Components \n";
4459 print "=============================================================================\n";
1d003803 4460 print " STATE | ID | MESSAGE TEXT \n";
669797e1 4461 print "---------+------+------------------------------------------------------------\n";
4462 foreach (@report_chassis) {
4463 my ($msg, $level, $nexus) = @{$_};
4464 print q{ } x (8 - length $reverse_exitcode{$level}) . "$reverse_exitcode{$level} | "
4465 . q{ } x (4 - length $nexus) . "$nexus | $msg\n";
4466 $nagios_alert_count{$reverse_exitcode{$level}}++;
4467 }
4468 }
4469 if ($#report_other >= 0) {
4470 print "-----------------------------------------------------------------------------\n";
4471 print " Other messages \n";
4472 print "=============================================================================\n";
4473 print " STATE | MESSAGE TEXT \n";
4474 print "---------+-------------------------------------------------------------------\n";
4475 foreach (@report_other) {
4476 my ($msg, $level, $nexus) = @{$_};
4477 print q{ } x (8 - length $reverse_exitcode{$level}) . "$reverse_exitcode{$level} | $msg\n";
4478 $nagios_alert_count{$reverse_exitcode{$level}}++;
4479 }
4480 }
4481}
4482else {
4483 my $c = 0; # counter to determine linebreaks
4484
4485 # Run through each message, sorted by severity level
4486 ALERT:
4487 foreach (sort {$a->[1] < $b->[1]} (@report_storage, @report_chassis, @report_other)) {
4488 my ($msg, $level, $nexus) = @{ $_ };
4489 next ALERT if $level == $E_OK;
4490
4491 if (defined $opt{only}) {
4492 # If user wants only critical alerts
4493 next ALERT if ($opt{only} eq 'critical' and $level == $E_WARNING);
4494
4495 # If user wants only warning alerts
4496 next ALERT if ($opt{only} eq 'warning' and $level == $E_CRITICAL);
4497 }
4498
4499 # Prefix with service tag if specified with option '-i|--info'
4500 if ($opt{info}) {
4501 if (defined $opt{htmlinfo}) {
4502 $msg = '[<a href="' . warranty_url($sysinfo{serial})
4503 . "\">$sysinfo{serial}</a>] " . $msg;
4504 }
4505 else {
4506 $msg = "[$sysinfo{serial}] " . $msg;
4507 }
4508 }
4509
4510 # Prefix with nagios level if specified with option '--state'
4511 $msg = $reverse_exitcode{$level} . ": $msg" if $opt{state};
4512
4513 # Prefix with one-letter nagios level if specified with option '--short-state'
4514 $msg = (substr $reverse_exitcode{$level}, 0, 1) . ": $msg" if $opt{shortstate};
4515
4516 ($c++ == 0) ? print $msg : print $linebreak, $msg;
4517
4518 $nagios_alert_count{$reverse_exitcode{$level}}++;
4519 }
4520}
4521
4522# Determine our exit code
4523$exit_code = $E_OK;
4524$exit_code = $E_UNKNOWN if $nagios_alert_count{'UNKNOWN'} > 0;
4525$exit_code = $E_WARNING if $nagios_alert_count{'WARNING'} > 0;
4526$exit_code = $E_CRITICAL if $nagios_alert_count{'CRITICAL'} > 0;
4527
4528# Global status via SNMP.. extra safety check
4529if ($globalstatus != $E_OK && $exit_code == $E_OK && !defined $opt{only}) {
4530 print "OOPS! Something is wrong with this server, but I don't know what. ";
4531 print "The global system health status is $reverse_exitcode{$globalstatus}, ";
4532 print "but every component check is OK. This may be a bug in the Nagios plugin, ";
4533 print "please file a bug report.\n";
4534 exit $E_UNKNOWN;
4535}
4536
4537# Print OK message
4538if ($exit_code == $E_OK && defined $opt{only} && $opt{only} !~ m{\A critical|warning|chassis \z}xms && !$opt{debug}) {
4539 my %okmsg
4540 = ( 'storage' => "STORAGE OK - $count{pdisk} physical drives, $count{vdisk} logical drives",
4541 'fans' => $count{fan} == 0 && $blade ? 'OK - blade system with no fan probes' : "FANS OK - $count{fan} fan probes checked",
4542 'temp' => "TEMPERATURES OK - $count{temp} temperature probes checked",
14e95f92 4543 'memory' => "MEMORY OK - $count{dimm} memory modules, $count{mem} MB total memory",
669797e1 4544 'power' => $count{power} == 0 ? 'OK - no instrumented power supplies found' : "POWER OK - $count{power} power supplies checked",
4545 'cpu' => "PROCESSORS OK - $count{cpu} processors checked",
4546 'voltage' => "VOLTAGE OK - $count{volt} voltage probes checked",
4547 'batteries' => $count{bat} == 0 ? 'OK - no batteries found' : "BATTERIES OK - $count{bat} batteries checked",
4548 'amperage' => $count{amp} == 0 ? 'OK - no power monitoring probes found' : "AMPERAGE OK - $count{amp} amperage (power monitoring) probes checked",
4549 'intrusion' => $count{intr} == 0 ? 'OK - no intrusion detection probes found' : "INTRUSION OK - $count{intr} intrusion detection probes checked",
4550 'alertlog' => $snmp ? 'OK - not supported via snmp' : "OK - Alert Log content: $count{alert}{Ok} ok, $count{alert}{'Non-Critical'} warning and $count{alert}{Critical} critical",
4551 'esmlog' => "OK - ESM Log content: $count{esm}{Ok} ok, $count{esm}{'Non-Critical'} warning and $count{esm}{Critical} critical",
4552 'esmhealth' => "ESM LOG OK - less than 80% used",
4553 );
4554
4555 print $okmsg{$opt{only}};
4556}
4557elsif ($exit_code == $E_OK && !$opt{debug}) {
4558 if (defined $opt{htmlinfo}) {
b3ce470d 4559 printf q{OK - System: '<a href="%s">%s</a>', SN: '<a href="%s">%s</a>'},
669797e1 4560 documentation_url($sysinfo{model}), $sysinfo{model},
4561 warranty_url($sysinfo{serial}), $sysinfo{serial};
4562 }
4563 else {
14e95f92 4564 printf q{OK - System: '%s', SN: '%s'},
669797e1 4565 $sysinfo{model}, $sysinfo{serial};
4566 }
4567
14e95f92 4568 if ($check{memory}) {
4569 my $unit = 'MB';
4570 if ($count{mem} >= 1024) {
4571 $count{mem} /= 1024;
4572 $unit = 'GB';
4573 }
4574 printf ', %d %s ram (%d dimms)', $count{mem}, $unit, $count{dimm};
4575 }
4576 else {
4577 print ', not checking memory';
4578 }
4579
669797e1 4580 if ($check{storage}) {
4581 printf ', %d logical drives, %d physical drives',
4582 $count{vdisk}, $count{pdisk};
4583 }
4584 else {
4585 print ', not checking storage';
4586 }
4587
4588 if ($opt{okinfo} >= 1) {
4589 print $linebreak;
4590 printf q{----- BIOS='%s %s'}, $sysinfo{bios}, $sysinfo{biosdate};
4591
4592 if ($sysinfo{rac}) {
4593 printf q{, %s='%s'}, $sysinfo{rac_name}, $sysinfo{rac_fw};
4594 }
4595 if ($sysinfo{bmc}) {
4596 printf q{, BMC='%s'}, $sysinfo{bmc_fw};
4597 }
4598 }
4599
4600 if ($opt{okinfo} >= 2) {
4601 if ($check{storage}) {
4602 my @storageprint = ();
4603 foreach my $id (sort keys %{ $sysinfo{controller} }) {
4604 chomp $sysinfo{controller}{$id}{driver};
956cf4d1 4605 my $msg = sprintf q{----- Ctrl %s [%s]: Fw='%s', Dr='%s'},
669797e1 4606 $sysinfo{controller}{$id}{id}, $sysinfo{controller}{$id}{name},
4607 $sysinfo{controller}{$id}{firmware}, $sysinfo{controller}{$id}{driver};
956cf4d1 4608 if (defined $sysinfo{controller}{$id}{storport}) {
4609 $msg .= sprintf q{, Storport: '%s'}, $sysinfo{controller}{$id}{storport};
4610 }
4611 push @storageprint, $msg;
669797e1 4612 }
4613 foreach my $id (sort keys %{ $sysinfo{enclosure} }) {
956cf4d1 4614 push @storageprint, sprintf q{----- Encl %s [%s]: Fw='%s'},
669797e1 4615 $sysinfo{enclosure}{$id}->{id}, $sysinfo{enclosure}{$id}->{name},
4616 $sysinfo{enclosure}{$id}->{firmware};
4617 }
4618
4619 # print stuff
4620 foreach my $line (@storageprint) {
4621 print $linebreak, $line;
4622 }
4623 }
4624 }
4625
4626 if ($opt{okinfo} >= 3) {
4627 print "$linebreak----- OpenManage Server Administrator (OMSA) version: '$sysinfo{om}'";
4628 }
4629
4630}
4631else {
4632 if ($opt{extinfo}) {
4633 print $linebreak;
4634 if (defined $opt{htmlinfo}) {
4635 printf '------ SYSTEM: <a href="%s">%s</a>, SN: <a href="%s">%s</a>',
4636 documentation_url($sysinfo{model}), $sysinfo{model},
4637 warranty_url($sysinfo{serial}), $sysinfo{serial};
4638 }
4639 else {
4640 printf '------ SYSTEM: %s, SN: %s',
4641 $sysinfo{model}, $sysinfo{serial};
4642 }
4643 }
4644 if (defined $opt{postmsg}) {
4645 my $post = undef;
4646 if (-f $opt{postmsg}) {
4647 open my $POST, '<', $opt{postmsg}
4648 or ( print $linebreak
4649 and print "ERROR: Couldn't open post message file $opt{postmsg}: $!\n"
4650 and exit $E_UNKNOWN );
4651 $post = <$POST>;
4652 close $POST;
4653 chomp $post;
4654 }
4655 else {
4656 $post = $opt{postmsg};
4657 }
4658 if (defined $post) {
4659 print $linebreak;
4660 $post =~ s{[%]s}{$sysinfo{serial}}gxms;
4661 $post =~ s{[%]m}{$sysinfo{model}}gxms;
4662 $post =~ s{[%]b}{$sysinfo{bios}}gxms;
4663 $post =~ s{[%]d}{$sysinfo{biosdate}}gxms;
4664 $post =~ s{[%]o}{$sysinfo{osname}}gxms;
4665 $post =~ s{[%]r}{$sysinfo{osver}}gxms;
4666 $post =~ s{[%]p}{$count{pdisk}}gxms;
4667 $post =~ s{[%]l}{$count{vdisk}}gxms;
4668 $post =~ s{[%]n}{$linebreak}gxms;
4669 $post =~ s{[%]{2}}{%}gxms;
4670 print $post;
4671 }
4672 }
4673}
4674
7c03958b 4675# Reset the WARN signal
4676$SIG{__WARN__} = 'DEFAULT';
4677
cbbc270f 4678# Print any perl warnings that have occured
4679if (@perl_warnings) {
4680 foreach (@perl_warnings) {
4681 chop @$_;
4682 print "${linebreak}INTERNAL ERROR: @$_";
4683 }
4684 $exit_code = $E_UNKNOWN;
4685}
4686
669797e1 4687# Print performance data
48aeec0b 4688if (defined $opt{perfdata} && !$opt{debug} && @perfdata) {
669797e1 4689 my $lb = $opt{perfdata} eq 'multiline' ? "\n" : q{ }; # line break for perfdata
4690 print q{|};
4691
48aeec0b 4692 # Sort routine for performance data
4693 sub perfsort {
4694 my %order = ( fan => 0, pwr => 1, tem => 2, enc => 3, );
4695 return ($order{(substr $a->{label}, 0, 3)} cmp $order{(substr $b->{label}, 0, 3)}) ||
4696 $a->{label} cmp $b->{label};
669797e1 4697 }
4698
48aeec0b 4699 # Print performance data sorted
4700 my $type = $opt{perfdata} eq 'minimal' ? 'mini' : 'label';
60fd6896 4701 print join $lb, map { "$_->{$type}=$_->{value};$_->{warn};$_->{crit}" } sort perfsort @perfdata;
669797e1 4702}
e133d101 4703
4704# Print a linebreak at the end
669797e1 4705print "\n" if !$opt{debug};
4706
4707# Exit with proper exit code
4708exit $exit_code;