You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1160 lines
40 KiB

  1. #!/usr/bin/perl
  2. # check_ipmi_sensor: Nagios/Icinga plugin to check IPMI sensors
  3. #
  4. # Copyright (C) 2009-2019 Thomas-Krenn.AG,
  5. # additional contributors see changelog.txt
  6. #
  7. # This program is free software; you can redistribute it and/or modify it under
  8. # the terms of the GNU General Public License as published by the Free Software
  9. # Foundation; either version 3 of the License, or (at your option) any later
  10. # version.
  11. #
  12. # This program is distributed in the hope that it will be useful, but WITHOUT
  13. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  14. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  15. # details.
  16. #
  17. # You should have received a copy of the GNU General Public License along with
  18. # this program; if not, see <http://www.gnu.org/licenses/>.
  19. #
  20. ################################################################################
  21. # The following guides provide helpful information if you want to extend this
  22. # script:
  23. # http://tldp.org/LDP/abs/html/ (Advanced Bash-Scripting Guide)
  24. # http://www.gnu.org/software/gawk/manual/ (Gawk: Effective AWK Programming)
  25. # http://de.wikibooks.org/wiki/Awk (awk Wikibook, in German)
  26. # http://nagios.sourceforge.net/docs/3_0/customobjectvars.html (hints on
  27. # custom object variables)
  28. # http://nagiosplug.sourceforge.net/developer-guidelines.html (plug-in
  29. # development guidelines)
  30. # http://nagios.sourceforge.net/docs/3_0/pluginapi.html (plugin API)
  31. ################################################################################
  32. use strict;
  33. use warnings;
  34. use Getopt::Long qw(:config no_ignore_case);
  35. use IPC::Run qw( run ); #interact with processes
  36. ################################################################################
  37. # set text variables
  38. our $check_ipmi_sensor_version = "3.14";
  39. sub get_version{
  40. return <<EOT;
  41. check_ipmi_sensor version $check_ipmi_sensor_version
  42. Copyright (C) 2009-2019 Thomas-Krenn.AG
  43. Current updates at https://github.com/thomas-krenn/check_ipmi_sensor_v3.git
  44. EOT
  45. }
  46. sub get_usage{
  47. return <<EOT;
  48. Usage:
  49. check_ipmi_sensor -H <hostname>
  50. [-f <FreeIPMI config file> | -U <username> -P <password> -L <privilege level>]
  51. [-O <FreeIPMI options>] [-b] [-T <sensor type(s)>] [-ST <SEL sensor type(s)>]
  52. [-x <sensor id>] [-xT <sensor type(s)>] [-xST <SEL sensor type(s)]
  53. [-i <sensor id>] [-o zenoss] [-D <protocol LAN version>] [-h] [-V]
  54. [-fc <num_fans>] [--fru] [--assettag] [--board] [--nosel] [--selonly]
  55. [--seltail <count>] [-sx|--selexclude <sel exclude file>]
  56. [-xx|--sexclude <exclude file>] [-us|--unify-sensors <unify file>] [--nosudo]
  57. [--nothresholds] [--nodcmi] [--noentityabsent]
  58. [-s <ipmi-sensor output file>] [-h] [-V]
  59. [-v|-vv|-vvv]
  60. EOT
  61. }
  62. sub get_help{
  63. return <<EOT;
  64. [-H <hostname>]
  65. hostname or IP of the IPMI interface.
  66. For \"-H localhost\" or if no host is specified (local computer) the
  67. Nagios/Icinga user must be allowed to run
  68. ipmimonitoring/ipmi-sensors/ipmi-sel/[ipmi-fru] with root privileges
  69. or via sudo (ipmimonitoring/ipmi-sensors/ipmi-sel/[ipmi-fru] must be
  70. able to access the IPMI devices via the IPMI system interface).
  71. [-f <FreeIPMI config file>]
  72. path to the FreeIPMI configuration file.
  73. Only neccessary for communication via network.
  74. Not neccessary for access via IPMI system interface (\"-H localhost\").
  75. It should contain IPMI username, IPMI password, and IPMI privilege-level,
  76. for example:
  77. username monitoring
  78. password yourpassword
  79. privilege-level user
  80. As alternative you can use -U/-P/-L instead (see below).
  81. [-U <username> -P <password> -L <privilege level>]
  82. IPMI username, IPMI password and IPMI privilege level, provided as
  83. parameters and not by a FreeIPMI configuration file. Useful for RHEL/
  84. Centos 5.* with FreeIPMI 0.5.1 (this elder FreeIPMI version does not
  85. support config files).
  86. Warning: with this method the password is visible in the process list.
  87. So whenever possible use a FreeIPMI confiugration file instead.
  88. [-O <FreeIPMI options>]
  89. additional options for FreeIPMI. Useful for RHEL/CentOS 5.* with
  90. FreeIPMI 0.5.1 (this elder FreeIPMI version does not support config
  91. files).
  92. [--seloptions <FreeIPMI SEL options>]
  93. additional options for FreeIPMI SEL command. Useful for passing extra
  94. options to the ipmi-sel call, e.g. --assume-system-event-records:
  95. --seloptions '--assume-system-event-records'
  96. [-b]
  97. backward compatibility mode for FreeIPMI 0.5.* (this omits the FreeIPMI
  98. caching options --quiet-cache and --sdr-cache-recreate)
  99. [-T <sensor type(s)>]
  100. limit sensors to query based on IPMI sensor type.
  101. Examples for IPMI sensor types are 'Fan', 'Temperature', 'Voltage', ...
  102. See the output of the FreeIPMI command 'ipmi-sensors -L' and chapter
  103. '42.2 Sensor Type Codes and Data' of the IPMI 2.0 spec for a full list
  104. of possible sensor types. You can also find the full list of possible
  105. sensor types at https://www.thomas-krenn.com/en/wiki/IPMI_Sensor_Types
  106. The available types depend on your particular server and the available
  107. sensors there.
  108. Multiple sensor types can be specified as a comma-separated list.
  109. [-ST <SEL sensor type(s)>]
  110. limit SEL entries to specific types, run 'ipmi-sel -L' for a list of
  111. types. All sensors are populated to the SEL and per default all sensor
  112. types are monitored. E.g. to limit the sensor SEL types to Memory and
  113. Processsor use -ST 'Memory,Processor'.
  114. [-x <sensor id>]
  115. exclude sensor matching <sensor id>. Useful for cases when unused
  116. sensors cannot be deleted from SDR and are reported in a non-OK state.
  117. Option can be specified multiple times. The <sensor id> is a numeric
  118. value (sensor names are not used as some servers have multiple sensors
  119. with the same name). Use -vvv option to query the <sensor ids>.
  120. [-xT <sensor type(s)>]
  121. exclude sensors based on IPMI sensor type.
  122. Multiple sensor types can be specified as a comma-separated list.
  123. [-xST <SEL sensor type(s)]
  124. exclude SEL entries of specific sensor types.
  125. Multiple sensor types can be specified as a comma-separated list.
  126. [-i <sensor id>]
  127. include only sensor matching <sensor id>. Useful for cases when only
  128. specific sensors should be monitored. Be aware that only for the
  129. specified sensor errors/warnings are generated. Use -vvv option to query
  130. the <sensor ids>.
  131. [-v|-vv|-vvv]
  132. be verbose
  133. (no -v) .. single line output
  134. -v ..... single line output with additional details for warnings
  135. -vv ..... multi line output, also with additional details for warnings
  136. -vvv ..... debugging output, followed by normal multi line output
  137. [-o]
  138. change output format. Useful for using the plugin with other monitoring
  139. software than Nagios or Icinga.
  140. -o zenoss .. create ZENOSS compatible formatted output (output with
  141. underscores instead of whitespaces and no single quotes)
  142. [-D]
  143. change the protocol LAN version. Normally LAN_2_0 is used as protocol
  144. version if not overwritten with this option. Use 'default' here if you
  145. don't want to use LAN_2_0.
  146. [-fc <num fans>]
  147. number of installed fans. If the number of current installed
  148. fans reported by IPMI is not equal than <num fans> then a Warning state
  149. is returned. Please use this option carefully as number of fans and
  150. number of fan sensors can differ!
  151. [--fru]
  152. print the product serial number if it is available in the IPMI FRU data.
  153. For this purpose the tool 'ipmi-fru' is used. E.g.:
  154. IPMI Status: OK (9000096781)
  155. [--assettag]
  156. --fru is mandatory
  157. print the assettag if it is available in the IPMI FRU data.
  158. IPMI Status: OK (9000096781)
  159. Asset Tag: 20200220
  160. [--board]
  161. --fru is mandatory
  162. print additional motherboard informations if it is available in the IPMI FRU data.
  163. IPMI Status: OK (9000096781)
  164. Board Manufacturer: Supermicro
  165. Board Product Name: X10DRW-iT
  166. Board Serial Number: AB123456
  167. [--nosel]
  168. turn off system event log checking via ipmi-sel. If there are
  169. unintentional entries in SEL, use 'ipmi-sel --clear' or the -sx or -xST
  170. option.
  171. [--selonly]
  172. check only system event log checking via ipmi-sel. If there are
  173. unintentional entries in SEL, use 'ipmi-sel --clear' or the -sx or -xST
  174. option.
  175. [--seltail <count>]
  176. limit SEL output to specified count of last messages
  177. [-sx|--selexclude <sel exclude file>]
  178. use a sel exclude file to exclude entries from the system event log.
  179. Specify name and type pipe delimitered in this file to exclude an entry,
  180. for example: System Chassis Chassis Intru|Physical Security
  181. To get valid names and types use the -vvv option and take a look at:
  182. debug output for sel (-vvv is set). Don't use name and type from the
  183. web interface as sensor descriptions are not complete there.
  184. As with the '-xx' option if the first character of a line is '~' the
  185. name is treated as a regular expression.
  186. [-xx|--sexclude <exclude file>]
  187. use an exclude file to exclude sensors, each line specifies an exclude.
  188. Specify name and type pipe delimitered in this file to exclude a sensor,
  189. for example: System Chassis Chassis Intru|Physical Security
  190. If the first character of a line is '~' the name is treated as a regular
  191. expression. E.g. to exclude all sensor names from CPU0 to CPU9:
  192. ~CPU[0-9] Temp|Temperature
  193. To get valid names and types use the -vvv option.
  194. [-us|--unify-sensors <unify file>]
  195. use an unify file to unify sensor names. This is an easy way to rename
  196. sensors with given patterns in the file. Once might use this option
  197. to get the same sensor names accross different platforms, e.g. to only
  198. have 'Mainboard Temperature' as sensor name and not 'MB1 Temperature' or 'System Temp'.
  199. Rules in the file follow simple regex patterns e.g.:
  200. ^(MB1 Temperature|System Temp)\$/Mainboard Temperature
  201. Temp\$/TEMP
  202. [--nosudo]
  203. turn off sudo usage on localhost or if ipmi host is ommited.
  204. [--nothresholds]
  205. turn off performance data thresholds from output-sensor-thresholds.
  206. [--noentityabsent]
  207. skip sensor checks for sensors that have 'noentityabsent' as event state
  208. [--nodcmi]
  209. turn off power statistics via ipmi-dcmi.
  210. [-s <ipmi-sensor output file>]
  211. simulation mode - test the plugin with an ipmi-sensor output redirected
  212. to a file.
  213. [-h]
  214. show this help
  215. [-V]
  216. show version information
  217. Examples:
  218. \$ check_ipmi_sensor -H 192.0.2.1 -U monitor -P monitor -L user
  219. IPMI Status: OK | 'System Temp'=30.00 'Peripheral Temp'=32.00
  220. 'FAN 1'=2775.00 [...]
  221. \$ check_ipmi_sensor -H 192.0.2.1 -U monitor -P monitor -L user -x 205
  222. IPMI Status: OK | 'System Temp'=30.00 'Peripheral Temp'=32.00
  223. 'FAN 2'=2775.00 [...]
  224. \$ check_ipmi_sensor -H 192.0.2.1 -U monitor -P monitor -L user -i 4,71
  225. IPMI Status: OK | 'System Temp'=30.00 'Peripheral Temp'=32.00
  226. \$ check_ipmi_sensor -H 192.0.2.1 -U monitor -P monitor -L user -i 4 --fru
  227. IPMI Status: OK (0000012345) | 'System Temp'=30.00
  228. Further information about this plugin can be found at
  229. http://www.thomas-krenn.com/en/wiki/IPMI_Sensor_Monitoring_Plugin
  230. Use the github repo at https://github.com/thomas-krenn/check_ipmi_sensor_v3.git
  231. to submit patches, suggest improvements or if you have questions regarding
  232. use of this plugin.
  233. Attention: the mailing list is no longer in use but an archive can be found at
  234. http://lists.thomas-krenn.com/
  235. EOT
  236. }
  237. sub usage{
  238. my ($arg) = @_; #the list of inputs
  239. my ($exitcode);
  240. if ( defined $arg ){
  241. if ( $arg =~ m/^\d+$/ ){
  242. $exitcode = $arg;
  243. }
  244. else{
  245. print STDOUT $arg, "\n";
  246. $exitcode = 1;
  247. }
  248. }
  249. print STDOUT get_usage();
  250. exit($exitcode) if defined $exitcode;
  251. }
  252. ################################################################################
  253. # set ipmimonitoring path
  254. our $MISSING_COMMAND_TEXT = '';
  255. our $IPMICOMMAND ="";
  256. if(-x "/usr/sbin/ipmimonitoring"){
  257. $IPMICOMMAND = "/usr/sbin/ipmimonitoring";
  258. }
  259. elsif (-x "/usr/bin/ipmimonitoring"){
  260. $IPMICOMMAND = "/usr/bin/ipmimonitoring";
  261. }
  262. elsif (-x "/usr/local/sbin/ipmimonitoring"){
  263. $IPMICOMMAND = "/usr/local/sbin/ipmimonitoring";
  264. }
  265. elsif (-x "/usr/local/bin/ipmimonitoring"){
  266. $IPMICOMMAND = "/usr/local/bin/ipmimonitoring";
  267. }
  268. else{
  269. $MISSING_COMMAND_TEXT = " ipmimonitoring/ipmi-sensors command not found!\n";
  270. }
  271. # Identify the version of the ipmi-tool
  272. sub get_ipmi_version{
  273. my @ipmi_version_output = '';
  274. my $ipmi_version = '';
  275. @ipmi_version_output = `$IPMICOMMAND -V`;
  276. $ipmi_version = shift(@ipmi_version_output);
  277. $ipmi_version =~ /(\d+)\.(\d+)\.(\d+)/;
  278. @ipmi_version_output = ();
  279. push @ipmi_version_output,$1,$2,$3;
  280. return @ipmi_version_output;
  281. }
  282. sub simulate{
  283. my $output = '';
  284. my $simul_file = $_[0];
  285. if( !defined $simul_file || (-x '\"'.$simul_file.'\"')){
  286. print "DEBUG: Using simulation file: $simul_file\n";
  287. print "Error: Simulation file with ipmi output not found.\n";
  288. exit(3);
  289. }
  290. return ($output = `cat $simul_file`);
  291. }
  292. sub get_fru{
  293. my @frucmd = @{(shift)};
  294. my $verbosity = shift;
  295. my $fru;
  296. if(-e '/usr/sbin/ipmi-fru'){
  297. $fru = '/usr/sbin/ipmi-fru';
  298. }
  299. else{
  300. chomp($fru = `which ipmi-fru`);
  301. }
  302. #if sudo is used the command is the second element
  303. if($frucmd[0] eq 'sudo'){
  304. $frucmd[1] = $fru;
  305. }
  306. else{
  307. $frucmd[0] = $fru;
  308. }
  309. #skip checksum validation
  310. push @frucmd,'-s';
  311. my $fruoutput;
  312. my $returncode;
  313. run \@frucmd, '>&', \$fruoutput;
  314. #the upper eight bits contain the error condition (exit code)
  315. #see http://perldoc.perl.org/perlvar.html#Error-Variables
  316. $returncode = $? >> 8;
  317. if ( $returncode != 0 ){
  318. print "$fruoutput\n";
  319. print "-> Execution of $fru failed with return code $returncode.\n";
  320. print "-> $fru was executed with the following parameters:\n";
  321. print " ", join(' ', @frucmd), "\n";
  322. exit(3);
  323. }
  324. if($verbosity == 3){
  325. print "------------- debug output for fru (-vvv is set): ------------\n";
  326. print " $fru was executed with the following parameters:\n";
  327. print " ", join(' ', @frucmd), "\n";
  328. print " output of FreeIPMI:\n";
  329. print "$fruoutput";
  330. }
  331. return split('\n', $fruoutput);
  332. }
  333. sub get_sel{
  334. my @selcmd = @{(shift)};
  335. my $verbosity = shift;
  336. my @sel_sensor_types = @{(shift)};
  337. my @exclude_sel_sensor_types = @{(shift)};
  338. my @sel_options = @{(shift)};
  339. my $sel;
  340. if(-e '/usr/sbin/ipmi-sel'){
  341. $sel = '/usr/sbin/ipmi-sel';
  342. }
  343. else{
  344. chomp($sel = `which ipmi-sel`);
  345. }
  346. #if sudo is used the command is the second element
  347. if($selcmd[0] eq 'sudo'){
  348. $selcmd[1] = $sel;
  349. }
  350. else{
  351. $selcmd[0] = $sel;
  352. }
  353. push @selcmd, '--output-event-state', '--interpret-oem-data', '--entity-sensor-names';
  354. push @selcmd, '--sensor-types=' . join(',', @sel_sensor_types);
  355. if(@exclude_sel_sensor_types){
  356. push @selcmd, '--exclude-sensor-types=' . join(',', @exclude_sel_sensor_types);
  357. }
  358. if(@sel_options){
  359. push @selcmd, @sel_options;
  360. }
  361. my $seloutput;
  362. my $returncode;
  363. run \@selcmd, '>&', \$seloutput;
  364. $returncode = $? >> 8;
  365. if ( $returncode != 0 ){
  366. print "$seloutput\n";
  367. print "-> Execution of $sel failed with return code $returncode.\n";
  368. print "-> $sel was executed with the following parameters:\n";
  369. print " ", join(' ', @selcmd), "\n";
  370. exit(3);
  371. }
  372. if($verbosity == 3){
  373. print "------------- debug output for sel (-vvv is set): ------------\n";
  374. print " $sel was executed with the following parameters:\n";
  375. print " ", join(' ', @selcmd), "\n";
  376. print " output of FreeIPMI:\n";
  377. print "$seloutput";
  378. }
  379. return split('\n', $seloutput);
  380. }
  381. sub parse_sel{
  382. my $selcmd = shift;
  383. my $verbosity = shift;
  384. my $sel_xfile = shift;
  385. my $sel_sensor_types = shift;
  386. my $exclude_sel_sensor_types = shift;
  387. my $sel_options = shift;
  388. my @seloutput = get_sel($selcmd, $verbosity, $sel_sensor_types, $exclude_sel_sensor_types, $sel_options);
  389. # Remove unknown SEL records as they break output, in verbose mode these unknown records are kept as debug output
  390. @seloutput = grep(!/Unknown SEL Record Type:/, @seloutput);
  391. @seloutput = map { [ map { s/^\s*//; s/\s*$//; $_; } split(m/\|/, $_) ] } @seloutput;
  392. my $header = shift(@seloutput);
  393. my @sel_rows;
  394. foreach my $row (@seloutput){
  395. my %curr_row;
  396. for(my $i = 0; $i < scalar(@{$header}); $i++){
  397. my $key = lc $header->[$i];
  398. $curr_row{$key} = $row->[$i];
  399. }
  400. if(!(exclude_with_file($sel_xfile, $curr_row{'name'}, $curr_row{'type'}))){
  401. push @sel_rows, \%curr_row;
  402. }
  403. }
  404. return \@sel_rows;
  405. }
  406. sub get_dcmi{
  407. my @dcmicmd = @{(shift)};
  408. my $verbosity = shift;
  409. my $dcmi;
  410. if(-e '/usr/sbin/ipmi-dcmi'){
  411. $dcmi = '/usr/sbin/ipmi-dcmi';
  412. }
  413. else{
  414. chomp($dcmi = `which ipmi-dcmi`);
  415. }
  416. #if sudo is used the command is the second element
  417. if($dcmicmd[0] eq 'sudo'){
  418. $dcmicmd[1] = $dcmi;
  419. }
  420. else{
  421. $dcmicmd[0] = $dcmi;
  422. }
  423. push @dcmicmd, '--get-system-power-statistics';
  424. my $dcmioutput;
  425. my $returncode;
  426. run \@dcmicmd, '>&', \$dcmioutput;
  427. $returncode = $? >> 8;
  428. if ( $returncode == 0 ){
  429. return split('\n', $dcmioutput);
  430. }
  431. }
  432. sub parse_dcmi{
  433. my $dcmicmd = shift;
  434. my $verbosity = shift;
  435. my @dcmioutput = get_dcmi($dcmicmd, $verbosity);
  436. if(@dcmioutput){
  437. @dcmioutput = map { [ map { s/^\s*//; s/\s*$//; $_; } split(m/\:/, $_) ] } @dcmioutput;
  438. my %current_power;
  439. my $power_available = 0;
  440. foreach my $power (@dcmioutput){
  441. if(defined($power) && defined($power->[0]) && $power->[0] ne ''){
  442. if($power->[0] eq 'Current Power'){
  443. $power->[1] =~ m/^(\d+)/;
  444. my $watts = $1;
  445. $current_power{'Current Power'} = $watts;
  446. }
  447. if($power->[0] eq 'Power Measurement'){
  448. if($power->[1] eq 'Active'){
  449. $power_available = 1;
  450. }
  451. }
  452. }
  453. }
  454. if($power_available == 1){
  455. return \%current_power;
  456. }
  457. }
  458. }
  459. # Excludes a name and type pair if it is present in the given file, pipe
  460. # delimitered.
  461. # @return 1 if name should be skipped, 0 if not
  462. sub exclude_with_file{
  463. my $file_name = shift;
  464. my $name = shift;
  465. my $type = shift;
  466. my @xlist;
  467. my $skip = 0;
  468. if($file_name){
  469. if(!(open (FH, "< $file_name"))){
  470. print "-> Reading exclude file $file_name failed with: $!.\n";
  471. exit(3);
  472. };
  473. @xlist = <FH>;
  474. }
  475. foreach my $exclude (@xlist){
  476. my @curr_exclude = map { s/^\s*//; s/\s*$//; $_; } split(/\|/,$exclude);
  477. if(@curr_exclude && $curr_exclude[0] ne '' && $curr_exclude[1] ne ''){
  478. #if the first char of the name in the exclude file is a '~' treat it as regex
  479. if(substr($curr_exclude[0], 0, 1 ) eq '~'){
  480. my $regex_curr_exclude = substr $curr_exclude[0], 1;
  481. if($name =~ m/$regex_curr_exclude/ && $curr_exclude[1] eq $type){
  482. $skip = 1;
  483. }
  484. }
  485. elsif($curr_exclude[0] eq $name && $curr_exclude[1] eq $type){
  486. $skip = 1;
  487. }
  488. }
  489. }
  490. close FH;
  491. return $skip;
  492. }
  493. # Reads regular expressions from a file and applies the rules to sensor names.
  494. # This unifies sensor names across different platforms.
  495. # @return The sensor name with specified unify rules applied
  496. sub unify_with_file{
  497. my $file_name = shift;
  498. my $name = shift;# given sensor name
  499. my @ulist;# list of rules to apply
  500. if($file_name){
  501. if(!(open (FH, "< $file_name"))){
  502. print "-> Reading unify file $file_name failed with: $!.\n";
  503. exit(3);
  504. };
  505. @ulist = <FH>;
  506. }
  507. foreach my $unify_rule (@ulist){
  508. #split at the only / that is not masked with a \,
  509. #this is the separator in s/x/y/g to get x and y
  510. my @curr_rule = map { s/^\s*//; s/\s*$//; $_; } split(/(?<!\\)\//,$unify_rule);
  511. if(@curr_rule && $curr_rule[0] ne '' && $curr_rule[1] ne ''){
  512. $name =~ s/$curr_rule[0]/$curr_rule[1]/g;
  513. }
  514. }
  515. close FH;
  516. return $name;
  517. }
  518. #define entire hashes
  519. our %hdrmap = (
  520. 'Record_ID' => 'id', # FreeIPMI ...,0.7.x
  521. 'Record ID' => 'id', # FreeIPMI 0.8.x,... with --legacy-output
  522. 'ID' => 'id', # FreeIPMI 0.8.x
  523. 'Sensor Name' => 'name',
  524. 'Name' => 'name', # FreeIPMI 0.8.x
  525. 'Sensor Group' => 'type',
  526. 'Type' => 'type', # FreeIPMI 0.8.x
  527. 'Monitoring Status' => 'state',
  528. 'State' => 'state', # FreeIPMI 0.8.x
  529. 'Sensor Units' => 'units',
  530. 'Units' => 'units', # FreeIPMI 0.8.x
  531. 'Sensor Reading' => 'reading',
  532. 'Reading' => 'reading', # FreeIPMI 0.8.x
  533. 'Event' => 'event', # FreeIPMI 0.8.x
  534. 'Lower C' => 'lowerC',
  535. 'Lower NC' => 'lowerNC',
  536. 'Upper C' => 'upperC',
  537. 'Upper NC' => 'upperNC',
  538. 'Lower NR' => 'lowerNR',
  539. 'Upper NR' => 'upperNR',
  540. );
  541. our $verbosity = 0;
  542. MAIN: {
  543. $| = 1; #force a flush after every write or print
  544. my @ARGV_SAVE = @ARGV;#keep args for verbose output
  545. my ($show_help, $show_version);
  546. my ($ipmi_host, $ipmi_user, $ipmi_password, $ipmi_privilege_level, $ipmi_config_file, $ipmi_outformat);
  547. my (@freeipmi_options, $freeipmi_compat, @sel_options);
  548. my (@ipmi_sensor_types, @ipmi_exclude_sensor_types, @ipmi_xlist, @ipmi_ilist);
  549. my (@ipmi_version);
  550. my $ipmi_sensors = 0;#states to use ipmi-sensors instead of ipmimonitoring
  551. my $fan_count;#number of fans that should be installed in unit
  552. my $lanVersion;#if desired use a different protocol version
  553. my $abort_text = '';
  554. my $zenoss = 0;
  555. my @sel_sensor_types;
  556. my @exclude_sel_sensor_types;
  557. my $sel_issues_present = 0;
  558. my $simulate = '';
  559. my ($use_fru, $use_asset, $use_board, $no_sel, $sel_only, $sel_tail, $no_sudo, $use_thresholds, $no_thresholds, $sel_xfile, $s_xfile, $s_ufile, $no_entity_absent, $no_dcmi);
  560. #read in command line arguments and init hash variables with the given values from argv
  561. if ( !( GetOptions(
  562. 'H|host=s' => \$ipmi_host,
  563. 'f|config-file=s' => \$ipmi_config_file,
  564. 'U|user=s' => \$ipmi_user,
  565. 'P|password=s' => \$ipmi_password,
  566. 'L|privilege-level=s' => \$ipmi_privilege_level,
  567. 'O|options=s' => \@freeipmi_options,
  568. 'seloptions=s' => \@sel_options,
  569. 'b|compat' => \$freeipmi_compat,
  570. 'T|sensor-types=s' => \@ipmi_sensor_types,
  571. 'xT|exclude-sensor-types=s' => \@ipmi_exclude_sensor_types,
  572. 'ST|sel-sensor-types=s' => \@sel_sensor_types,
  573. 'xST|exclude-sel-sensor-types=s' => \@exclude_sel_sensor_types,
  574. 'fru' => \$use_fru,
  575. 'assettag' => \$use_asset,
  576. 'board' => \$use_board,
  577. 'nosel' => \$no_sel,
  578. 'selonly' => \$sel_only,
  579. 'seltail=s' => \$sel_tail,
  580. 'nosudo' => \$no_sudo,
  581. 'nothresholds' => \$no_thresholds,
  582. 'nodcmi' => \$no_dcmi,
  583. 'noentityabsent' => \$no_entity_absent,
  584. 'v|verbosity' => \$verbosity,
  585. 'vv' => sub{$verbosity=2},
  586. 'vvv' => sub{$verbosity=3},
  587. 'x|exclude=s' => \@ipmi_xlist,
  588. 'sx|selexclude=s' => \$sel_xfile,
  589. 'xx|sexclude=s' => \$s_xfile,
  590. 'us|unify-sensors=s'=> \$s_ufile,
  591. 'i|include=s' => \@ipmi_ilist,
  592. 'o|outformat=s' => \$ipmi_outformat,
  593. 'fc|fancount=i' => \$fan_count,
  594. 'D=s' => \$lanVersion,
  595. 's=s' => \$simulate,
  596. 'h|help' =>
  597. sub{print STDOUT get_version();
  598. print STDOUT "\n";
  599. print STDOUT get_usage();
  600. print STDOUT "\n";
  601. print STDOUT get_help();
  602. exit(0)
  603. },
  604. 'V|version' =>
  605. sub{
  606. print STDOUT get_version();
  607. exit(0);
  608. },
  609. 'usage|?' =>
  610. sub{print STDOUT get_usage();
  611. exit(3);
  612. }
  613. ) ) ){
  614. usage(1);#call usage if GetOptions failed
  615. }
  616. usage(1) if @ARGV;#print usage if unknown arg list is left
  617. ################################################################################
  618. # check for ipmimonitoring or ipmi-sensors. Since version > 0.8 ipmi-sensors is used
  619. # if '--legacy-output' is given ipmi-sensors cannot be used
  620. if( $MISSING_COMMAND_TEXT ne "" ){
  621. print STDOUT "Error:$MISSING_COMMAND_TEXT";
  622. exit(3);
  623. }
  624. else{
  625. @ipmi_version = get_ipmi_version();
  626. if( $ipmi_version[0] > 0 && (grep(/legacy\-output/,@freeipmi_options)) == 0){
  627. $IPMICOMMAND =~ s/ipmimonitoring/ipmi-sensors/;
  628. $ipmi_sensors = 1;
  629. }
  630. if( $ipmi_version[0] > 0 && (grep(/legacy\-output/,@freeipmi_options)) == 1){
  631. print "Error: Cannot use ipmi-sensors with option \'--legacy-output\'. Remove it to work correctly.\n";
  632. exit(3);
  633. }
  634. # check if output-sensor-thresholds can be used, this is supported
  635. # since 1.2.1. Version 1.2.0 was not released, so skip the third minor
  636. # version number
  637. if($ipmi_version[0] > 1 || ($ipmi_version[0] == 1 && $ipmi_version[1] >= 2)){
  638. $use_thresholds = 1;
  639. }
  640. else{
  641. $use_thresholds = 0;
  642. }
  643. }
  644. ###############################################################################
  645. # verify if all mandatory parameters are set and initialize various variables
  646. #\s defines any whitespace characters
  647. #first join the list, then split it at whitespace ' '
  648. #also cf. http://perldoc.perl.org/Getopt/Long.html#Options-with-multiple-values
  649. @freeipmi_options = split(/\s+/, join(' ', @freeipmi_options)); # a bit hack, shell word splitting should be implemented...
  650. @ipmi_sensor_types = split(/,/, join(',', @ipmi_sensor_types));
  651. @ipmi_exclude_sensor_types = split(/,/, join(',', @ipmi_exclude_sensor_types));
  652. @sel_sensor_types = split(/,/, join(',', @sel_sensor_types));
  653. @sel_options = split(/,/, join(',', @sel_options));
  654. @exclude_sel_sensor_types = split(/,/, join(',', @exclude_sel_sensor_types));
  655. @ipmi_xlist = split(/,/, join(',', @ipmi_xlist));
  656. @ipmi_ilist = split(/,/, join(',', @ipmi_ilist));
  657. #check for zenoss output
  658. if(defined $ipmi_outformat && $ipmi_outformat eq "zenoss"){
  659. $zenoss = 1;
  660. }
  661. # Per default monitor all sensor types, use -ST to specify your sensor types
  662. if(!@sel_sensor_types){
  663. @sel_sensor_types = ('all');
  664. }
  665. # If -xST has not been set, set this array to empty.
  666. if(!@exclude_sel_sensor_types){
  667. @exclude_sel_sensor_types = ();
  668. }
  669. if(!@sel_options){
  670. @sel_options = ();
  671. }
  672. # Define basic ipmi command
  673. my @basecmd = $IPMICOMMAND;
  674. # If host is omitted localhost is assumed, if not turned off sudo is used
  675. if(!(defined $ipmi_host) || ($ipmi_host eq 'localhost')){
  676. if(!defined($no_sudo)){
  677. # Only add sudo if not already root
  678. @basecmd = ($> != 0 ? 'sudo' : (), $IPMICOMMAND);
  679. }
  680. }
  681. # If we are not local, we need authentication credentials
  682. else{
  683. # Add the ipmi desired host
  684. push @basecmd, '-h', $ipmi_host;
  685. if(defined $ipmi_config_file){
  686. push @basecmd, '--config-file', $ipmi_config_file;
  687. }
  688. elsif(defined $ipmi_user && defined $ipmi_password && defined $ipmi_privilege_level ){
  689. push @basecmd, '-u', $ipmi_user, '-p', $ipmi_password, '-l', $ipmi_privilege_level;
  690. }
  691. else{
  692. $abort_text = $abort_text . " -f <FreeIPMI config file> or -U <username> -P <password> -L <privilege level>";
  693. }
  694. if( $abort_text ne ""){
  695. print STDOUT "Error: " . $abort_text . " missing.";
  696. print STDOUT get_usage();
  697. exit(3);
  698. }
  699. }
  700. # copy command for fru usage
  701. my @frucmd;
  702. if($use_fru){
  703. @frucmd = @basecmd
  704. }
  705. my @selcmd = @basecmd;
  706. my @dcmicmd = @basecmd;
  707. if(@ipmi_sensor_types){
  708. # , is the seperator in the new string
  709. # -g option is older name for ipmi-sensors -t or --sensor-types and
  710. # compatible with both older and newer version of FreeIPMI
  711. push @basecmd, '-g', join(',', @ipmi_sensor_types);
  712. }
  713. # add sensor types to exclude
  714. if(@ipmi_exclude_sensor_types){
  715. push @basecmd, '--exclude-sensor-types', join(',', @ipmi_exclude_sensor_types);
  716. }
  717. if(@freeipmi_options){
  718. push @basecmd, @freeipmi_options;
  719. }
  720. #keep original basecmd for later usage
  721. my @getstatus = @basecmd;
  722. #if -b is not defined, caching options are used
  723. if( !(defined $freeipmi_compat) ){
  724. push @getstatus, '--quiet-cache', '--sdr-cache-recreate';
  725. }
  726. #since version 0.8 it is possible to interpret OEM data
  727. if( ($ipmi_version[0] == 0 && $ipmi_version[1] > 7) ||
  728. $ipmi_version[0] > 0){
  729. push @getstatus, '--interpret-oem-data';
  730. }
  731. #since version 0.8 it is necessary to add the legacy option
  732. if( ($ipmi_version[0] == 0 && $ipmi_version[1] > 7) && (grep(/legacy\-output/,@freeipmi_options) == 0)){
  733. push @getstatus, '--legacy-output';
  734. }
  735. #if ipmi-sensors is used show the state of sensors and ignore N/A
  736. if($ipmi_sensors){
  737. push @getstatus, '--output-sensor-state', '--ignore-not-available-sensors';
  738. }
  739. #if not stated otherwise we use protocol lan version 2 per default
  740. if(!defined($lanVersion)){
  741. $lanVersion = 'LAN_2_0';
  742. }
  743. if($lanVersion ne 'default' && defined $ipmi_host && $ipmi_host ne 'localhost'){
  744. push @getstatus, "--driver-type=$lanVersion";
  745. if(!$no_sel){
  746. push @selcmd, "--driver-type=$lanVersion";
  747. }
  748. if($use_fru){
  749. push @frucmd, "--driver-type=$lanVersion";
  750. }
  751. }
  752. if($use_thresholds && !$no_thresholds){
  753. push @getstatus, '--output-sensor-thresholds';
  754. }
  755. if(defined($sel_tail)){
  756. push @selcmd, "--tail=$sel_tail";
  757. }
  758. ################################################################################
  759. #execute status command and redirect stdout and stderr to ipmioutput
  760. my $ipmioutput;
  761. my $returncode;
  762. if ($sel_only){
  763. $returncode = 0;
  764. }
  765. elsif(!$simulate){
  766. run \@getstatus, '>&', \$ipmioutput;
  767. #the upper eight bits contain the error condition (exit code)
  768. #see http://perldoc.perl.org/perlvar.html#Error-Variables
  769. $returncode = $? >> 8;
  770. }
  771. else{
  772. $ipmioutput = simulate($simulate);
  773. print "DEBUG: Using simulation mode\n";
  774. $returncode = 0;
  775. }
  776. my @fruoutput;
  777. if($use_fru){
  778. @fruoutput = get_fru(\@frucmd, $verbosity);
  779. }
  780. my $seloutput;
  781. if(!$no_sel){
  782. $seloutput = parse_sel(\@selcmd, $verbosity, $sel_xfile, \@sel_sensor_types, \@exclude_sel_sensor_types, \@sel_options);
  783. }
  784. my $dcmioutput;
  785. if(!$no_dcmi){
  786. $dcmioutput = parse_dcmi(\@dcmicmd, $verbosity);
  787. }
  788. ################################################################################
  789. # print debug output when verbosity is set to 3 (-vvv)
  790. if ( $verbosity == 3 && !$sel_only ){
  791. my $ipmicommandversion;
  792. run [$IPMICOMMAND, '-V'], '2>&1', '|', ['head', '-n', 1], '&>', \$ipmicommandversion;
  793. #remove trailing newline with chomp
  794. chomp $ipmicommandversion;
  795. print "------------- debug output for sensors (-vvv is set): ------------\n";
  796. print " script was executed with the following parameters:\n";
  797. print " $0 ", join(' ', @ARGV_SAVE), "\n";
  798. print " check_ipmi_sensor version:\n";
  799. print " $check_ipmi_sensor_version\n";
  800. print " FreeIPMI version:\n";
  801. print " $ipmicommandversion\n";
  802. print " FreeIPMI was executed with the following parameters:\n";
  803. print " ", join(' ', @getstatus), "\n";
  804. print " FreeIPMI return code: $returncode\n";
  805. print " output of FreeIPMI:\n";
  806. print "$ipmioutput\n";
  807. print "--------------------- end of debug output ---------------------\n";
  808. }
  809. ################################################################################
  810. # generate main output
  811. if ( $returncode != 0 ){
  812. print "$ipmioutput\n";
  813. print "-> Execution of $IPMICOMMAND failed with return code $returncode.\n";
  814. print "-> $IPMICOMMAND was executed with the following parameters:\n";
  815. my $getstatusoutput = join(' ', @getstatus);
  816. $getstatusoutput =~ s/$ipmi_password/[HIDDEN]/;
  817. print " ", $getstatusoutput, "\n";
  818. exit(3);
  819. }
  820. else{
  821. my @outputRows;
  822. if(defined($ipmioutput)){
  823. @outputRows = split('\n', $ipmioutput);
  824. }
  825. if(!$sel_only && (!defined($ipmioutput) || scalar(@outputRows) == 1)){
  826. print "-> Your server seems to be powered off.";
  827. print " (Execution of FreeIPMI returned an empty output or only 1 header row!)\n";
  828. print "-> $IPMICOMMAND was executed with the following parameters:\n";
  829. my $getstatusoutput = join(' ', @getstatus);
  830. $getstatusoutput =~ s/$ipmi_password/[HIDDEN]/;
  831. print " ", $getstatusoutput, "\n";
  832. exit(3);
  833. }
  834. #print desired filter types
  835. if ( @ipmi_sensor_types ){
  836. print "Sensor Type(s) ", join(', ', @ipmi_sensor_types), " Status: ";
  837. }
  838. elsif ($sel_only){
  839. print "SEL Status: ";
  840. }
  841. else{
  842. print "IPMI Status: ";
  843. }
  844. #start with main output
  845. my $exit = 0;
  846. my $w_sensors = '';#sensors with warnings
  847. my $sel_w_sensors = '';#verbose output for sel entries with warnings
  848. my $perf = '';#performance sensor
  849. my $curr_fans = 0;
  850. my @ipmioutput2;#filtered original ipmi output
  851. #skip ipmi output, if only SEL queried
  852. if (!$sel_only){
  853. #split at newlines, fetch array with lines of output
  854. my @ipmioutput = split('\n', $ipmioutput);
  855. #remove sudo errors and warnings like they appear on dns resolving issues
  856. @ipmioutput = map { /^sudo:/ ? () : $_ } @ipmioutput;
  857. #remove leading and trailing whitespace characters, split at the pipe delimiter
  858. @ipmioutput = map { [ map { s/^\s*//; s/\s*$//; $_; } split(m/\|/, $_) ] } @ipmioutput;
  859. #shift out the header as it is the first line
  860. my $header = shift @ipmioutput;
  861. if(!defined($header)){
  862. print "$ipmioutput\n";
  863. print " FreeIPMI returned an empty header map (first line)";
  864. if(@ipmi_sensor_types){
  865. print " FreeIPMI could not find any sensors for the given sensor type (option '-T').\n";
  866. }
  867. exit(3);
  868. }
  869. my %header;
  870. for(my $i = 0; $i < @$header; $i++)
  871. {
  872. #assigning %header with (key from hdrmap) => $i
  873. #checking at which position in the header is which key
  874. $header{$hdrmap{$header->[$i]}} = $i;
  875. }
  876. foreach my $row ( @ipmioutput ){
  877. my %row;
  878. #fetch keys from header and assign existent values to row
  879. #this maps the values from row(ipmioutput) to the header values
  880. while ( my ($key, $index) = each %header ){
  881. #check if the option to unify sensor names is active
  882. if($key eq 'name' && $s_ufile && $s_ufile ne ''){
  883. $row{$key} = unify_with_file($s_ufile, $row->[$index]);
  884. }
  885. else{
  886. $row{$key} = $row->[$index];
  887. }
  888. }
  889. if(!(exclude_with_file($s_xfile, $row{'name'}, $row{'type'}))){
  890. push @ipmioutput2, \%row;
  891. }
  892. }
  893. #create hash with sensor name an 1
  894. my %ipmi_xlist = map { ($_, 1) } @ipmi_xlist;
  895. #filter out the desired sensor values
  896. @ipmioutput2 = grep(!exists $ipmi_xlist{$_->{'id'}}, @ipmioutput2);
  897. #check for an include list
  898. if(@ipmi_ilist){
  899. my %ipmi_ilist = map { ($_, 1) } @ipmi_ilist;
  900. #only include sensors from include list
  901. @ipmioutput2 = grep(exists $ipmi_ilist{$_->{'id'}}, @ipmioutput2);
  902. }
  903. foreach my $row ( @ipmioutput2 ){
  904. if( $zenoss ){
  905. $row->{'name'} =~ s/ /_/g;
  906. }
  907. my $check_sensor_state = 1;
  908. if($no_entity_absent){
  909. if(exists $row->{'event'} && ($row->{'event'} =~ /\'.*((Device|Entity) (Absent|Removed)).*\'/)){
  910. $check_sensor_state = 0;
  911. }
  912. if(exists $row->{'reading'} && ($row->{'reading'} =~ /\'.*((Device|Entity) (Absent|Removed)).*\'/)){
  913. $check_sensor_state = 0;
  914. }
  915. }
  916. #check for warning sensors
  917. if($check_sensor_state && ($row->{'state'} ne 'Nominal' && $row->{'state'} ne 'N/A')){
  918. $exit = 1 if $exit < 1;
  919. $exit = 2 if $exit < 2 && $row->{'state'} ne 'Warning';
  920. #don't insert a , the first time
  921. $w_sensors .= ", " unless $w_sensors eq '';
  922. $w_sensors .= "$row->{'name'} = $row->{'state'}";
  923. if( $verbosity ){
  924. if( $row->{'reading'} ne 'N/A'){
  925. $w_sensors .= " ($row->{'reading'})" ;
  926. }
  927. else{
  928. $w_sensors .= " ($row->{'event'})";
  929. }
  930. }
  931. }
  932. if($check_sensor_state && ($row->{'units'} ne 'N/A')){
  933. my $val = $row->{'reading'};
  934. my $perf_data;
  935. my $perf_thresholds;
  936. if($zenoss){
  937. $perf_data = $row->{'name'}."=".$val;
  938. }
  939. else{
  940. $perf_data = "'".$row->{'name'}."'=".$val;
  941. }
  942. if($use_thresholds && !$no_thresholds){
  943. if(($row->{'lowerNC'} ne 'N/A') && ($row->{'upperNC'} ne 'N/A')){
  944. $perf_thresholds = $row->{'lowerNC'}.":".$row->{'upperNC'}.";";
  945. }
  946. elsif(($row->{'lowerNC'} ne 'N/A') && ($row->{'upperNC'} eq 'N/A')){
  947. $perf_thresholds = $row->{'lowerNC'}.":;";
  948. }
  949. elsif(($row->{'lowerNC'} eq 'N/A') && ($row->{'upperNC'} ne 'N/A')){
  950. $perf_thresholds = "~:".$row->{'upperNC'}.";";
  951. }
  952. elsif(($row->{'lowerNC'} eq 'N/A') && ($row->{'upperNC'} eq 'N/A')){
  953. $perf_thresholds = ";";
  954. }
  955. if(($row->{'lowerC'} ne 'N/A') && ($row->{'upperC'} ne 'N/A')){
  956. $perf_thresholds .= $row->{'lowerC'}.":".$row->{'upperC'};
  957. }
  958. elsif(($row->{'lowerC'} ne 'N/A') && ($row->{'upperC'} eq 'N/A')){
  959. $perf_thresholds .= $row->{'lowerC'}.":";
  960. }
  961. elsif(($row->{'lowerC'} eq 'N/A') && ($row->{'upperC'} ne 'N/A')){
  962. $perf_thresholds .= "~:".$row->{'upperC'};
  963. }
  964. # Add thresholds to performance data
  965. if(($row->{'lowerNC'} ne 'N/A') || ($row->{'upperNC'} ne 'N/A') ||
  966. ($row->{'lowerC'} ne 'N/A') || ($row->{'upperC'} ne 'N/A')){
  967. $perf_data .= ";".$perf_thresholds;
  968. }
  969. }
  970. $perf .= $perf_data." ";
  971. }
  972. if( $row->{'type'} eq 'Fan' && $row->{'reading'} ne 'N/A' ){
  973. $curr_fans++;
  974. }
  975. }
  976. }
  977. foreach my $row (@{$seloutput}){
  978. if( $zenoss ){
  979. $row->{'name'} =~ s/ /_/g;
  980. }
  981. if ($row->{'state'} ne 'Nominal'){
  982. $sel_issues_present += 1;
  983. $exit = 1 if $exit < 1;
  984. $exit = 2 if $exit < 2 && $row->{'state'} ne 'Warning';
  985. if( $verbosity ){
  986. $sel_w_sensors .= ", " unless $sel_w_sensors eq '';
  987. $sel_w_sensors .= "($row->{'name'} = $row->{'state'},";
  988. $sel_w_sensors .= " $row->{'type'}," ;
  989. $sel_w_sensors .= " $row->{'event'})" ;
  990. }
  991. }
  992. }
  993. if ( $sel_issues_present ){
  994. $w_sensors .= ", " unless $w_sensors eq '';
  995. if ( $sel_issues_present == 1 ){
  996. $w_sensors .= "1 system event log (SEL) entry present";
  997. }else{
  998. $w_sensors .= $sel_issues_present." system event log (SEL) entries present";
  999. }
  1000. if( $verbosity ){
  1001. $w_sensors .= " - details: ";
  1002. $w_sensors .= $sel_w_sensors;
  1003. $w_sensors .= " - fix the reported issues and clear your SEL";
  1004. $w_sensors .= " or exclude specific SEL entries using the -sx or -xST option";
  1005. }
  1006. }
  1007. #now check if num fans equals desired unit fans
  1008. if( $fan_count ){
  1009. if( $curr_fans < $fan_count ){
  1010. $exit = 1 if $exit < 1;
  1011. $w_sensors .= ", " unless $w_sensors eq '';
  1012. $w_sensors .= "Fan = Warning";
  1013. if( $verbosity ){
  1014. $w_sensors .= " ($curr_fans)" ;
  1015. }
  1016. }
  1017. }
  1018. #check for the FRU serial number, asset tag and board information
  1019. my @server_serial;
  1020. my $serial_number;
  1021. my @server_asset;
  1022. my $asset_tag;
  1023. my @server_board;
  1024. my $board_manufacturer;
  1025. my $board_product;
  1026. my $board_serial;
  1027. if( $use_fru ){
  1028. @server_serial = grep(/Product Serial Number/,@fruoutput);
  1029. if(@server_serial){
  1030. $server_serial[0] =~ m/\:\s+(.*)$/;
  1031. $serial_number = $1;
  1032. }
  1033. if ( $use_asset ){
  1034. @server_asset = grep(/Product Asset Tag/,@fruoutput);
  1035. if(@server_asset){
  1036. $server_asset[0] =~ m/\:\s+(.*)$/;
  1037. $asset_tag = $1;
  1038. }
  1039. }
  1040. if ( $use_board ){
  1041. @server_board = grep(/Board/,@fruoutput);
  1042. foreach ( @server_board) {
  1043. if ( $_ =~ /Board Manufacturer/ ){
  1044. $_ =~ m/Board Manufacturer\:\s+(.*)/;
  1045. $board_manufacturer = $1;
  1046. }
  1047. if ( $_ =~ /Board Product Name/ ){
  1048. $_ =~ m/Board Product Name\:\s+(.*)/;
  1049. $board_product = $1;
  1050. }
  1051. if ( $_ =~ /Board Serial Number/ ){
  1052. $_ =~ m/Board Serial Number\:\s+(.*)/;
  1053. $board_serial = $1;
  1054. }
  1055. }
  1056. }
  1057. }
  1058. if(defined($dcmioutput) && $dcmioutput ne ''){
  1059. my $power_perf = '';
  1060. if(exists $dcmioutput->{'Current Power'}){
  1061. my $power_key = 'Current Power';
  1062. if($s_ufile && $s_ufile ne ''){
  1063. $power_key = unify_with_file($s_ufile, $power_key);
  1064. }
  1065. if( $zenoss ){
  1066. $power_key =~ s/ /_/g;
  1067. }
  1068. $power_perf = "\'$power_key\'=" . $dcmioutput->{'Current Power'};
  1069. }
  1070. $perf = $power_perf . ' ' . $perf;
  1071. }
  1072. $perf = substr($perf, 0, -1);#cut off the last chars
  1073. if ( $exit == 0 ){
  1074. print "OK";
  1075. }
  1076. elsif ( $exit == 1 ){
  1077. print "Warning [$w_sensors]";
  1078. }
  1079. else{
  1080. print "Critical [$w_sensors]";
  1081. }
  1082. if( $use_fru && defined($serial_number)){
  1083. print " ($serial_number)";
  1084. if ($use_asset && defined($asset_tag)){
  1085. print "\nAsset Tag: $asset_tag";
  1086. }
  1087. if ( $use_board ){
  1088. if (defined($board_manufacturer)){
  1089. print "\nBoard Manufacturer: $board_manufacturer";
  1090. }
  1091. if (defined($board_product)){
  1092. print "\nBoard Product: $board_product";
  1093. }
  1094. if (defined($board_serial)){
  1095. print "\nBoard Serial: $board_serial";
  1096. }
  1097. }
  1098. }
  1099. print " | ", $perf if $perf ne '';
  1100. print "\n";
  1101. if ( $verbosity > 1 ){
  1102. foreach my $row (@ipmioutput2){
  1103. if( $row->{'state'} eq 'N/A'){
  1104. next;
  1105. }
  1106. elsif( $row->{'reading'} ne 'N/A'){
  1107. print "$row->{'name'} = $row->{'reading'} ";
  1108. }
  1109. elsif( $row->{'event'} ne 'N/A'){
  1110. print "$row->{'name'} = $row->{'event'} ";
  1111. }
  1112. else{
  1113. next;
  1114. }
  1115. print "(Status: $row->{'state'})\n";
  1116. }
  1117. }
  1118. exit $exit;
  1119. }
  1120. };