From 2cbbf5c96433bd7d89e8e8fc2873413491dc4160 Mon Sep 17 00:00:00 2001 From: Martin Grubhofer Date: Thu, 23 May 2013 23:36:17 +0200 Subject: [PATCH] - beta version ready for intense testing --- check_lsi_raid | 787 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 773 insertions(+), 14 deletions(-) diff --git a/check_lsi_raid b/check_lsi_raid index 55712ee..523f7cc 100644 --- a/check_lsi_raid +++ b/check_lsi_raid @@ -1,6 +1,7 @@ #!/usr/bin/perl -w # ====================================================================================== -# $Id$ +# $Id: check_lsi_raid 25 2013-05-02 19:36:30Z s1110239032 $ +# check_lsi_raid: Nagios/Icinga plugin to check LSI Raid Controller status # -------------------------------------------------------------------------------------- # Created as part of a semester project at the University of Applied Sciences Hagenberg # (http://www.fh-ooe.at/en/hagenberg-campus/) @@ -10,17 +11,775 @@ # Scheipner Alexander (s1110239032@students.fh-hagenberg.at) # Werner Sebastian (s1110239038@students.fh-hagenberg.at) # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +# details. +# +# You should have received a copy of the GNU General Public License along with +# this program; if not, see . # ====================================================================================== +use strict; +use warnings; +use Getopt::Long qw(:config no_ignore_case); +use Data::Dumper; +use feature qw/switch/; #später durch nachfolgende Zeile ersetzen, da wir noch mit Perl 5 arbeiten! +#use Switch 'Perl6'; + +our $VERBOSITY = 0; +our $VERSION = "0.1"; +our $NAME = "check_lsi_raid: Nagios/Icinga plugin to check LSI Raid Controller status"; + +use constant { + STATE_OK => 0, + STATE_WARNING => 1, + STATE_CRITICAL => 2, + STATE_UNKNOWN => 3, + }; + +# Always return the highest state level +sub getExitState { + my $returnState = STATE_OK; + # check if no state is NULL + if (!defined($_[0]) || !defined($_[1])) { + $returnState = STATE_UNKNOWN; + } + # check previous state + if ($_[0] > $returnState) { + $returnState = $_[0]; + } + # check upcoming state + if ($_[1] > $returnState) { + $returnState = $_[1]; + } + return $returnState; +} + +# Explains the Usage of the plugin, also which options take which values +sub displayUsage { + print "Usage: \n"; + print " [ -h | --help ]\n Display this help page\n"; + print " [ -v | -vv | -vvv | --verbose ]\n Sets the verbosity level.\n no -v is the normal single line output for Nagios/Icinga\n -v is a more detailed version but still usable in Nagios.\n -vv is a multiline output for debugging configuration errors or more detailed information.\n -vvv is for plugin problem diagnosis.\n For further information please visit: http://nagiosplug.sourceforge.net/developer-guidelines.html#AEN39\n"; + print " [ -V --version ]\n Displays the Version of the tk-lsi-plugin and the version of StorCLI\n"; + print " [ -C | --controller ]\n Specifies a Controller number, defaults to 1\n"; + print " [ -EID | --enclosure ]\n Specifies one or more Enclosures, defaults to all\n Takes either an integer as additional argument (>=0) or a comma seperated list(0,1,2,3,...)\n"; + print " [ -LD | --logicaldevice ]\n Specifies one or more Logical Devices, defaults to all\n Takes either an integer as additional argument (>=0) or a comma seperated list(0,1,2,3,...)\n"; + print " [ -PD | --physicaldevice ]\n Specifies one or more Physical Devices, defaults to all\n Takes either an integer as additional argument (>=0) or a comma seperated list(0,1,2,3,...)\n"; + print " [ -Tw | --temperature-warn ]\n Specifies the RAID-Controller temperature warning range, default is 40C or more\n"; + print " [ -Tc | --temperature-critical ]\n Specifies the RAID-Controller temperature critical error range, default is 50C or more\n"; + print " [ -PDTw | --physicaldevicetemperature-warn ]\n Specifies the disk temperature warning range, default is 40C or more\n"; + print " [ -PDTc | --physicaldevicetemperature-critical ]\n Specifies the disk temperature critical error range, default is 45C or more\n"; + print " [ -p | --path ]\n Specifies the path to StorCLI, default is /usr/bin/storcli or C:\\Programme\\...\\storcli.exe\n"; + print " [ -b <0/1> | BBU <0/1> ]\n Boolean Value which specifies if an Battery Backup Unit is Present (1 is present/0 is not Present). Default is 1\n This option is only needed if you have an LSI-Raid Controller without a Battery Backup Unit."; +} + +# Displays a short Help text for the user +# TODO: FH Copyright, ADD URL and Mailing List +sub displayHelp { + print $NAME . " Version: " . $VERSION ."\n"; + print "Copyright (C) 2009-2013 Thomas-Krenn.AG\n"; + #FH Copyright + print "Current updates available at http://www.thomas-krenn.com/en/oss//\n"; + print "This Nagios/Icinga Plugin checks LSI RAID-Controllers for Controller, \nPhysical-Device and Logical Device warnings and errors.\n"; + print "In order for this plugin to work properly you need to add the \nnagios-user to your sudoers file (or create a new one in /etc/sudoers.d/)\n"; + displayUsage(); + print "Further information about this plugin can be found at +http://www.thomas-krenn.com/en/oss/ +Send email to the -plugin-user mailing list if you have questions regarding +use of this software, to submit patches, or suggest improvements. +The mailing list is available at http://lists.thomas-krenn.com/"; + exit(STATE_UNKNOWN); +} + +# Prints the Name, Version of the Plugin +# Also Prints the version of StorCLI +sub displayVersion { + my $sudo = $_[0]; + my $storcli = $_[1]; + my @storcliVersion = `$sudo $storcli -v`; + print $NAME . "\nVersion: ". $VERSION . "\n\n"; + foreach my $line (@storcliVersion){ + if($line =~ /^\s*Storage/) { + $line =~ s/^\s+|\s+$//g; + print $line; + } + } +} + +# Returns information about: +# - Controller status +# - Temperature, ... +sub getControllerStatus { + my $sudo = $_[0]; + my $storcli = $_[1]; + my $controller = $_[2]; + my @temperature_w = @{($_[3])}; + my @temperature_c = @{($_[4])}; + + my $command = "$sudo $storcli /c$controller show all"; + my $status = 0; # Return Status + my $statusMessage = ''; # Return String + + my @output = `$command`; + #my @output = ("", "Status = Success\n"); + + #command successful? + if($output[1] eq "Status = Success\n") { + foreach my $line (@output) { + #/^([a-zA-Z0-9]*)/ erstes wort + #/(\s+[a-zA-Z0-9]*)/ zweites wort + #/([a-zA-Z0-9]*)$/ letztes wort + my $first; + my $last; + if($line =~ /^([a-zA-Z0-9]*)/) { + $first = $1; + if($line =~ /([a-zA-Z0-9]*)$/) { + $last = $1; + given($first) { + when("Controller") { + if($line =~ /(\s+[a-zA-Z0-9]*)/) { + given($1) { + when("Status") { + if($last ne "OK") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "Ctrl. status not optimal, "; + } + } + when("must") { + if($last ne "No") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "Ctrl. needs reboot, "; + } + } + when("has") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "Ctrl. booted in safe mode, "; + } + } + } + } + } + when("Memory") { + if($line =~ /(\s+[a-zA-Z0-9]*)/) { + given($1) { + when("Correctable") { + if($last ne "0") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "Memory correctable errors detected, "; + } + } + when("Uncorrectable") { + if($last ne "0") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "Memory uncorrectable errors detected, "; + } + } + } + } + } + when("Failed") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "Failed to get lock key on bootup, "; + } + } + when("A") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "A rollback operation is in progress, "; + } + } + } + } + } + } + return ($status, $statusMessage); + } else { + print "Invalid StorCLI command!"; + exit(STATE_UNKNOWN); + } +} + +sub getLogicalDeviceStatus { + my $sudo = $_[0]; + my $storcli = $_[1]; + my $controller = $_[2]; + my @logDevices = @{($_[3])}; + + my $command = "$sudo $storcli /c$controller "; + my $status = 0; # Return Status + my $statusMessage = ''; # Return String + + given(scalar(@logDevices)) { + when(0) { $command .= "/vall"; } + when(1) { $command .= "/v$logDevices[0]"; } + default { $command .= "/v".join(",", @logDevices); } + } + $command .= " show all"; + my @output = `$command`; + #my @output = ("", "Status = Success\n", "/c0/v1 :", "1/1 RAID5 Optl RW Yes RWBD - 930.0 GB"); + + #command successful? + if($output[1] eq "Status = Success\n") { + my $output_dev = -1; + my $flag = -1; + foreach my $line (@output) { + #/^([a-zA-Z0-9]*)/ erstes wort + #/(\s+[a-zA-Z0-9]*)/ zweites wort + #/([a-zA-Z0-9]*)$/ letztes wort + if($flag > 0) { + $flag--; + } elsif($line =~ /^\/c$controller\/v([0-9]*)/) { + $output_dev = $1; + $flag = 6; + } + if($flag eq 0) { + $flag = -1; + my @values = split(' ',$line); + if($values[2] ne "Optl") { + if($values[4] eq "Yes") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Virtual disk state not optimal, "; } + if ($VERBOSITY >= 1) {$statusMessage .= "Virtual disk $output_dev state not optimal ($values[2]), "; } + } else { + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "Virtual disk not consistant, "; } + if ($VERBOSITY >= 1) {$statusMessage .= "Virtual disk $output_dev is not consistant (state $values[2]), "; } + } + } + } + } + return ($status, $statusMessage); + } else { + print "Invalid StorCLI command!"; + exit(STATE_UNKNOWN); + } +} + +# Returns information about: +# - Physical device status +sub getPhysDeviceStatus { + my $sudo = $_[0]; + my $storcli = $_[1]; + my $controller = $_[2]; + my @enclosures = @{($_[3])}; + my @physDevices = @{($_[4])}; + my @physicalDeviceTemperature_w = @{($_[5])}; + my @physicalDeviceTemperature_c = @{($_[6])}; + my $action = $_[7]; + + my $command = "$sudo $storcli /c$controller"; + my $status = 0; + my $statusMessage = ''; + + given(scalar(@enclosures)) { + when(0) { $command .= "/eall"; } + when(1) { $command .= "/e$enclosures[0]"; } + default { $command .= "/e".join(",", @enclosures); } + } + given(scalar(@physDevices)) { + when(0) { $command .= "/sall"; } + when(1) { $command .= "/s$physDevices[0]"; } + default { $command .= "/s".join(",", @physDevices); } + } + $command .= " show $action"; + my @output = `$command`; + #my @output = ("", "Status = Success\n", "Drive /c0/e252/s0 State :", "=======================", "Shield Counter = 0", "Media Error Count = 0", "Other Error Count = 0", "Drive Temperature = 31C (87.80 F)", "Predictive Failure Count = 0", "S.M.A.R.T alert flagged by drive = No"); + + if($output[1] eq "Status = Success\n") { + if($action eq "all") { + my $output_enc = -1; + my $output_dev = -1; + foreach my $line (@output) { + my $first; + my $last; + if($line =~ /^Drive\s\/c$controller\/e([0-9]*)\/s([0-9]*)\sState\s\:/) { + $output_enc = $1; + $output_dev = $2; + } + if(($output_enc ne -1) && ($output_dev ne -1)) { + if($line =~ /^([a-zA-Z0-9\.]*)/) { + $first = $1; + if($line =~ /([a-zA-Z0-9]*)$/) { + $last = $1; + given($first) { + when("Shield") { + if($last ne "0") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Shield counter (phys. drive) not 0, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: shield counter not 0, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: shield counter not 0, "; } + } + } + when("Media") { + if($last ne "0") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Media error count (phys. drive) not 0, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: media error count not 0, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: media error count not 0, "; } + } + } + when("Other") { + if($last ne "0") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Other error count (phys. drive) not 0, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: media error count not 0, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: media error count not 0, "; } + } + } + when("Drive") { + if($line =~ /^Drive\sTemperature\s=\s*(-?[0-9]*)C/) { + my $temp = $1; + my $crit = 0; + # check for warn range + if($physicalDeviceTemperature_w[0] eq "in") { + if(($temp >= $physicalDeviceTemperature_w[1]) && ($temp <= $physicalDeviceTemperature_w[2])) { + # is in warn range, so also check if in critical error range + if($physicalDeviceTemperature_c[0] eq "in") { + if(($temp >= $physicalDeviceTemperature_c[1]) && ($temp <= $physicalDeviceTemperature_c[2])) { + # critical error + $crit = 1; + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature critical, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } else { + if(($temp < $physicalDeviceTemperature_c[1]) || ($temp > $physicalDeviceTemperature_c[2])) { + # critical error + $crit = 1; + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature critical, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } + if($crit eq 0) { # only warn if not already given a critical error + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature warning, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } + } else { + if(($temp < $physicalDeviceTemperature_w[1]) || ($temp > $physicalDeviceTemperature_w[2])) { + # is in warn range, so also check if in critical error range + if($physicalDeviceTemperature_c[0] eq "in") { + if(($temp >= $physicalDeviceTemperature_c[1]) && ($temp <= $physicalDeviceTemperature_c[2])) { + # critical error + $crit = 1; + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature critical, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } else { + if(($temp < $physicalDeviceTemperature_c[1]) || ($temp > $physicalDeviceTemperature_c[2])) { + # critical error + $crit = 1; + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature critical, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } + if($crit eq 0) { # only warn if not already given a critical error + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive temperature warning, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: temperature is ${temp}C, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: temperature is ${temp}C, "; } + } + } + } + } + } + when("Predictive") { + if($last ne "0") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive Predictive Fail Count not 0, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: Predictive Fail Count not 0, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: Predictive Fail Count not 0, "; } + } + } + when("S.M.A.R.T") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "S.M.A.R.T alert flagged by drive, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: S.M.A.R.T alert flagged, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: S.M.A.R.T alert flagged, "; } + } + } + } + } + } + } + } + } else { + my $output_enc = -1; + my $output_dev = -1; + foreach my $line (@output) { + if($line =~ /^\/c$controller\/e([0-9]*)\/s([0-9]*)/) { + $output_enc = $1; + $output_dev = $2; + } + if(($output_enc ne -1) && ($output_dev ne -1)) { + if($line =~ /^\/c$controller$output_enc$output_dev\s*([\-]{1}|[0-9\%]*)\s*([\w\s]*)$/) { # gets status and percentage + if($2 ne "Not in progress") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "Phys. drive: $action in progress, "; } + if ($VERBOSITY == 1) {$statusMessage .= "Physical drive $output_dev: $action in progress, "; } + if ($VERBOSITY >= 2) {$statusMessage .= "Physical drive $output_dev in enclosure $output_enc: $action in progress (percentage: $1), "; } + } + } + } + } + } + return ($status, $statusMessage); + } else { + print "Invalid StorCLI command!"; + exit(STATE_UNKNOWN); + } +} + +# Returns information about: +# - Battery Backup Unit status +# - Temperature, Battery status, voltage +sub getBBUStatus { + my $sudo = $_[0]; + my $storcli = $_[1]; + my $controller = $_[2]; + + my $command = "$sudo $storcli /c$controller/bbu show status"; + my $status = 0; + my $statusMessage = ''; + + my @output = `$command`; + #my @output = ("", "Status = Success\n"); + + if($output[1] eq "Status = Success\n") { + my $blockid = 0; + foreach my $line (@output) { + my $first; + my $last; + if($line =~ /^([a-zA-Z0-9]*)/) { + $first = $1; + if($first eq 'BBU_Info' || $first eq 'BBU_Firmware_Status' || $first eq 'GasGaugeStatus') { + $blockid++; + } + if($line =~ /([a-zA-Z0-9]*)$/) { + $last = $1; + # Check BBU_Info block + if ($blockid eq 1) { + given($first) { + when("Battery") { + if($line =~ /(\s+[a-zA-Z0-9]*)/) { + given($1) { + when("State") { + if($last ne "Optimal") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "BBU state not optimal, "; + } + } + } + } + } + } + } + # Check BBU_Firmware_Status + if ($blockid eq 2) { + given($first) { + when("Temperature") { + if($last ne "OK") { + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "BBU temp. critical, "; } + if ($VERBOSITY >= 1) {$statusMessage .= "BBU Temperature critical, "; } + } + } + when("Battery") { + if($line =~ /(\s+[a-zA-Z0-9]*)/) { + given($1) { + when("State") { + if($last ne "Optimal") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "BBU state not optimal, "; + } + } + when("Pack") { + if($last ne "No") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "BBU pack missing, "; + } + } + when("Replacement") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "BBU replacement required, "; + } + } + } + } + } + when("Voltage") { + if($last ne "OK") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "BBU voltage not ok, "; + } + } + when("Learn") { + if($last ne "OK") { + $status = getExitState($status, STATE_WARNING); + $statusMessage .= "BBU learn cycle status not ok, "; + } + } + when("I2C") { + if($last ne "No") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "BBU I2C errors, "; + } + } + when("Remaining") { + if($last ne "No") { + $status = getExitState($status, STATE_WARNING); + if ($VERBOSITY == 0) {$statusMessage .= "BBU capacity low, "; } + if ($VERBOSITY >= 1) {$statusMessage .= "BBU remaining capacity is low, "; } + } + } + } + } + # Check GasGaugeStatus + if ($blockid eq 3) { + given($first) { + when("Over") { + if($line =~ /(\s+[a-zA-Z0-9]*)/) { + if($1 eq "Temperature") { + if($last ne "No") { + $status = getExitState($status, STATE_CRITICAL); + if ($VERBOSITY == 0) {$statusMessage .= "BBU temp. critical, "; } + if ($VERBOSITY >= 1) {$statusMessage .= "BBU Temperature critical, "; } + } + } elsif($1 eq "Charged") { + if($last ne "No") { + $status = getExitState($status, STATE_CRITICAL); + $statusMessage .= "BBU over charged, "; + } + } + } + } + } + } + } + } + } + return ($status, $statusMessage); + } else { + print "Invalid StorCLI command!"; + exit(STATE_UNKNOWN); + } +} + +# Nagios development guidelines: temperature threshold sheme +# http://nagiosplug.sourceforge.net/developer-guidelines.html#THRESHOLDFORMAT +# Returns a temperature range (array) in or out which a temperature should be +# Array content: ("in" or "out", range from, range to) +# Example ranges: +# Generate an alert if x... +# -Tw 10 < 0 or > 10, (outside the range of {0 .. 10}) +# -Tw 10: < 10, (outside {10 .. inf}) +# -Tw ~:10 > 10, (outside the range of {-inf .. 10}) +# -Tw 10:20 < 10 or > 20, (outside the range of {10 .. 20}) +# -Tw @10:20 >= 10 and <= 20, (inside the range of {10 .. 20}) +sub getThresholds { + my @thresholds = @{($_[0])}; + my $default = $_[1]; + + if(scalar(@thresholds) eq 0) { + return @thresholds = ("out", -273, $default); + } + if(substr($thresholds[0], 0, 1) eq "@") { + if($thresholds[0] =~ /^\@([0-9]*)\:([0-9]*)$/) { + @thresholds = ("in", $1, $2); + } else { + print "Invalid temperature parameter!"; + exit(STATE_UNKNOWN); + } + } elsif(substr($thresholds[0], 0, 1) eq "~") { + if($thresholds[0] =~ /^\~\:([0-9]*)$/) { + @thresholds = ("out", -273, $1); + } else { + print "Invalid temperature parameter!"; + exit(STATE_UNKNOWN); + } + } elsif(index($thresholds[0], ":") ne -1) { + if($thresholds[0] =~ /^([0-9]*)\:([0-9]{1,3})$/) { + @thresholds = ("out", $1, $2); + } elsif($thresholds[0] =~ /^([0-9]*)\:$/) { + @thresholds = ("in", -273, ($1 - 1)); + } else { + print "Invalid temperature parameter!"; + exit(STATE_UNKNOWN); + } + } else { + @thresholds = ("out", 0, $thresholds[0]); + } + if(($thresholds[1] =~ /^(-?[0-9]*)$/) && ($thresholds[2] =~ /^(-?[0-9]*)$/)) { + return @thresholds; + } else { + print "Invalid temperature parameter!"; + exit(STATE_UNKNOWN); + } +} + +MAIN: { + my $sudo = ''; + my $storcli = ''; + my $controller = 0; + my @enclosures; + my @logDevices; + my @physDevices; + my @temperature_w; + my @temperature_c; + my @physicalDeviceTemperature_w; + my @physicalDeviceTemperature_c; + my $bbu = 1; + my $platform = $^O; + my $statusMessage = ''; + + if ( !(GetOptions( + 'h|help' => sub {displayHelp();}, + 'v|verbose' => sub {$VERBOSITY = 1 }, + 'vv' => sub {$VERBOSITY = 2}, + 'vvv' => sub {$VERBOSITY = 3}, + 'V|version' => sub {displayVersion($sudo, $storcli);}, + 'C|controller=i' => \$controller, + 'EID|enclosure=s' => \@enclosures, + 'LD|logicaldevice=s' => \@logDevices, + 'PD|physicaldevice=s' => \@physDevices, + 'Tw|temperature-warn=s' => \@temperature_w, + 'Tc|temperature-critical=s' => \@temperature_c, + 'PDTw|physicaldevicetemperature-warn=s' => \@physicalDeviceTemperature_w, + 'PDTc|physicaldevicetemperature-critical=s' => \@physicalDeviceTemperature_c, + 'p|path=s' => \$storcli, + 'b|BBU=i' => \$bbu + ))) { + print $NAME . " Version: " . $VERSION ."\n"; + displayUsage(); + exit(STATE_UNKNOWN); + } + + # Check platform + if ($platform eq 'linux') { + chomp($sudo = `which sudo`); + if ($storcli eq '') { + if ( -e '/opt/MegaRAID/storcli/storcli64') { + $storcli = '/opt/MegaRAID/storcli/storcli64' + } + elsif ( -e '/opt/MegaRAID/storcli/storcli') { + $storcli = '/opt/MegaRAID/storcli/storcli' + } + else { + chomp($storcli= `which storcli`); + } + } + unless ( -e $storcli && -x $sudo ) { + print "No sudo rights or StorCLI not found!\n"; + exit(STATE_UNKNOWN); + } + } else { + eval('use File::Which'); + $sudo = ''; + if ($storcli eq '') { + if (defined(which( 'storcli64.exe' )) ) { + $storcli = which( 'storcli64.exe' ); + } elsif (defined(which( 'storcli.exe' ))) { + $storcli = which( 'storcli.exe' ); + } + } + unless ( -e $storcli ) { + print "StorCLI not found!\n"; + exit(STATE_UNKNOWN); + } + } + + # Input validation + my @controllerVersion = `$sudo $storcli /c$controller show all`; + #my @controllerVersion = ("", "", "Description = None\n"); + if($controllerVersion[2] eq "Description = Controller $controller not found\n") { + print "Invalid controller number, device not found!"; + exit(STATE_UNKNOWN); + } + if(($bbu ne "1") && ($bbu ne "0")) { + print "Invalid BBU parameter, must be 0 or 1!"; + exit(STATE_UNKNOWN); + } else { + if($bbu eq "1") { + my @bbucheck = `$sudo $storcli /c$controller/bbu show`; + my $flag = 0; + my $found = 0; + foreach my $line (@bbucheck) { + if($flag > 0) { + $flag--; + } elsif($line =~ /^([a-zA-Z]*)/) { + if($1 eq "Model") { + $flag = 2; + } + } + if($flag eq 0) { + my @values = split(' ', $line); + if(defined($values[6])) { + if($values[6] =~ /^([0-9]{4}\/[0-9]{2}\/[0-9]{2})$/) { + $found = 1; + } + } + } + } + if($found eq 0) { + print "No battery backup unit found for controller $controller!"; + exit(STATE_UNKNOWN); + } + } + } + @enclosures = split(/,/,join(',', @enclosures)); + @logDevices = split(/,/,join(',', @logDevices)); + @physDevices = split(/,/,join(',', @physDevices)); + @temperature_w = getThresholds(\@temperature_w, 40); # 40 = default value + @temperature_c = getThresholds(\@temperature_c, 50); + @physicalDeviceTemperature_w = getThresholds(\@physicalDeviceTemperature_w, 40); + @physicalDeviceTemperature_c = getThresholds(\@physicalDeviceTemperature_c, 45); + + # Set exit status + my $exitstatus = 0; + my $newexitstatus = 0; + my $newstatusMessage = ''; + ($newexitstatus, $statusMessage) = getControllerStatus($sudo, $storcli, $controller, \@temperature_w, \@temperature_c); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + ($newexitstatus, $statusMessage) = getLogicalDeviceStatus($sudo, $storcli, $controller, \@logDevices); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + ($newexitstatus, $statusMessage) = getPhysDeviceStatus($sudo, $storcli, $controller, \@enclosures, \@physDevices, \@physicalDeviceTemperature_w, \@physicalDeviceTemperature_c, "initialization"); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + ($newexitstatus, $statusMessage) = getPhysDeviceStatus($sudo, $storcli, $controller, \@enclosures, \@physDevices, \@physicalDeviceTemperature_w, \@physicalDeviceTemperature_c, "rebuild"); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + ($newexitstatus, $statusMessage) = getPhysDeviceStatus($sudo, $storcli, $controller, \@enclosures, \@physDevices, \@physicalDeviceTemperature_w, \@physicalDeviceTemperature_c, "all"); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + if($bbu) { + ($newexitstatus, $statusMessage) = getBBUStatus($sudo, $storcli, $controller); + $newstatusMessage .= $statusMessage; + $exitstatus = getExitState($newexitstatus, $exitstatus); + } + given($exitstatus) { + when(0) { print "LSIRAID OK (Ctrl #$controller)\n"; } + when(1) { chop($newstatusMessage); chop($newstatusMessage); print "LSIRAID WARNING (Ctrl #$controller): [$newstatusMessage]\n"; } + when(2) { chop($newstatusMessage); chop($newstatusMessage); print "LSIRAID CRITICAL (Ctrl #$controller): [$newstatusMessage]\n"; } + } + exit($exitstatus); +} \ No newline at end of file