├── .github ├── ISSUE_TEMPLATE │ └── bug_report.md └── workflows │ └── python3check.yml ├── README.md └── check_esxi_hardware.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Before actually creating a new issue** 11 | I confirm, I have read the FAQ (https://www.claudiokuenzler.com/blog/308/check-esxi-hardware-faq-frequently-asked-questions): Y/N 12 | I confirm, I have restarted the CIM server (` /etc/init.d/sfcbd-watchdog restart `) on the ESXi server and the problem remains: Y/N 13 | I confirm, I have cleared the server's local IPMI cache (`localcli hardware ipmi sel clear`) and restarted the services (`/sbin/services.sh restart`) on the ESXi server and the problem remains: Y/N 14 | 15 | **Describe the bug** 16 | A clear and concise description of what the bug is. 17 | 18 | **Show the full plugin output, including the command with -v parameter** 19 | Run the plugin with `-v` parameter and show the full output (including command) here. Obviously obfuscate credentials. 20 | 21 | **Expected behavior** 22 | A clear and concise description of what you expected to happen. 23 | 24 | **Versions:** 25 | - check_esxi_hardware plugin: 26 | - VMware ESXi: 27 | - pywbem: 28 | - Python: 29 | - Third party tools (Dell OMSA, HP Offline Bundle, etc): 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/workflows/python3check.yml: -------------------------------------------------------------------------------- 1 | # @file python3check.yml 2 | --- 3 | name: Python3 check 4 | 5 | # Trigger the workflow on push or pull request 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | python3-pywbem-latest: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - name: Install latest pywbem from pip 14 | run: | 15 | sudo apt-get install -qq -yy python3 python3-pip 16 | pip3 install pywbem 17 | - name: Verify python sys.path 18 | run: (echo "import sys"; echo "print(', '.join(sys.path))") | python3 19 | - name: Launch script with --help 20 | run: | 21 | ./check_esxi_hardware.py --help 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | check_esxi_hardware 2 | ========= 3 | 4 | Monitoring plugin to check the hardware on VMware ESX/ESXi servers. 5 | 6 | This is the public git repository for development of the plugin. 7 | 8 | 9 | Documentation + Production Ready Plugin 10 | ------------- 11 | Please refer to https://www.claudiokuenzler.com/monitoring-plugins/check_esxi_hardware.php 12 | 13 | Compatibility list 14 | ------------- 15 | Please check https://www.claudiokuenzler.com/blog/1110/check_esxi_hardware-esxi-compatibility-matrix-list for a (non conclusive) matrix of known working versions. 16 | -------------------------------------------------------------------------------- /check_esxi_hardware.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | # 4 | # Script for checking global health of host running VMware ESX/ESXi 5 | # 6 | # Licence : GNU General Public Licence (GPL) http://www.gnu.org/ 7 | # This program is free software; you can redistribute it and/or 8 | # modify it under the terms of the GNU General Public License 9 | # as published by the Free Software Foundation; either version 2 10 | # of the License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; if not, see . 19 | # 20 | # Pre-req : pywbem 21 | # 22 | # Copyright (c) 2008 David Ligeret 23 | # Copyright (c) 2009 Joshua Daniel Franklin 24 | # Copyright (c) 2010 Branden Schneider 25 | # Copyright (c) 2010-2025 Claudio Kuenzler 26 | # Copyright (c) 2010 Samir Ibradzic 27 | # Copyright (c) 2010 Aaron Rogers 28 | # Copyright (c) 2011 Ludovic Hutin 29 | # Copyright (c) 2011 Carsten Schoene 30 | # Copyright (c) 2011-2012 Phil Randal 31 | # Copyright (c) 2011 Fredrik Aslund 32 | # Copyright (c) 2011 Bertrand Jomin 33 | # Copyright (c) 2011 Ian Chard 34 | # Copyright (c) 2012 Craig Hart 35 | # Copyright (c) 2013 Carl R. Friend 36 | # Copyright (c) 2015 Andreas Gottwald 37 | # Copyright (c) 2015 Stanislav German-Evtushenko 38 | # Copyright (c) 2015 Stefan Roos 39 | # Copyright (c) 2018 Peter Newman 40 | # Copyright (c) 2020 Luca Berra 41 | # Copyright (c) 2022 Marco Markgraf 42 | # 43 | # The VMware CIM API is documented here (as of October 2024): 44 | # https://docs.vmware.com/en/VMware-vSphere/7.0/vsphere-cim-smash-server-management-api-programming-guide/GUID-2725D01E-AE02-4EF2-9E98-5AB82AA0349A.html 45 | 46 | # The CIM classes are documented here (as of October 2024): 47 | # https://vdc-download.vmware.com/vmwb-repository/dcr-public/27c1c014-7315-4d6b-8e6b-292130a79b3c/36aca268-99fa-4916-b993-a077de55cbf1/CIM_API_Reference/index.html 48 | # 49 | # This monitoring plugin is maintained and documented here: 50 | # https://www.claudiokuenzler.com/monitoring-plugins/check_esxi_hardware.php 51 | # 52 | #@--------------------------------------------------- 53 | #@ History 54 | #@--------------------------------------------------- 55 | #@ Date : 20080820 56 | #@ Author : David Ligeret 57 | #@ Reason : Initial release 58 | #@--------------------------------------------------- 59 | #@ Date : 20080821 60 | #@ Author : David Ligeret 61 | #@ Reason : Add verbose mode 62 | #@--------------------------------------------------- 63 | #@ Date : 20090219 64 | #@ Author : Joshua Daniel Franklin 65 | #@ Reason : Add try/except to catch AuthError and CIMError 66 | #@--------------------------------------------------- 67 | #@ Date : 20100202 68 | #@ Author : Branden Schneider 69 | #@ Reason : Added HP Support (HealthState) 70 | #@--------------------------------------------------- 71 | #@ Date : 20100512 72 | #@ Author : Claudio Kuenzler www.claudiokuenzler.com 73 | #@ Reason : Combined different versions (Joshua and Branden) 74 | #@ Reason : Added hardware type switch (dell or hp) 75 | #@--------------------------------------------------- 76 | #@ Date : 20100626/28 77 | #@ Author : Samir Ibradzic www.brastel.com 78 | #@ Reason : Added basic server info 79 | #@ Reason : Wanted to have server name, serial number & bios version at output 80 | #@ Reason : Set default return status to Unknown 81 | #@--------------------------------------------------- 82 | #@ Date : 20100702 83 | #@ Author : Aaron Rogers www.cloudmark.com 84 | #@ Reason : GlobalStatus was incorrectly getting (re)set to OK with every CIM element check 85 | #@--------------------------------------------------- 86 | #@ Date : 20100705 87 | #@ Author : Claudio Kuenzler www.claudiokuenzler.com 88 | #@ Reason : Due to change 20100702 all Dell servers would return UNKNOWN instead of OK... 89 | #@ Reason : ... so added Aaron's logic at the end of the Dell checks as well 90 | #@--------------------------------------------------- 91 | #@ Date : 20101028 92 | #@ Author : Claudio Kuenzler www.claudiokuenzler.com 93 | #@ Reason : Changed text in Usage and Example so people dont forget to use https:// 94 | #@--------------------------------------------------- 95 | #@ Date : 20110110 96 | #@ Author : Ludovic Hutin (Idea and Coding) / Claudio Kuenzler (Bugfix) 97 | #@ Reason : If Dell Blade Servers are used, Serial Number of Chassis was returned 98 | #@--------------------------------------------------- 99 | #@ Date : 20110207 100 | #@ Author : Carsten Schoene carsten.schoene.cc 101 | #@ Reason : Bugfix for Intel systems (in this case Intel SE7520) - use 'intel' as system type 102 | #@--------------------------------------------------- 103 | #@ Date : 20110215 104 | #@ Author : Ludovic Hutin 105 | #@ Reason : Plugin now catches Socket Error (Timeout Error) and added a timeout parameter 106 | #@--------------------------------------------------- 107 | #@ Date : 20110217/18 108 | #@ Author : Ludovic Hutin / Tom Murphy 109 | #@ Reason : Bugfix in Socket Error if clause 110 | #@--------------------------------------------------- 111 | #@ Date : 20110221 112 | #@ Author : Claudio Kuenzler www.claudiokuenzler.com 113 | #@ Reason : Remove recently added Timeout due to incompabatility on Windows 114 | #@ Reason : and changed name of plugin to check_esxi_hardware 115 | #@--------------------------------------------------- 116 | #@ Date : 20110426 117 | #@ Author : Claudio Kuenzler www.claudiokuenzler.com 118 | #@ Reason : Added 'ibm' hardware type (compatible to Dell output). Tested by Keith Erekson. 119 | #@--------------------------------------------------- 120 | #@ Date : 20110426 121 | #@ Author : Phil Randal 122 | #@ Reason : URLise Dell model and tag numbers (as in check_openmanage) 123 | #@ Reason : Return performance data (as in check_openmanage, using similar names where possible) 124 | #@ Reason : Minor code tidyup - use elementName instead of instance['ElementName'] 125 | #@--------------------------------------------------- 126 | #@ Date : 20110428 127 | #@ Author : Phil Randal (phil.randal@gmail.com) 128 | #@ Reason : If hardware type is specified as 'auto' try to autodetect vendor 129 | #@ Reason : Return performance data for some HP models 130 | #@ Reason : Indent 'verbose' output to make it easier to read 131 | #@ Reason : Use OptionParser to give better parameter parsing (retaining compatability with original) 132 | #@--------------------------------------------------- 133 | #@ Date : 20110503 134 | #@ Author : Phil Randal (phil.randal@gmail.com) 135 | #@ Reason : Fix bug in HP Virtual Fan percentage output 136 | #@ Reason : Slight code reorganisation 137 | #@ Reason : Sort performance data 138 | #@ Reason : Fix formatting of current output 139 | #@--------------------------------------------------- 140 | #@ Date : 20110504 141 | #@ Author : Phil Randal (phil.randal@gmail.com) 142 | #@ Reason : Minor code changes and documentation improvements 143 | #@ Reason : Remove redundant mismatched ' character in performance data output 144 | #@ Reason : Output non-integral values for all sensors to fix problem seen with system board voltage sensors 145 | #@ on an IBM server (thanks to Attilio Drei for the sample output) 146 | #@--------------------------------------------------- 147 | #@ Date : 20110505 148 | #@ Author : Fredrik Aslund 149 | #@ Reason : Added possibility to use first line of a file as password (file:) 150 | #@--------------------------------------------------- 151 | #@ Date : 20110505 152 | #@ Author : Phil Randal (phil.randal@gmail.com) 153 | #@ Reason : Simplfy 'verboseoutput' to use 'verbose' as global variable instead of as parameter 154 | #@ Reason : Don't look at performance data from CIM_NumericSensor if we're not using it 155 | #@ Reason : Add --no-power, --no-volts, --no-current, --no-temp, and --no-fan options 156 | #@--------------------------------------------------- 157 | #@ Date : 20110506 158 | #@ Author : Phil Randal (phil.randal@gmail.com) 159 | #@ Reason : Reinstate timeouts with --timeout parameter (but not on Windows) 160 | #@ Reason : Allow file:passwordfile in old-style arguments too 161 | #@--------------------------------------------------- 162 | #@ Date : 20110507 163 | #@ Author : Phil Randal (phil.randal@gmail.com) 164 | #@ Reason : On error, include numeric sensor value in output 165 | #@--------------------------------------------------- 166 | #@ Date : 20110520 167 | #@ Author : Bertrand Jomin 168 | #@ Reason : Plugin had problems to handle some S/N from IBM Blade Servers 169 | #@--------------------------------------------------- 170 | #@ Date : 20110614 171 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 172 | #@ Reason : Rewrote file handling and file can now be used for user AND password 173 | #@--------------------------------------------------- 174 | #@ Date : 20111003 175 | #@ Author : Ian Chard (ian@chard.org) 176 | #@ Reason : Allow a list of unwanted elements to be specified, which is useful 177 | #@ in cases where hardware isn't well supported by ESXi 178 | #@--------------------------------------------------- 179 | #@ Date : 20120402 180 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 181 | #@ Reason : Making plugin GPL compatible (Copyright) and preparing for OpenBSD port 182 | #@--------------------------------------------------- 183 | #@ Date : 20120405 184 | #@ Author : Phil Randal (phil.randal@gmail.com) 185 | #@ Reason : Fix lookup of warranty info for Dell 186 | #@--------------------------------------------------- 187 | #@ Date : 20120501 188 | #@ Author : Craig Hart 189 | #@ Reason : Bugfix in manufacturer discovery when cim entry not found or empty 190 | #@--------------------------------------------------- 191 | #@ Date : 20121027 192 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 193 | #@ Reason : Added workaround for Dell PE x620 where "System Board 1 Riser Config Err 0: Connected" 194 | #@ element outputs wrong return code. Dell, please fix that. 195 | #@ Added web-link to VMware CIM API 5.x at top of script. 196 | #@--------------------------------------------------- 197 | #@ Date : 20130424 198 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 199 | #@ Reason : Another workaround for Dell systems "System Board 1 LCD Cable Pres 0: Connected" 200 | #@--------------------------------------------------- 201 | #@ Date : 20130702 202 | #@ Author : Carl R. Friend 203 | #@ Reason : Improving wrong authentication timeout and exit UNKNOWN 204 | #@--------------------------------------------------- 205 | #@ Date : 20130725 206 | #@ Author : Phil Randal (phil.randal@gmail.com) 207 | #@ Reason : Fix lookup of warranty info for Dell 208 | #@--------------------------------------------------- 209 | #@ Date : 20140319 210 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 211 | #@ Reason : Another two workarounds for Dell systems (VGA Cable Pres 0, Add-in Card 4 PEM Presence 0) 212 | #@--------------------------------------------------- 213 | #@ Date : 20150109 214 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 215 | #@ Reason : Output serial number of chassis if a blade server is checked 216 | #@--------------------------------------------------- 217 | #@ Date : 20150119 218 | #@ Author : Andreas Gottwald 219 | #@ Reason : Fix NoneType element bug 220 | #@--------------------------------------------------- 221 | #@ Date : 20150626 222 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 223 | #@ Reason : Added support for patched pywbem 0.7.0 and new version 0.8.0, handle SSL error exception 224 | #@--------------------------------------------------- 225 | #@ Date : 20150710 226 | #@ Author : Stanislav German-Evtushenko 227 | #@ Reason : Exit Unknown instead of Critical for timeouts and auth errors 228 | #@--------------------------------------------------- 229 | #@ Date : 20151111 230 | #@ Author : Stefan Roos 231 | #@ Reason : Removed unused sensor_value variable and string import. 232 | #@ Reason : Added global hosturl variable declaration after imports. 233 | #@--------------------------------------------------- 234 | #@ Date : 20160411 235 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 236 | #@ Reason : Distinguish between pywbem 0.7 and 0.8 (which is now released) 237 | #@--------------------------------------------------- 238 | #@ Date : 20160531 239 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 240 | #@ Reason : Add parameter for variable CIM port (useful when behind NAT) 241 | #@--------------------------------------------------- 242 | #@ Date : 20161013 243 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 244 | #@ Reason : Added support for pywbem 0.9.x (and upcoming releases) 245 | #@--------------------------------------------------- 246 | #@ Date : 20170905 247 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 248 | #@ Reason : Added option to ignore LCD/Display related elements (--no-lcd) 249 | #@--------------------------------------------------- 250 | #@ Date : 20180329 251 | #@ Author : Claudio Kuenzler (www.claudiokuenzler.com) 252 | #@ Reason : Try to use internal pywbem function to determine version 253 | #@--------------------------------------------------- 254 | #@ Date : 20180411 255 | #@ Author : Peter Newman 256 | #@ Reason : Throw an unknown if we can't fetch the data for some reason 257 | #@--------------------------------------------------- 258 | #@ Date : 20181001 259 | #@ Author : Claudio Kuenzler 260 | #@ Reason : python3 compatibility 261 | #@--------------------------------------------------- 262 | #@ Date : 20190510 263 | #@ Author : Claudio Kuenzler 264 | #@ Reason : Allow regular expressions from ignore list (-r) 265 | #@--------------------------------------------------- 266 | #@ Date : 20190701 267 | #@ Author : Phil Randal (phil.randal@gmail.com) 268 | #@ Reason : Fix lookup of warranty info for Dell (again) 269 | #@--------------------------------------------------- 270 | #@ Date : 20200605 271 | #@ Author : Luca Berra 272 | #@ Reason : Add option to ignore chassis intrusion (Supermicro) 273 | #@--------------------------------------------------- 274 | #@ Date : 20200605 275 | #@ Author : Claudio Kuenzler 276 | #@ Reason : Add parameter (-S) for custom SSL/TLS protocol version 277 | #@--------------------------------------------------- 278 | #@ Date : 20200710 279 | #@ Author : Claudio Kuenzler 280 | #@ Reason : Improve missing mandatory parameter error text (issue #47) 281 | #@ Delete temporary openssl config file after use (issue #48) 282 | #@--------------------------------------------------- 283 | #@ Date : 20210809 284 | #@ Author : Claudio Kuenzler 285 | #@ Reason : Fix TLSv1 usage (issue #51) 286 | #@--------------------------------------------------- 287 | #@ Date : 20220509 288 | #@ Author : Marco Markgraf 289 | #@ Reason : Added JSON-output (Zabbix needs it) 290 | #@--------------------------------------------------- 291 | #@ Date : 20221230 292 | #@ Author : Claudio Kuenzler 293 | #@ Reason : Fix bug when missing S/N (issue #68) 294 | #@--------------------------------------------------- 295 | #@ Date : 20241129 296 | #@ Author : Claudio Kuenzler 297 | #@ Reason : Fix pkg_resources deprecation warning 298 | # Remove python2 compatibility 299 | # Remove pywbem 0.7.0 compatibility 300 | #@--------------------------------------------------- 301 | #@ Date : 20250221 302 | #@ Author : Claudio Kuenzler 303 | #@ Reason : Update to newer pywbem exception call, catch HTTPError 304 | #@ Attn : Requires 'packaging' Python module from now on! 305 | #@--------------------------------------------------- 306 | 307 | import sys 308 | import time 309 | import pywbem 310 | import re 311 | import json 312 | from optparse import OptionParser,OptionGroup 313 | from packaging.version import Version 314 | 315 | version = '20250221' 316 | 317 | NS = 'root/cimv2' 318 | hosturl = '' 319 | 320 | # define classes to check 'OperationStatus' instance 321 | ClassesToCheck = [ 322 | 'OMC_SMASHFirmwareIdentity', 323 | 'CIM_Chassis', 324 | 'CIM_Card', 325 | 'CIM_ComputerSystem', 326 | 'CIM_NumericSensor', 327 | 'CIM_Memory', 328 | 'CIM_Processor', 329 | 'CIM_RecordLog', 330 | 'OMC_DiscreteSensor', 331 | 'OMC_Fan', 332 | 'OMC_PowerSupply', 333 | 'VMware_StorageExtent', 334 | 'VMware_Controller', 335 | 'VMware_StorageVolume', 336 | 'VMware_Battery', 337 | 'VMware_SASSATAPort' 338 | ] 339 | 340 | sensor_Type = { 341 | 0:'unknown', 342 | 1:'Other', 343 | 2:'Temperature', 344 | 3:'Voltage', 345 | 4:'Current', 346 | 5:'Tachometer', 347 | 6:'Counter', 348 | 7:'Switch', 349 | 8:'Lock', 350 | 9:'Humidity', 351 | 10:'Smoke Detection', 352 | 11:'Presence', 353 | 12:'Air Flow', 354 | 13:'Power Consumption', 355 | 14:'Power Production', 356 | 15:'Pressure', 357 | 16:'Intrusion', 358 | 32768:'DMTF Reserved', 359 | 65535:'Vendor Reserved' 360 | } 361 | 362 | data = [] 363 | xdata = {} 364 | 365 | perf_Prefix = { 366 | 1:'Pow', 367 | 2:'Vol', 368 | 3:'Cur', 369 | 4:'Tem', 370 | 5:'Fan', 371 | 6:'FanP' 372 | } 373 | 374 | 375 | # parameters 376 | 377 | # host name 378 | hostname='' 379 | 380 | # cim port 381 | cimport='' 382 | 383 | # user 384 | user='' 385 | 386 | # password 387 | password='' 388 | 389 | # vendor - possible values are 'unknown', 'auto', 'dell', 'hp', 'ibm', 'intel' 390 | vendor='unknown' 391 | 392 | # verbose 393 | verbose=False 394 | 395 | # output json 396 | format='string' 397 | pretty=False 398 | 399 | # Produce performance data output for nagios 400 | perfdata=False 401 | 402 | # timeout 403 | timeout = 0 404 | 405 | # elements to ignore (full SEL, broken BIOS, etc) 406 | ignore_list=[] 407 | regex_ignore_list=[] 408 | regex=False 409 | 410 | # urlise model and tag numbers (currently only Dell supported, but the code does the right thing for other vendors) 411 | urlise_country='' 412 | 413 | # collect perfdata for each category 414 | get_power = True 415 | get_volts = True 416 | get_current = True 417 | get_temp = True 418 | get_fan = True 419 | get_lcd = True 420 | get_intrusion = True 421 | 422 | # define exit codes 423 | ExitOK = 0 424 | ExitWarning = 1 425 | ExitCritical = 2 426 | ExitUnknown = 3 427 | 428 | # Special handling for blade servers 429 | isblade = "no" 430 | 431 | def dell_country(country): 432 | if country == 'at': # Austria 433 | return 'at/de/' 434 | if country == 'be': # Belgium 435 | return 'be/nl/' 436 | if country == 'cz': # Czech Republic 437 | return 'cz/cs/' 438 | if country == 'de': # Germany 439 | return 'de/de/' 440 | if country == 'dk': # Denmark 441 | return 'dk/da/' 442 | if country == 'es': # Spain 443 | return 'es/es/' 444 | if country == 'fi': # Finland 445 | return 'fi/fi/' 446 | if country == 'fr': # France 447 | return 'fr/fr/' 448 | if country == 'gr': # Greece 449 | return 'gr/en/' 450 | if country == 'it': # Italy 451 | return 'it/it/' 452 | if country == 'il': # Israel 453 | return 'il/en/' 454 | if country == 'me': # Middle East 455 | return 'me/en/' 456 | if country == 'no': # Norway 457 | return 'no/no/' 458 | if country == 'nl': # The Netherlands 459 | return 'nl/nl/' 460 | if country == 'pl': # Poland 461 | return 'pl/pl/' 462 | if country == 'pt': # Portugal 463 | return 'pt/en/' 464 | if country == 'ru': # Russia 465 | return 'ru/ru/' 466 | if country == 'se': # Sweden 467 | return 'se/sv/' 468 | if country == 'uk': # United Kingdom 469 | return 'uk/en/' 470 | if country == 'za': # South Africa 471 | return 'za/en/' 472 | if country == 'br': # Brazil 473 | return 'br/pt/' 474 | if country == 'ca': # Canada 475 | return 'ca/en/' 476 | if country == 'mx': # Mexico 477 | return 'mx/es/' 478 | if country == 'us': # United States 479 | return 'us/en/' 480 | if country == 'au': # Australia 481 | return 'au/en/' 482 | if country == 'cn': # China 483 | return 'cn/zh/' 484 | if country == 'in': # India 485 | return 'in/en/' 486 | # default 487 | return 'en/us/' 488 | 489 | def urlised_server_info(vendor, country, server_info): 490 | #server_inf = server_info 491 | if vendor == 'dell' : 492 | # Dell support URLs (idea and tables borrowed from check_openmanage) 493 | du = 'http://www.dell.com/support/home/' + dell_country(country) + '04/product-support/product/poweredge-' 494 | if (server_info is not None) : 495 | p=re.match('(.*)PowerEdge (.*) (.*)',server_info) 496 | if (p is not None) : 497 | md=p.group(2) 498 | if md == 'R210 II': 499 | md='r210-2' 500 | md=md.lower() 501 | server_info = p.group(1) + 'PowerEdge ' + p.group(2)+' ' + p.group(3) 502 | elif vendor == 'hp': 503 | return server_info 504 | elif vendor == 'ibm': 505 | return server_info 506 | elif vendor == 'intel': 507 | return server_info 508 | 509 | return server_info 510 | 511 | # ---------------------------------------------------------------------- 512 | 513 | def system_tag_url(vendor,country): 514 | if vendor == 'dell': 515 | # Dell support sites 516 | supportsite = 'http://www.dell.com/support/home/' 517 | dellsuffix = '19/product-support/servicetag/' 518 | 519 | # warranty URLs for different country codes 520 | return supportsite + dell_country(country) + dellsuffix 521 | # elif vendor == 'hp': 522 | # elif vendor == 'ibm': 523 | # elif vendor == 'intel': 524 | 525 | return '' 526 | 527 | # ---------------------------------------------------------------------- 528 | 529 | def urlised_serialnumber(vendor,country,SerialNumber): 530 | if SerialNumber is not None : 531 | tu = system_tag_url(vendor,country) 532 | if tu != '' : 533 | SerialNumber = '' + SerialNumber + '' 534 | return SerialNumber 535 | 536 | # ---------------------------------------------------------------------- 537 | 538 | def verboseoutput(message) : 539 | if verbose: 540 | print(time.strftime("%Y%m%d %H:%M:%S"), message) 541 | 542 | # ---------------------------------------------------------------------- 543 | 544 | def xdataprint(): 545 | if format == 'json' and not pretty: 546 | print(json.dumps(xdata, sort_keys=True)) 547 | if format == 'json' and pretty: 548 | print(json.dumps(xdata, sort_keys=True, indent=4)) 549 | 550 | # ---------------------------------------------------------------------- 551 | 552 | def getopts() : 553 | global hosturl,hostname,cimport,sslproto,user,password,vendor,verbose,perfdata,urlise_country,timeout,ignore_list,regex,get_power,get_volts,get_current,get_temp,get_fan,get_lcd,get_intrusion,format,pretty 554 | usage = "usage: %prog -H hostname -U username -P password [-C port -S proto -V vendor -v -p -I XX -i list,list -r]\n" \ 555 | "example: %prog -H hostname -U root -P password -C 5989 -V auto -I uk\n\n" \ 556 | "or, verbosely:\n\n" \ 557 | "usage: %prog --host=hostname --user=username --pass=password [--cimport=port --sslproto=version --vendor=system --verbose --perfdata --html=XX --format=json --pretty]\n" 558 | 559 | parser = OptionParser(usage=usage, version="%prog "+version) 560 | group1 = OptionGroup(parser, 'Mandatory parameters') 561 | group2 = OptionGroup(parser, 'Optional parameters') 562 | 563 | group1.add_option("-H", "--host", dest="host", help="connect to HOST", metavar="HOST") 564 | group1.add_option("-U", "--user", dest="user", help="user to connect as", metavar="USER") 565 | group1.add_option("-P", "--pass", dest="password", \ 566 | help="password, if password matches file:, first line of given file will be used as password", metavar="PASS") 567 | 568 | group2.add_option("-C", "--cimport", dest="cimport", help="CIM port (default 5989)", metavar="CIMPORT") 569 | group2.add_option("-S", "--sslproto", dest="sslproto", help="SSL/TLS protocol version to overwrite system default: SSLv2, SSLv3, TLSv1, TLSv1.1, TLSv1.2, TLSv1.3", metavar="SSLPROTO") 570 | group2.add_option("-V", "--vendor", dest="vendor", help="Vendor code: auto, dell, hp, ibm, intel, or unknown (default)", \ 571 | metavar="VENDOR", type='choice', choices=['auto','dell','hp','ibm','intel','unknown'],default="unknown") 572 | group2.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, \ 573 | help="print status messages to stdout (default is to be quiet)") 574 | group2.add_option("-p", "--perfdata", action="store_true", dest="perfdata", default=False, \ 575 | help="collect performance data for pnp4nagios (default is not to)") 576 | group2.add_option("-I", "--html", dest="urlise_country", default="", \ 577 | help="generate html links for country XX (default is not to)", metavar="XX") 578 | group2.add_option("-t", "--timeout", action="store", type="int", dest="timeout", default=0, \ 579 | help="timeout in seconds - no effect on Windows (default = no timeout)") 580 | group2.add_option("-i", "--ignore", action="store", type="string", dest="ignore", default="", \ 581 | help="comma-separated list of elements to ignore") 582 | group2.add_option("-r", "--regex", action="store_true", dest="regex", default=False, \ 583 | help="allow regular expression lookup of ignore list") 584 | group2.add_option("--no-power", action="store_false", dest="get_power", default=True, \ 585 | help="don't collect power performance data") 586 | group2.add_option("--no-volts", action="store_false", dest="get_volts", default=True, \ 587 | help="don't collect voltage performance data") 588 | group2.add_option("--no-current", action="store_false", dest="get_current", default=True, \ 589 | help="don't collect current performance data") 590 | group2.add_option("--no-temp", action="store_false", dest="get_temp", default=True, \ 591 | help="don't collect temperature performance data") 592 | group2.add_option("--no-fan", action="store_false", dest="get_fan", default=True, \ 593 | help="don't collect fan performance data") 594 | group2.add_option("--no-lcd", action="store_false", dest="get_lcd", default=True, \ 595 | help="don't collect lcd/front display status") 596 | group2.add_option("--no-intrusion", action="store_false", dest="get_intrusion", default=True, \ 597 | help="don't collect chassis intrusion status") 598 | group2.add_option("--format", dest="format", help="'string' (default) or 'json'", \ 599 | metavar="FORMAT", type='choice', choices=['string','json'],default="string") 600 | group2.add_option("--pretty", action="store_true", dest="pretty", default=False, \ 601 | help="return data as a pretty-printed json-array") 602 | 603 | parser.add_option_group(group1) 604 | parser.add_option_group(group2) 605 | 606 | # check input arguments 607 | if len(sys.argv) < 2: 608 | print("no parameters specified\n") 609 | parser.print_help() 610 | sys.exit(-1) 611 | # if first argument starts with 'https://' we have old-style parameters, so handle in old way 612 | if re.match("https://",sys.argv[1]): 613 | # check input arguments 614 | if len(sys.argv) < 5: 615 | print("too few parameters\n") 616 | parser.print_help() 617 | sys.exit(-1) 618 | if len(sys.argv) > 5 : 619 | if sys.argv[5] == "verbose" : 620 | verbose = True 621 | hosturl = sys.argv[1] 622 | user = sys.argv[2] 623 | password = sys.argv[3] 624 | vendor = sys.argv[4] 625 | else: 626 | # we're dealing with new-style parameters, so go get them! 627 | (options, args) = parser.parse_args() 628 | 629 | # Making sure all mandatory options appeared. 630 | mandatories = ['host', 'user', 'password'] 631 | for m in mandatories: 632 | if not options.__dict__[m]: 633 | print("mandatory option '" + m + "' not defined. read usage in help.\n") 634 | parser.print_help() 635 | sys.exit(-1) 636 | 637 | hostname=options.host.lower() 638 | # if user has put "https://" in front of hostname out of habit, do the right thing 639 | # hosturl will end up as https://hostname 640 | if re.match('^https://',hostname): 641 | hosturl = hostname 642 | else: 643 | hosturl = 'https://' + hostname 644 | 645 | user=options.user 646 | password=options.password 647 | cimport=options.cimport 648 | ignore_list=options.ignore.split(',') 649 | format=options.format 650 | pretty=options.pretty 651 | perfdata=options.perfdata 652 | regex=options.regex 653 | sslproto=options.sslproto 654 | timeout=options.timeout 655 | urlise_country=options.urlise_country.lower() 656 | vendor=options.vendor.lower() 657 | verbose=options.verbose 658 | get_power=options.get_power 659 | get_volts=options.get_volts 660 | get_current=options.get_current 661 | get_temp=options.get_temp 662 | get_fan=options.get_fan 663 | get_lcd=options.get_lcd 664 | get_intrusion=options.get_intrusion 665 | 666 | # if user or password starts with 'file:', use the first string in file as user, second as password 667 | if (re.match('^file:', user) or re.match('^file:', password)): 668 | if re.match('^file:', user): 669 | filextract = re.sub('^file:', '', user) 670 | filename = open(filextract, 'r') 671 | filetext = filename.readline().split() 672 | user = filetext[0] 673 | password = filetext[1] 674 | filename.close() 675 | elif re.match('^file:', password): 676 | filextract = re.sub('^file:', '', password) 677 | filename = open(filextract, 'r') 678 | filetext = filename.readline().split() 679 | password = filetext[0] 680 | filename.close() 681 | 682 | # ---------------------------------------------------------------------- 683 | 684 | getopts() 685 | 686 | # if running on Windows, don't use timeouts and signal.alarm 687 | on_windows = True 688 | os_platform = sys.platform 689 | if os_platform != "win32": 690 | on_windows = False 691 | import signal 692 | def handler(signum, frame): 693 | print('UNKNOWN: Execution time too long!') 694 | sys.exit(ExitUnknown) 695 | 696 | # Use non-default CIM port 697 | if cimport: 698 | verboseoutput("Using manually defined CIM port "+cimport) 699 | hosturl += ':'+cimport 700 | 701 | # Use non-default SSL protocol version 702 | if sslproto: 703 | verboseoutput("Using non-default SSL protocol: "+sslproto) 704 | allowed_protos = ["SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"] 705 | if any(proto.lower() == sslproto.lower() for proto in allowed_protos): 706 | import os 707 | sslconfpath = '/tmp/'+hostname+'_openssl.conf' 708 | verboseoutput("Creating OpenSSL config file: "+sslconfpath) 709 | try: 710 | with open(sslconfpath, 'w') as config_file: 711 | config_file.write("openssl_conf = openssl_init\n[openssl_init]\nssl_conf = ssl_configuration\n[ssl_configuration]\nsystem_default = tls_system_default\n[tls_system_default]\nMinProtocol = "+sslproto+"\n") 712 | except Exception as e: 713 | print('CRITICAL: An error occured while trying to write ssl config file: %s (%s)' % (sslconfpath, e)) 714 | sys.exit(ExitCritical) 715 | os.environ["OPENSSL_CONF"] = sslconfpath 716 | else: 717 | print('CRITICAL: Invalid SSL protocol version given!') 718 | sys.exit(ExitCritical) 719 | 720 | # Append lcd related elements to ignore list if --no-lcd was used 721 | verboseoutput("LCD Status: %s" % get_lcd) 722 | if not get_lcd: 723 | ignore_list.append("System Board 1 LCD Cable Pres 0: Connected") 724 | ignore_list.append("System Board 1 VGA Cable Pres 0: Connected") 725 | ignore_list.append("Front Panel Board 1 FP LCD Cable 0: Connected") 726 | ignore_list.append("Front Panel Board 1 FP LCD Cable 0: Config Error") 727 | 728 | # Append chassis intrusion related elements to ignore list if --no-intrusion was used 729 | verboseoutput("Chassis Intrusion Status: %s" % get_intrusion) 730 | if not get_intrusion: 731 | ignore_list.append("System Chassis 1 Chassis Intru: General Chassis intrusion") 732 | ignore_list.append("System Chassis 1 Chassis Intru: Drive Bay intrusion") 733 | ignore_list.append("System Chassis 1 Chassis Intru: I/O Card area intrusion") 734 | ignore_list.append("System Chassis 1 Chassis Intru: Processor area intrusion") 735 | ignore_list.append("System Chassis 1 Chassis Intru: System unplugged from LAN") 736 | ignore_list.append("System Chassis 1 Chassis Intru: Unauthorized dock") 737 | ignore_list.append("System Chassis 1 Chassis Intru: FAN area intrusion") 738 | ignore_list.append("System Chassis 1 Chassis Intru: Unknown") 739 | 740 | # connection to host 741 | pywbemversion = pywbem.__version__ 742 | verboseoutput("Found pywbem version "+pywbemversion) 743 | verboseoutput("Connection to "+hosturl) 744 | wbemclient = pywbem.WBEMConnection(hosturl, (user,password), NS, no_verification=True) 745 | 746 | # Backward compatibility for older pywbem exceptions, big thanks to Claire M.! 747 | if Version(pywbemversion) >= Version("1.0.0"): 748 | verboseoutput("pywbem is 1.0.0 or newer") 749 | import pywbem._cim_operations as PywbemCimOperations 750 | import pywbem._cim_http as PywbemCimHttp 751 | import pywbem._exceptions as PywbemExceptions 752 | else: 753 | verboseoutput("pywbem is older than 1.0.0") 754 | import pywbem.cim_operations as PywbemCimOperations 755 | import pywbem.cim_http as PywbemCimHttp 756 | import pywbem.exceptions as PywbemExceptions 757 | 758 | # Add a timeout for the script. When using with Nagios, the Nagios timeout cannot be < than plugin timeout. 759 | if on_windows == False and timeout > 0: 760 | signal.signal(signal.SIGALRM, handler) 761 | signal.alarm(timeout) 762 | 763 | # run the check for each defined class 764 | GlobalStatus = ExitUnknown 765 | server_info = "" 766 | bios_info = "" 767 | SerialNumber = "" 768 | ExitMsg = "" 769 | 770 | # if vendor is specified as 'auto', try to get vendor from CIM 771 | # note: the default vendor is 'unknown' 772 | if vendor=='auto': 773 | try: 774 | c=wbemclient.EnumerateInstances('CIM_Chassis') 775 | except PywbemCimOperations.CIMError as args: 776 | if ( args[1].find('Socket error') >= 0 ): 777 | print("UNKNOWN: {}".format(args)) 778 | sys.exit (ExitUnknown) 779 | elif ( args[1].find('ThreadPool --- Failed to enqueue request') >= 0 ): 780 | print("UNKNOWN: {}".format(args)) 781 | sys.exit (ExitUnknown) 782 | else: 783 | verboseoutput("Unknown CIM Error: %s" % args) 784 | except PywbemExceptions.ConnectionError as args: 785 | GlobalStatus = ExitUnknown 786 | print("UNKNOWN: {}".format(args)) 787 | sys.exit (GlobalStatus) 788 | except PywbemExceptions.HTTPError as args: 789 | GlobalStatus = ExitUnknown 790 | print("UNKNOWN: {}".format(args)) 791 | sys.exit (GlobalStatus) 792 | except PywbemCimHttp.AuthError as arg: 793 | verboseoutput("Global exit set to UNKNOWN") 794 | GlobalStatus = ExitUnknown 795 | print("UNKNOWN: Authentication Error") 796 | sys.exit (GlobalStatus) 797 | else: 798 | man=c[0][u'Manufacturer'] 799 | if re.match("Dell",man): 800 | vendor="dell" 801 | elif re.match("HP",man): 802 | vendor="hp" 803 | elif re.match("IBM",man): 804 | vendor="ibm" 805 | elif re.match("Intel",man): 806 | vendor="intel" 807 | else: 808 | vendor='unknown' 809 | 810 | for classe in ClassesToCheck : 811 | verboseoutput("Check classe "+classe) 812 | try: 813 | instance_list = wbemclient.EnumerateInstances(classe) 814 | except PywbemCimOperations.CIMError as args: 815 | if ( args[1].find('Socket error') >= 0 ): 816 | print("UNKNOWN: {}".format(args)) 817 | sys.exit (ExitUnknown) 818 | elif ( args[1].find('ThreadPool --- Failed to enqueue request') >= 0 ): 819 | print("UNKNOWN: {}".format(args)) 820 | sys.exit (ExitUnknown) 821 | else: 822 | verboseoutput("Unknown CIM Error: %s" % args) 823 | except PywbemExceptions.ConnectionError as args: 824 | GlobalStatus = ExitUnknown 825 | print("UNKNOWN: {}".format(args)) 826 | sys.exit (GlobalStatus) 827 | except PywbemExceptions.HTTPError as args: 828 | GlobalStatus = ExitUnknown 829 | print("UNKNOWN: {}".format(args)) 830 | sys.exit (GlobalStatus) 831 | except PywbemCimHttp.AuthError as arg: 832 | verboseoutput("Global exit set to UNKNOWN") 833 | GlobalStatus = ExitUnknown 834 | print("UNKNOWN: Authentication Error") 835 | sys.exit (GlobalStatus) 836 | else: 837 | # GlobalStatus = ExitOK #ARR 838 | for instance in instance_list : 839 | elementName = instance['ElementName'] 840 | if elementName is None : 841 | elementName = 'Unknown' 842 | elementNameValue = elementName 843 | verboseoutput(" Element Name = "+elementName) 844 | 845 | # Ignore element if we don't want it 846 | if (regex == True) and (len(ignore_list) > 0) : 847 | for ignore in ignore_list : 848 | if re.search(ignore, elementName, re.IGNORECASE) : 849 | verboseoutput(" (ignored through regex)") 850 | regex_ignore_list.append(elementName) 851 | 852 | if (elementName in ignore_list) or (elementName in regex_ignore_list) : 853 | verboseoutput(" (ignored)") 854 | continue 855 | 856 | # BIOS & Server info 857 | if elementName == 'System BIOS' : 858 | bios_info = instance[u'Name'] + ': ' \ 859 | + instance[u'VersionString'] + ' ' \ 860 | + str(instance[u'ReleaseDate'].datetime.date()) 861 | verboseoutput(" VersionString = "+instance[u'VersionString']) 862 | 863 | xdata['Bios Info'] = bios_info 864 | 865 | elif elementName == 'Chassis' : 866 | man = instance[u'Manufacturer'] 867 | if man is None : 868 | man = 'Unknown Manufacturer' 869 | verboseoutput(" Manufacturer = "+man) 870 | SerialNumber = instance[u'SerialNumber'] 871 | SerialChassis = instance[u'SerialNumber'] 872 | if SerialNumber: 873 | verboseoutput(" SerialNumber = "+SerialNumber) 874 | server_info = man + ' ' 875 | if vendor != 'intel': 876 | model = instance[u'Model'] 877 | if model: 878 | verboseoutput(" Model = "+model) 879 | server_info += model 880 | 881 | elif elementName == 'Server Blade' : 882 | SerialNumber = instance[u'SerialNumber'] 883 | if SerialNumber: 884 | verboseoutput(" SerialNumber = "+SerialNumber) 885 | isblade = "yes" 886 | 887 | xdata['SerialNumber'] = SerialNumber 888 | 889 | # Report detail of Numeric Sensors and generate nagios perfdata 890 | 891 | if classe == "CIM_NumericSensor" : 892 | sensorType = instance[u'sensorType'] 893 | sensStr = sensor_Type.get(sensorType,"Unknown") 894 | if sensorType: 895 | verboseoutput(" sensorType = %d - %s" % (sensorType,sensStr)) 896 | units = instance[u'BaseUnits'] 897 | if units: 898 | verboseoutput(" BaseUnits = %d" % units) 899 | # grab some of these values for Nagios performance data 900 | scale = 10**instance[u'UnitModifier'] 901 | verboseoutput(" Scaled by = %f " % scale) 902 | cr = int(instance[u'CurrentReading'])*scale 903 | verboseoutput(" Current Reading = %f" % cr) 904 | elementNameValue = "%s: %g" % (elementName,cr) 905 | ltnc = 0 906 | utnc = 0 907 | ltc = 0 908 | utc = 0 909 | if instance[u'LowerThresholdNonCritical'] is not None: 910 | ltnc = instance[u'LowerThresholdNonCritical']*scale 911 | verboseoutput(" Lower Threshold Non Critical = %f" % ltnc) 912 | if instance[u'UpperThresholdNonCritical'] is not None: 913 | utnc = instance[u'UpperThresholdNonCritical']*scale 914 | verboseoutput(" Upper Threshold Non Critical = %f" % utnc) 915 | if instance[u'LowerThresholdCritical'] is not None: 916 | ltc = instance[u'LowerThresholdCritical']*scale 917 | verboseoutput(" Lower Threshold Critical = %f" % ltc) 918 | if instance[u'UpperThresholdCritical'] is not None: 919 | utc = instance[u'UpperThresholdCritical']*scale 920 | verboseoutput(" Upper Threshold Critical = %f" % utc) 921 | # 922 | if perfdata: 923 | perf_el = elementName.replace(' ','_') 924 | 925 | # Power and Current 926 | if sensorType == 4: # Current or Power Consumption 927 | if units == 7: # Watts 928 | if get_power: 929 | data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),1) ) 930 | xdata[perf_el] = { 'Unit': 'Watt', 'Value': cr, 'warn' : utnc, 'crit': utc } 931 | elif units == 6: # Current 932 | if get_current: 933 | data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),3) ) 934 | xdata[perf_el] = { 'Unit': 'Ampere', 'Value': cr, 'warn' : utnc, 'crit': utc } 935 | 936 | # PSU Voltage 937 | elif sensorType == 3: # Voltage 938 | if get_volts: 939 | data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),2) ) 940 | xdata[perf_el] = { 'Unit': 'Volt', 'Value': cr, 'warn' : utnc, 'crit': utc } 941 | 942 | # Temperatures 943 | elif sensorType == 2: # Temperature 944 | if get_temp: 945 | data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),4) ) 946 | xdata[perf_el] = { 'Value': cr, 'warn' : utnc, 'crit': utc } 947 | 948 | # Fan speeds 949 | elif sensorType == 5: # Tachometer 950 | if get_fan: 951 | if units == 65: # percentage 952 | data.append( ("%s=%g%%;%g;%g " % (perf_el, cr, utnc, utc),6) ) 953 | xdata[perf_el] = { 'Unit': '%', 'Value': cr, 'warn' : utnc, 'crit': utc } 954 | else: 955 | data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),5) ) 956 | xdata[perf_el] = { 'Value': cr, 'warn' : utnc, 'crit': utc } 957 | 958 | elif classe == "CIM_Processor" : 959 | verboseoutput(" Family = %d" % instance['Family']) 960 | verboseoutput(" CurrentClockSpeed = %dMHz" % instance['CurrentClockSpeed']) 961 | 962 | # HP Check 963 | if vendor == "hp" : 964 | if instance['HealthState'] is not None : 965 | elementStatus = instance['HealthState'] 966 | verboseoutput(" Element HealthState = %d" % elementStatus) 967 | interpretStatus = { 968 | 0 : ExitOK, # Unknown 969 | 5 : ExitOK, # OK 970 | 10 : ExitWarning, # Degraded 971 | 15 : ExitWarning, # Minor 972 | 20 : ExitCritical, # Major 973 | 25 : ExitCritical, # Critical 974 | 30 : ExitCritical, # Non-recoverable Error 975 | }[elementStatus] 976 | if (interpretStatus == ExitCritical) : 977 | verboseoutput("Global exit set to CRITICAL") 978 | GlobalStatus = ExitCritical 979 | ExitMsg += " CRITICAL : %s " % elementNameValue 980 | if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) : 981 | verboseoutput("Global exit set to WARNING") 982 | GlobalStatus = ExitWarning 983 | ExitMsg += " WARNING : %s " % elementNameValue 984 | # Added the following for when GlobalStatus is ExitCritical and a warning is detected 985 | # This way the ExitMsg gets added but GlobalStatus isn't changed 986 | if (interpretStatus == ExitWarning and GlobalStatus == ExitCritical) : # ARR 987 | ExitMsg += " WARNING : %s " % elementNameValue #ARR 988 | # Added the following so that GlobalStatus gets set to OK if there's no warning or critical 989 | if (interpretStatus == ExitOK and GlobalStatus != ExitWarning and GlobalStatus != ExitCritical) : #ARR 990 | GlobalStatus = ExitOK #ARR 991 | 992 | 993 | 994 | # Dell, Intel, IBM and unknown hardware check 995 | elif (vendor == "dell" or vendor == "intel" or vendor == "ibm" or vendor=="unknown") : 996 | # Added 20121027 As long as Dell doesnt correct these CIM elements return code we have to ignore it 997 | ignore_list.append("System Board 1 Riser Config Err 0: Connected") 998 | ignore_list.append("Add-in Card 4 PEM Presence 0: Connected") 999 | if instance['OperationalStatus'] is not None : 1000 | elementStatus = instance['OperationalStatus'][0] 1001 | verboseoutput(" Element Op Status = %d" % elementStatus) 1002 | interpretStatus = { 1003 | 0 : ExitOK, # Unknown 1004 | 1 : ExitCritical, # Other 1005 | 2 : ExitOK, # OK 1006 | 3 : ExitWarning, # Degraded 1007 | 4 : ExitWarning, # Stressed 1008 | 5 : ExitWarning, # Predictive Failure 1009 | 6 : ExitCritical, # Error 1010 | 7 : ExitCritical, # Non-Recoverable Error 1011 | 8 : ExitWarning, # Starting 1012 | 9 : ExitWarning, # Stopping 1013 | 10 : ExitCritical, # Stopped 1014 | 11 : ExitOK, # In Service 1015 | 12 : ExitWarning, # No Contact 1016 | 13 : ExitCritical, # Lost Communication 1017 | 14 : ExitCritical, # Aborted 1018 | 15 : ExitOK, # Dormant 1019 | 16 : ExitCritical, # Supporting Entity in Error 1020 | 17 : ExitOK, # Completed 1021 | 18 : ExitOK, # Power Mode 1022 | 19 : ExitOK, # DMTF Reserved 1023 | 20 : ExitOK # Vendor Reserved 1024 | }[elementStatus] 1025 | if (interpretStatus == ExitCritical) : 1026 | verboseoutput("Global exit set to CRITICAL") 1027 | GlobalStatus = ExitCritical 1028 | ExitMsg += " CRITICAL : %s " % elementNameValue 1029 | if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) : 1030 | verboseoutput("Global exit set to WARNING") 1031 | GlobalStatus = ExitWarning 1032 | ExitMsg += " WARNING : %s " % elementNameValue 1033 | # Added same logic as in 20100702 here, otherwise Dell servers would return UNKNOWN instead of OK 1034 | if (interpretStatus == ExitWarning and GlobalStatus == ExitCritical) : # ARR 1035 | ExitMsg += " WARNING : %s " % elementNameValue #ARR 1036 | if (interpretStatus == ExitOK and GlobalStatus != ExitWarning and GlobalStatus != ExitCritical) : #ARR 1037 | GlobalStatus = ExitOK #ARR 1038 | if elementName == 'Server Blade' : 1039 | if SerialNumber : 1040 | if SerialNumber.find(".") != -1 : 1041 | SerialNumber = SerialNumber.split('.')[1] 1042 | 1043 | 1044 | # Munge the ouptput to give links to documentation and warranty info 1045 | if (urlise_country != '') : 1046 | SerialNumber = urlised_serialnumber(vendor,urlise_country,SerialNumber) 1047 | server_info = urlised_server_info(vendor,urlise_country,server_info) 1048 | 1049 | # If this is a blade server, also output chassis serial number as additional info 1050 | if (isblade == "yes") : 1051 | SerialNumber += " Chassis S/N: %s " % (SerialChassis) 1052 | xdata['ChassisSerialNumber'] = SerialChassis 1053 | 1054 | # Output performance data 1055 | perf = '|' 1056 | if perfdata: 1057 | sdata=[] 1058 | ctr=[0,0,0,0,0,0,0] 1059 | # sort the data so we always get perfdata in the right order 1060 | # we make no assumptions about the order in which CIM returns data 1061 | # first sort by element name (effectively) and insert sequence numbers 1062 | for p in sorted(data): 1063 | p1 = p[1] 1064 | sdata.append( ("P%d%s_%d_%s") % (p1,perf_Prefix[p1], ctr[p1], p[0]) ) 1065 | ctr[p1] += 1 1066 | # then sort perfdata into groups and output perfdata string 1067 | for p in sorted(sdata): 1068 | perf += p 1069 | 1070 | # sanitise perfdata - don't output "|" if nothing to report 1071 | if perf == '|': 1072 | perf = '' 1073 | 1074 | # Cleanup temporary openssl config 1075 | if sslproto: 1076 | os.remove(sslconfpath) 1077 | 1078 | xdata['GlobalStatus'] = GlobalStatus 1079 | 1080 | if GlobalStatus == ExitOK : 1081 | if format == 'string': 1082 | print("OK - Server: %s s/n: %s %s%s" % (server_info, SerialNumber, bios_info, perf)) 1083 | else: 1084 | xdataprint() 1085 | 1086 | elif GlobalStatus == ExitUnknown : 1087 | if format == 'string': 1088 | print("UNKNOWN: %s" % (ExitMsg)) #ARR 1089 | else: 1090 | xdataprint() 1091 | 1092 | else: 1093 | if format == 'string': 1094 | print("%s - Server: %s %s %s%s" % (ExitMsg, server_info, 's/n: ' + SerialNumber, bios_info, perf)) 1095 | else: 1096 | xdataprint() 1097 | 1098 | sys.exit (GlobalStatus) 1099 | --------------------------------------------------------------------------------