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