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