├── .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 |
--------------------------------------------------------------------------------