1
0
mirror of https://github.com/Napsty/check_esxi_hardware.git synced 2026-02-06 15:15:20 +01:00

54 Commits

Author SHA1 Message Date
Claudio Kuenzler
8b6917f0ca Improve pywbem exception handling (#75)
This PR improves exception handling of the `pywbem` module, which uses different exception calls before and starting with pywbem 1.0.0. 

This allows correct exception handling with older `pywbem` versions (< 1.0.0) and newer versions.

The PR also adds exception handling for HTTP exception, when a HTTP server responds on the requested host and port, but it's not an ESXi CIM server.
2025-02-21 11:08:04 +01:00
Napsty
7b02ddad1f Version bump 2024-11-29 08:23:45 +01:00
Claudio Kuenzler
29b32b785e Remove python2 and pywbem 0.7.0 compatibility (#73)
Remove python2 and pywbem 0.7.0 compatibility
2024-11-29 08:20:00 +01:00
Claudio Kuenzler
7aaaae4337 Fix bug when missing serial number from CIM elements (#69) 2022-12-30 08:57:39 +01:00
Claudio Kuenzler
8a99d8aace Update bug_report.md 2022-12-30 08:49:49 +01:00
Claudio Kuenzler
141fdfb500 Bump version 2022-07-08 06:49:52 +02:00
Marco Markgraf
a548d358e0 added json-output (#65)
added json-output
2022-07-08 06:44:09 +02:00
Claudio Kuenzler
2b6511a60a Update issue templates 2021-11-17 06:53:02 +01:00
Claudio Kuenzler
d154bc9f1b Update README.md 2021-11-11 16:03:31 +01:00
Claudio Kuenzler
e4f406a309 Fix TLSv1 usage (#57)
* Fixing TLSv1 protocol version

* Update changelog and version
2021-08-09 14:23:32 +02:00
Claudio Kuenzler
d8f9fa1c82 Fix reported issues #47 and #48 (#49) 2020-07-10 07:30:43 +02:00
Claudio Kuenzler
274dceee74 Allow to overwrite system defaults for SSL protocol (#46)
* Add parameter (-S) for custom SSL/TLS protocol version

* Add parameter (-S) for custom SSL/TLS protocol version

* Add parameter (-S) for custom SSL/TLS protocol version
2020-06-05 16:12:13 +02:00
lberra
bf4ec05979 Add option to ignore chassis intrusion (Supermicro) (#42) 2020-06-05 10:18:51 +02:00
Claudio Kuenzler
489da3de76 Github actions: pywbem
Github actions: fix names

Github actions: Other fixes

Github actions: Other fixes

Github actions: Debugging

Github actions: Debugging and remove pywbem 0.9 from pip install

Github actions: Add PYTHONPATH env

Github actions: Debugging

Github actions: Debugging

Github actions: Debugging

Github actions: PIP modules dont seem to work

Github actions: PIP modules dont seem to work

Github actions: PIP modules dont seem to work
2019-11-26 09:58:53 +01:00
Claudio Kuenzler
bcf607b2cb Added Github actions for automated syntax validation 2019-11-26 07:45:32 +01:00
Phil Randal
ff3228b804 Fix dell support links (#38) 2019-07-01 10:39:59 +02:00
Claudio Kuenzler
08cf10de24 Regex (#36)
* Allow regular expressions from ignore list (-r)

* Allow regular expressions from ignore list (-r)
2019-05-10 10:00:50 +02:00
Claudio Kuenzler
df56d7373d Merge pull request #30 from Napsty/python3
Python3 compatibility
2018-10-02 06:58:00 +02:00
Claudio Kuenzler
6aebd1a4bb Added future statement for print function 2018-10-01 12:39:10 +02:00
Claudio Kuenzler
96aced6377 Python3 compatibility 2018-10-01 11:15:59 +02:00
Claudio Kuenzler
9f8b22a393 version bump 2018-04-11 07:35:09 +02:00
Claudio Kuenzler
413dac781b Merge pull request #21 from peternewman/master
Throw an unknown if we can't fetch the data for some reason
2018-04-11 07:33:28 +02:00
Peter Newman
21e0c2b3ed Fix a whitespace issue 2018-04-10 14:37:18 +01:00
Peter Newman
b758ee20df Merge pull request #1 from Napsty/master
Resync with master
2018-04-10 14:36:13 +01:00
Claudio Kuenzler
277206abc7 Merge pull request #28 from Napsty/pywbemversion
Try to use internal pywbem function to determine version
2018-03-29 09:19:41 +02:00
Claudio Kuenzler
bd61850aa5 Try to use internal pywbem function to determine version 2018-03-29 09:15:07 +02:00
Claudio Kuenzler
ccb315d2d7 Merge pull request #23 from Napsty/lcd
Option --no-lcd
2017-09-05 21:08:58 +02:00
Claudio Kuenzler
575c4b47a7 Ready for new version 2017-09-05 21:06:53 +02:00
Claudio Kuenzler
0e62849ffb Define lcd ignore list before checking classes 2017-09-02 11:24:37 +02:00
Claudio Kuenzler
9ea8ba5967 Corected lcd condition 2017-09-02 11:21:31 +02:00
Claudio Kuenzler
7d4a85d0f3 Update check_esxi_hardware.py 2017-09-01 16:49:19 +02:00
Claudio Kuenzler
ff33e0a7b8 Debugging --no-lcd 2017-09-01 07:05:54 +02:00
Claudio Kuenzler
cec24c19bd Added --no-lcd option 2017-08-31 21:19:34 +02:00
Peter Newman
db0ffb7a05 Throw another unknown if we can't fetch the data elsewhere for some reason 2017-01-25 10:48:19 +00:00
Peter Newman
3566f03ecb SPaG 2017-01-24 18:06:35 +00:00
Peter Newman
ecf93a695e Throw an unknown if we can't fetch the data for some reason 2017-01-24 17:57:28 +00:00
Claudio Kuenzler
89f9e505c3 Added support for pywbem 0.9.x (and upcoming releases) 2016-10-13 15:59:19 +02:00
Claudio Kuenzler
82a4156e34 Bump version, edit changelog 2016-05-31 07:28:55 +02:00
Claudio Kuenzler
1f7c3b5927 Merge pull request #17 from Napsty/ports
Add CIM port option, adapt help
2016-05-31 07:27:03 +02:00
Claudio Kuenzler
ced39ad416 Add CIM port option, adapt help 2016-04-14 21:41:31 +02:00
Claudio Kuenzler
ec7db6479d Merge pull request #16 from Napsty/pywbem-versions
Distinguish between pywbem 0.7 and 0.8
2016-04-11 15:21:16 +02:00
Claudio Kuenzler
496ae6cf31 Distinguish between pywbem 0.7 and 0.8 2016-04-11 15:19:37 +02:00
Claudio Kuenzler
d8a95f864f Merge pull request #14 from stefaro/patch-1
Merge pull request from Stefan Roos. Declare hosturl variable. Remove sensor_value variable (it's not used).
2016-04-07 13:51:36 +02:00
Stefan Roos
156016869a Update check_esxi_hardware.py
Removed some modifications.
2016-04-07 10:11:40 +03:00
stefaro
03f8ca8335 Minor changes and cleanup
Removed unused sensor_value variable and string import.
Removed no SSL tryout on pywbem v 0.0.7 (default version doesn't have no_verification).
Added global hosturl variable declaration after imports.
Added "Front Panel Board 1 FP LCD Cable 0: Connected" to ignore list (LENOVO System x3550 M5).
2015-11-11 09:57:05 +02:00
Claudio Kuenzler
3727e67609 Exit Unknown instead of Critical for non-essential errors 2015-07-10 09:56:25 +02:00
Claudio Kuenzler
8c147a204e Merge pull request #12 from giner/patch-1
check_esxi_hardware.py: unknown instead of crit
2015-07-10 09:45:02 +02:00
Napsty
0036c441de Change version number for public release 2015-06-26 14:50:10 +02:00
Napsty
c806fb4d90 Change version number for public release 2015-06-26 14:30:42 +02:00
Napsty
6c8a4bfd63 Extended pywbem version compatibility 2015-06-10 12:47:12 +02:00
Stanislav German-Evtushenko
fb038edcda check_esxi_hardware.py: revert last change
1. This change was wrong and inappropriate.
2. The issue I tried to fix was caused by the bug in python library pywbem: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=780264
2015-06-01 19:52:59 +03:00
Napsty
3f97ca6485 Added support for patched pywbem 0.7.0 and new version 0.8.0, handle SSL error exception 2015-05-12 08:45:32 +02:00
Stanislav German-Evtushenko
b3c0ca4cdd check_esxi_hardware.py: force exit on timeout
Replace sys.exit by os._exit to be sure that the script stops on timeout immediately and does not produce false-positive results (usually it comes to WARNING).
2015-04-07 15:56:00 +03:00
Stanislav German-Evtushenko
e3a43a5dd0 check_esxi_hardware.py: unknown instead of crit
Set status to unknown instead of critical for timeouts, authentication errors and such things as those issues are critical for the check itself but not for the service.
2015-03-16 15:16:47 +03:00
4 changed files with 354 additions and 64 deletions

32
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,32 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Before actually creating a new issue**
I confirm, I have read the FAQ (https://www.claudiokuenzler.com/blog/308/check-esxi-hardware-faq-frequently-asked-questions): Y/N
I confirm, I have restarted the CIM server (` /etc/init.d/sfcbd-watchdog restart `) on the ESXi server and the problem remains: Y/N
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
**Describe the bug**
A clear and concise description of what the bug is.
**Show the full plugin output, including the command with -v parameter**
Run the plugin with `-v` parameter and show the full output (including command) here. Obviously obfuscate credentials.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Versions:**
- check_esxi_hardware plugin:
- VMware ESXi:
- pywbem:
- Python:
- Third party tools (Dell OMSA, HP Offline Bundle, etc):
**Additional context**
Add any other context about the problem here.

21
.github/workflows/python3check.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
# @file python3check.yml
---
name: Python3 check
# Trigger the workflow on push or pull request
on: [push, pull_request]
jobs:
python3-pywbem-latest:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install latest pywbem from pip
run: |
sudo apt-get install -qq -yy python3 python3-pip
pip3 install pywbem
- name: Verify python sys.path
run: (echo "import sys"; echo "print(', '.join(sys.path))") | python3
- name: Launch script with --help
run: |
./check_esxi_hardware.py --help

View File

@@ -8,4 +8,8 @@ This is the public git repository for development of the plugin.
Documentation + Production Ready Plugin Documentation + Production Ready Plugin
------------- -------------
Please refer to http://www.claudiokuenzler.com/nagios-plugins/check_esxi_hardware.php Please refer to https://www.claudiokuenzler.com/monitoring-plugins/check_esxi_hardware.php
Compatibility list
-------------
Please check https://www.claudiokuenzler.com/blog/1110/check_esxi_hardware-esxi-compatibility-matrix-list for a (non conclusive) matrix of known working versions.

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
# -*- coding: UTF-8 -*- # -*- coding: UTF-8 -*-
# #
# Script for checking global health of host running VMware ESX/ESXi # Script for checking global health of host running VMware ESX/ESXi
@@ -15,16 +15,14 @@
# GNU General Public License for more details. # GNU General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software # along with this program; if not, see <https://www.gnu.org/licenses/>.
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
# #
# Pre-req : pywbem # Pre-req : pywbem
# #
# Copyright (c) 2008 David Ligeret # Copyright (c) 2008 David Ligeret
# Copyright (c) 2009 Joshua Daniel Franklin # Copyright (c) 2009 Joshua Daniel Franklin
# Copyright (c) 2010 Branden Schneider # Copyright (c) 2010 Branden Schneider
# Copyright (c) 2010-2015 Claudio Kuenzler # Copyright (c) 2010-2025 Claudio Kuenzler
# Copyright (c) 2010 Samir Ibradzic # Copyright (c) 2010 Samir Ibradzic
# Copyright (c) 2010 Aaron Rogers # Copyright (c) 2010 Aaron Rogers
# Copyright (c) 2011 Ludovic Hutin # Copyright (c) 2011 Ludovic Hutin
@@ -36,16 +34,20 @@
# Copyright (c) 2012 Craig Hart # Copyright (c) 2012 Craig Hart
# Copyright (c) 2013 Carl R. Friend # Copyright (c) 2013 Carl R. Friend
# Copyright (c) 2015 Andreas Gottwald # Copyright (c) 2015 Andreas Gottwald
# Copyright (c) 2015 Stanislav German-Evtushenko
# Copyright (c) 2015 Stefan Roos
# Copyright (c) 2018 Peter Newman
# Copyright (c) 2020 Luca Berra
# Copyright (c) 2022 Marco Markgraf
# #
# The VMware 4.1 CIM API is documented here: # The VMware CIM API is documented here (as of October 2024):
# http://www.vmware.com/support/developer/cim-sdk/4.1/smash/cim_smash_410_prog.pdf # https://docs.vmware.com/en/VMware-vSphere/7.0/vsphere-cim-smash-server-management-api-programming-guide/GUID-2725D01E-AE02-4EF2-9E98-5AB82AA0349A.html
# http://www.vmware.com/support/developer/cim-sdk/smash/u2/ga/apirefdoc/
# The CIM classes are documented here (as of October 2024):
# https://vdc-download.vmware.com/vmwb-repository/dcr-public/27c1c014-7315-4d6b-8e6b-292130a79b3c/36aca268-99fa-4916-b993-a077de55cbf1/CIM_API_Reference/index.html
# #
# The VMware 5.x CIM API is documented here: # This monitoring plugin is maintained and documented here:
# http://pubs.vmware.com/vsphere-50/index.jsp?nav=/5_1_1 # https://www.claudiokuenzler.com/monitoring-plugins/check_esxi_hardware.php
#
# This Nagios plugin is maintained here:
# http://www.claudiokuenzler.com/nagios-plugins/check_esxi_hardware.php
# #
#@--------------------------------------------------- #@---------------------------------------------------
#@ History #@ History
@@ -190,7 +192,7 @@
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com) #@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Added workaround for Dell PE x620 where "System Board 1 Riser Config Err 0: Connected" #@ Reason : Added workaround for Dell PE x620 where "System Board 1 Riser Config Err 0: Connected"
#@ element outputs wrong return code. Dell, please fix that. #@ element outputs wrong return code. Dell, please fix that.
#@ Added web-link to VMware CIM API 5.x at top of script. #@ Added web-link to VMware CIM API 5.x at top of script.
#@--------------------------------------------------- #@---------------------------------------------------
#@ Date : 20130424 #@ Date : 20130424
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com) #@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
@@ -216,17 +218,104 @@
#@ Author : Andreas Gottwald #@ Author : Andreas Gottwald
#@ Reason : Fix NoneType element bug #@ Reason : Fix NoneType element bug
#@--------------------------------------------------- #@---------------------------------------------------
#@ Date : 20150626
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Added support for patched pywbem 0.7.0 and new version 0.8.0, handle SSL error exception
#@---------------------------------------------------
#@ Date : 20150710
#@ Author : Stanislav German-Evtushenko
#@ Reason : Exit Unknown instead of Critical for timeouts and auth errors
#@---------------------------------------------------
#@ Date : 20151111
#@ Author : Stefan Roos
#@ Reason : Removed unused sensor_value variable and string import.
#@ Reason : Added global hosturl variable declaration after imports.
#@---------------------------------------------------
#@ Date : 20160411
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Distinguish between pywbem 0.7 and 0.8 (which is now released)
#@---------------------------------------------------
#@ Date : 20160531
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Add parameter for variable CIM port (useful when behind NAT)
#@---------------------------------------------------
#@ Date : 20161013
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Added support for pywbem 0.9.x (and upcoming releases)
#@---------------------------------------------------
#@ Date : 20170905
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Added option to ignore LCD/Display related elements (--no-lcd)
#@---------------------------------------------------
#@ Date : 20180329
#@ Author : Claudio Kuenzler (www.claudiokuenzler.com)
#@ Reason : Try to use internal pywbem function to determine version
#@---------------------------------------------------
#@ Date : 20180411
#@ Author : Peter Newman
#@ Reason : Throw an unknown if we can't fetch the data for some reason
#@---------------------------------------------------
#@ Date : 20181001
#@ Author : Claudio Kuenzler
#@ Reason : python3 compatibility
#@---------------------------------------------------
#@ Date : 20190510
#@ Author : Claudio Kuenzler
#@ Reason : Allow regular expressions from ignore list (-r)
#@---------------------------------------------------
#@ Date : 20190701
#@ Author : Phil Randal (phil.randal@gmail.com)
#@ Reason : Fix lookup of warranty info for Dell (again)
#@---------------------------------------------------
#@ Date : 20200605
#@ Author : Luca Berra
#@ Reason : Add option to ignore chassis intrusion (Supermicro)
#@---------------------------------------------------
#@ Date : 20200605
#@ Author : Claudio Kuenzler
#@ Reason : Add parameter (-S) for custom SSL/TLS protocol version
#@---------------------------------------------------
#@ Date : 20200710
#@ Author : Claudio Kuenzler
#@ Reason : Improve missing mandatory parameter error text (issue #47)
#@ Delete temporary openssl config file after use (issue #48)
#@---------------------------------------------------
#@ Date : 20210809
#@ Author : Claudio Kuenzler
#@ Reason : Fix TLSv1 usage (issue #51)
#@---------------------------------------------------
#@ Date : 20220509
#@ Author : Marco Markgraf
#@ Reason : Added JSON-output (Zabbix needs it)
#@---------------------------------------------------
#@ Date : 20221230
#@ Author : Claudio Kuenzler
#@ Reason : Fix bug when missing S/N (issue #68)
#@---------------------------------------------------
#@ Date : 20241129
#@ Author : Claudio Kuenzler
#@ Reason : Fix pkg_resources deprecation warning
# Remove python2 compatibility
# Remove pywbem 0.7.0 compatibility
#@---------------------------------------------------
#@ Date : 20250221
#@ Author : Claudio Kuenzler
#@ Reason : Update to newer pywbem exception call, catch HTTPError
#@ Attn : Requires 'packaging' Python module from now on!
#@---------------------------------------------------
import sys import sys
import time import time
import pywbem import pywbem
import re import re
import string import json
from optparse import OptionParser,OptionGroup from optparse import OptionParser,OptionGroup
from packaging.version import Version
version = '20150119' version = '20250221'
NS = 'root/cimv2' NS = 'root/cimv2'
hosturl = ''
# define classes to check 'OperationStatus' instance # define classes to check 'OperationStatus' instance
ClassesToCheck = [ ClassesToCheck = [
@@ -271,6 +360,7 @@ sensor_Type = {
} }
data = [] data = []
xdata = {}
perf_Prefix = { perf_Prefix = {
1:'Pow', 1:'Pow',
@@ -287,6 +377,9 @@ perf_Prefix = {
# host name # host name
hostname='' hostname=''
# cim port
cimport=''
# user # user
user='' user=''
@@ -299,6 +392,10 @@ vendor='unknown'
# verbose # verbose
verbose=False verbose=False
# output json
format='string'
pretty=False
# Produce performance data output for nagios # Produce performance data output for nagios
perfdata=False perfdata=False
@@ -307,6 +404,8 @@ timeout = 0
# elements to ignore (full SEL, broken BIOS, etc) # elements to ignore (full SEL, broken BIOS, etc)
ignore_list=[] ignore_list=[]
regex_ignore_list=[]
regex=False
# urlise model and tag numbers (currently only Dell supported, but the code does the right thing for other vendors) # urlise model and tag numbers (currently only Dell supported, but the code does the right thing for other vendors)
urlise_country='' urlise_country=''
@@ -317,6 +416,8 @@ get_volts = True
get_current = True get_current = True
get_temp = True get_temp = True
get_fan = True get_fan = True
get_lcd = True
get_intrusion = True
# define exit codes # define exit codes
ExitOK = 0 ExitOK = 0
@@ -389,7 +490,7 @@ def urlised_server_info(vendor, country, server_info):
#server_inf = server_info #server_inf = server_info
if vendor == 'dell' : if vendor == 'dell' :
# Dell support URLs (idea and tables borrowed from check_openmanage) # Dell support URLs (idea and tables borrowed from check_openmanage)
du = 'http://www.dell.com/support/troubleshooting/' + dell_country(country) + '19/Product/poweredge-' du = 'http://www.dell.com/support/home/' + dell_country(country) + '04/product-support/product/poweredge-'
if (server_info is not None) : if (server_info is not None) :
p=re.match('(.*)PowerEdge (.*) (.*)',server_info) p=re.match('(.*)PowerEdge (.*) (.*)',server_info)
if (p is not None) : if (p is not None) :
@@ -397,7 +498,7 @@ def urlised_server_info(vendor, country, server_info):
if md == 'R210 II': if md == 'R210 II':
md='r210-2' md='r210-2'
md=md.lower() md=md.lower()
server_info = p.group(1) + '<a href="' + du + md + '#ui-tabs-4">PowerEdge ' + p.group(2)+'</a> ' + p.group(3) server_info = p.group(1) + '<a href="' + du + md + '/">PowerEdge ' + p.group(2)+'</a> ' + p.group(3)
elif vendor == 'hp': elif vendor == 'hp':
return server_info return server_info
elif vendor == 'ibm': elif vendor == 'ibm':
@@ -412,8 +513,8 @@ def urlised_server_info(vendor, country, server_info):
def system_tag_url(vendor,country): def system_tag_url(vendor,country):
if vendor == 'dell': if vendor == 'dell':
# Dell support sites # Dell support sites
supportsite = 'http://www.dell.com/support/troubleshooting/' supportsite = 'http://www.dell.com/support/home/'
dellsuffix = 'nodhs1/Index?t=warranty&servicetag=' dellsuffix = '19/product-support/servicetag/'
# warranty URLs for different country codes # warranty URLs for different country codes
return supportsite + dell_country(country) + dellsuffix return supportsite + dell_country(country) + dellsuffix
@@ -436,29 +537,36 @@ def urlised_serialnumber(vendor,country,SerialNumber):
def verboseoutput(message) : def verboseoutput(message) :
if verbose: if verbose:
print "%s %s" % (time.strftime("%Y%m%d %H:%M:%S"), message) print(time.strftime("%Y%m%d %H:%M:%S"), message)
# ----------------------------------------------------------------------
def xdataprint():
if format == 'json' and not pretty:
print(json.dumps(xdata, sort_keys=True))
if format == 'json' and pretty:
print(json.dumps(xdata, sort_keys=True, indent=4))
# ---------------------------------------------------------------------- # ----------------------------------------------------------------------
def getopts() : def getopts() :
global hosturl,user,password,vendor,verbose,perfdata,urlise_country,timeout,ignore_list,get_power,get_volts,get_current,get_temp,get_fan 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
usage = "usage: %prog https://hostname user password system [verbose]\n" \ usage = "usage: %prog -H hostname -U username -P password [-C port -S proto -V vendor -v -p -I XX -i list,list -r]\n" \
"example: %prog https://my-shiny-new-vmware-server root fakepassword dell\n\n" \ "example: %prog -H hostname -U root -P password -C 5989 -V auto -I uk\n\n" \
"or, using new style options:\n\n" \
"usage: %prog -H hostname -U username -P password [-V system -v -p -I XX]\n" \
"example: %prog -H my-shiny-new-vmware-server -U root -P fakepassword -V auto -I uk\n\n" \
"or, verbosely:\n\n" \ "or, verbosely:\n\n" \
"usage: %prog --host=hostname --user=username --pass=password [--vendor=system --verbose --perfdata --html=XX]\n" "usage: %prog --host=hostname --user=username --pass=password [--cimport=port --sslproto=version --vendor=system --verbose --perfdata --html=XX --format=json --pretty]\n"
parser = OptionParser(usage=usage, version="%prog "+version) parser = OptionParser(usage=usage, version="%prog "+version)
group1 = OptionGroup(parser, 'Mandatory parameters') group1 = OptionGroup(parser, 'Mandatory parameters')
group2 = OptionGroup(parser, 'Optional parameters') group2 = OptionGroup(parser, 'Optional parameters')
group1.add_option("-H", "--host", dest="host", help="report on HOST", metavar="HOST") group1.add_option("-H", "--host", dest="host", help="connect to HOST", metavar="HOST")
group1.add_option("-U", "--user", dest="user", help="user to connect as", metavar="USER") group1.add_option("-U", "--user", dest="user", help="user to connect as", metavar="USER")
group1.add_option("-P", "--pass", dest="password", \ group1.add_option("-P", "--pass", dest="password", \
help="password, if password matches file:<path>, first line of given file will be used as password", metavar="PASS") help="password, if password matches file:<path>, first line of given file will be used as password", metavar="PASS")
group2.add_option("-C", "--cimport", dest="cimport", help="CIM port (default 5989)", metavar="CIMPORT")
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")
group2.add_option("-V", "--vendor", dest="vendor", help="Vendor code: auto, dell, hp, ibm, intel, or unknown (default)", \ group2.add_option("-V", "--vendor", dest="vendor", help="Vendor code: auto, dell, hp, ibm, intel, or unknown (default)", \
metavar="VENDOR", type='choice', choices=['auto','dell','hp','ibm','intel','unknown'],default="unknown") metavar="VENDOR", type='choice', choices=['auto','dell','hp','ibm','intel','unknown'],default="unknown")
group2.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, \ group2.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, \
@@ -471,6 +579,8 @@ def getopts() :
help="timeout in seconds - no effect on Windows (default = no timeout)") help="timeout in seconds - no effect on Windows (default = no timeout)")
group2.add_option("-i", "--ignore", action="store", type="string", dest="ignore", default="", \ group2.add_option("-i", "--ignore", action="store", type="string", dest="ignore", default="", \
help="comma-separated list of elements to ignore") help="comma-separated list of elements to ignore")
group2.add_option("-r", "--regex", action="store_true", dest="regex", default=False, \
help="allow regular expression lookup of ignore list")
group2.add_option("--no-power", action="store_false", dest="get_power", default=True, \ group2.add_option("--no-power", action="store_false", dest="get_power", default=True, \
help="don't collect power performance data") help="don't collect power performance data")
group2.add_option("--no-volts", action="store_false", dest="get_volts", default=True, \ group2.add_option("--no-volts", action="store_false", dest="get_volts", default=True, \
@@ -481,20 +591,28 @@ def getopts() :
help="don't collect temperature performance data") help="don't collect temperature performance data")
group2.add_option("--no-fan", action="store_false", dest="get_fan", default=True, \ group2.add_option("--no-fan", action="store_false", dest="get_fan", default=True, \
help="don't collect fan performance data") help="don't collect fan performance data")
group2.add_option("--no-lcd", action="store_false", dest="get_lcd", default=True, \
help="don't collect lcd/front display status")
group2.add_option("--no-intrusion", action="store_false", dest="get_intrusion", default=True, \
help="don't collect chassis intrusion status")
group2.add_option("--format", dest="format", help="'string' (default) or 'json'", \
metavar="FORMAT", type='choice', choices=['string','json'],default="string")
group2.add_option("--pretty", action="store_true", dest="pretty", default=False, \
help="return data as a pretty-printed json-array")
parser.add_option_group(group1) parser.add_option_group(group1)
parser.add_option_group(group2) parser.add_option_group(group2)
# check input arguments # check input arguments
if len(sys.argv) < 2: if len(sys.argv) < 2:
print "no parameters specified\n" print("no parameters specified\n")
parser.print_help() parser.print_help()
sys.exit(-1) sys.exit(-1)
# if first argument starts with 'https://' we have old-style parameters, so handle in old way # if first argument starts with 'https://' we have old-style parameters, so handle in old way
if re.match("https://",sys.argv[1]): if re.match("https://",sys.argv[1]):
# check input arguments # check input arguments
if len(sys.argv) < 5: if len(sys.argv) < 5:
print "too few parameters\n" print("too few parameters\n")
parser.print_help() parser.print_help()
sys.exit(-1) sys.exit(-1)
if len(sys.argv) > 5 : if len(sys.argv) > 5 :
@@ -512,7 +630,7 @@ def getopts() :
mandatories = ['host', 'user', 'password'] mandatories = ['host', 'user', 'password']
for m in mandatories: for m in mandatories:
if not options.__dict__[m]: if not options.__dict__[m]:
print "mandatory parameter '--" + m + "' is missing\n" print("mandatory option '" + m + "' not defined. read usage in help.\n")
parser.print_help() parser.print_help()
sys.exit(-1) sys.exit(-1)
@@ -526,17 +644,24 @@ def getopts() :
user=options.user user=options.user
password=options.password password=options.password
cimport=options.cimport
ignore_list=options.ignore.split(',')
format=options.format
pretty=options.pretty
perfdata=options.perfdata
regex=options.regex
sslproto=options.sslproto
timeout=options.timeout
urlise_country=options.urlise_country.lower()
vendor=options.vendor.lower() vendor=options.vendor.lower()
verbose=options.verbose verbose=options.verbose
perfdata=options.perfdata
urlise_country=options.urlise_country.lower()
timeout=options.timeout
ignore_list=options.ignore.split(',')
get_power=options.get_power get_power=options.get_power
get_volts=options.get_volts get_volts=options.get_volts
get_current=options.get_current get_current=options.get_current
get_temp=options.get_temp get_temp=options.get_temp
get_fan=options.get_fan get_fan=options.get_fan
get_lcd=options.get_lcd
get_intrusion=options.get_intrusion
# if user or password starts with 'file:', use the first string in file as user, second as password # if user or password starts with 'file:', use the first string in file as user, second as password
if (re.match('^file:', user) or re.match('^file:', password)): if (re.match('^file:', user) or re.match('^file:', password)):
@@ -565,12 +690,70 @@ if os_platform != "win32":
on_windows = False on_windows = False
import signal import signal
def handler(signum, frame): def handler(signum, frame):
print 'CRITICAL: Execution time too long!' print('UNKNOWN: Execution time too long!')
sys.exit(ExitUnknown)
# Use non-default CIM port
if cimport:
verboseoutput("Using manually defined CIM port "+cimport)
hosturl += ':'+cimport
# Use non-default SSL protocol version
if sslproto:
verboseoutput("Using non-default SSL protocol: "+sslproto)
allowed_protos = ["SSLv2", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3"]
if any(proto.lower() == sslproto.lower() for proto in allowed_protos):
import os
sslconfpath = '/tmp/'+hostname+'_openssl.conf'
verboseoutput("Creating OpenSSL config file: "+sslconfpath)
try:
with open(sslconfpath, 'w') as config_file:
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")
except Exception as e:
print('CRITICAL: An error occured while trying to write ssl config file: %s (%s)' % (sslconfpath, e))
sys.exit(ExitCritical)
os.environ["OPENSSL_CONF"] = sslconfpath
else:
print('CRITICAL: Invalid SSL protocol version given!')
sys.exit(ExitCritical) sys.exit(ExitCritical)
# Append lcd related elements to ignore list if --no-lcd was used
verboseoutput("LCD Status: %s" % get_lcd)
if not get_lcd:
ignore_list.append("System Board 1 LCD Cable Pres 0: Connected")
ignore_list.append("System Board 1 VGA Cable Pres 0: Connected")
ignore_list.append("Front Panel Board 1 FP LCD Cable 0: Connected")
ignore_list.append("Front Panel Board 1 FP LCD Cable 0: Config Error")
# Append chassis intrusion related elements to ignore list if --no-intrusion was used
verboseoutput("Chassis Intrusion Status: %s" % get_intrusion)
if not get_intrusion:
ignore_list.append("System Chassis 1 Chassis Intru: General Chassis intrusion")
ignore_list.append("System Chassis 1 Chassis Intru: Drive Bay intrusion")
ignore_list.append("System Chassis 1 Chassis Intru: I/O Card area intrusion")
ignore_list.append("System Chassis 1 Chassis Intru: Processor area intrusion")
ignore_list.append("System Chassis 1 Chassis Intru: System unplugged from LAN")
ignore_list.append("System Chassis 1 Chassis Intru: Unauthorized dock")
ignore_list.append("System Chassis 1 Chassis Intru: FAN area intrusion")
ignore_list.append("System Chassis 1 Chassis Intru: Unknown")
# connection to host # connection to host
pywbemversion = pywbem.__version__
verboseoutput("Found pywbem version "+pywbemversion)
verboseoutput("Connection to "+hosturl) verboseoutput("Connection to "+hosturl)
wbemclient = pywbem.WBEMConnection(hosturl, (user,password), NS) wbemclient = pywbem.WBEMConnection(hosturl, (user,password), NS, no_verification=True)
# Backward compatibility for older pywbem exceptions, big thanks to Claire M.!
if Version(pywbemversion) >= Version("1.0.0"):
verboseoutput("pywbem is 1.0.0 or newer")
import pywbem._cim_operations as PywbemCimOperations
import pywbem._cim_http as PywbemCimHttp
import pywbem._exceptions as PywbemExceptions
else:
verboseoutput("pywbem is older than 1.0.0")
import pywbem.cim_operations as PywbemCimOperations
import pywbem.cim_http as PywbemCimHttp
import pywbem.exceptions as PywbemExceptions
# Add a timeout for the script. When using with Nagios, the Nagios timeout cannot be < than plugin timeout. # Add a timeout for the script. When using with Nagios, the Nagios timeout cannot be < than plugin timeout.
if on_windows == False and timeout > 0: if on_windows == False and timeout > 0:
@@ -589,16 +772,27 @@ ExitMsg = ""
if vendor=='auto': if vendor=='auto':
try: try:
c=wbemclient.EnumerateInstances('CIM_Chassis') c=wbemclient.EnumerateInstances('CIM_Chassis')
except pywbem.cim_operations.CIMError,args: except PywbemCimOperations.CIMError as args:
if ( args[1].find('Socket error') >= 0 ): if ( args[1].find('Socket error') >= 0 ):
print "UNKNOWN: %s" %args print("UNKNOWN: {}".format(args))
sys.exit (ExitUnknown)
elif ( args[1].find('ThreadPool --- Failed to enqueue request') >= 0 ):
print("UNKNOWN: {}".format(args))
sys.exit (ExitUnknown) sys.exit (ExitUnknown)
else: else:
verboseoutput("Unknown CIM Error: %s" % args) verboseoutput("Unknown CIM Error: %s" % args)
except pywbem.cim_http.AuthError,arg: except PywbemExceptions.ConnectionError as args:
GlobalStatus = ExitUnknown
print("UNKNOWN: {}".format(args))
sys.exit (GlobalStatus)
except PywbemExceptions.HTTPError as args:
GlobalStatus = ExitUnknown
print("UNKNOWN: {}".format(args))
sys.exit (GlobalStatus)
except PywbemCimHttp.AuthError as arg:
verboseoutput("Global exit set to UNKNOWN") verboseoutput("Global exit set to UNKNOWN")
GlobalStatus = ExitUnknown GlobalStatus = ExitUnknown
print "UNKNOWN: Authentication Error" print("UNKNOWN: Authentication Error")
sys.exit (GlobalStatus) sys.exit (GlobalStatus)
else: else:
man=c[0][u'Manufacturer'] man=c[0][u'Manufacturer']
@@ -617,29 +811,45 @@ for classe in ClassesToCheck :
verboseoutput("Check classe "+classe) verboseoutput("Check classe "+classe)
try: try:
instance_list = wbemclient.EnumerateInstances(classe) instance_list = wbemclient.EnumerateInstances(classe)
except pywbem.cim_operations.CIMError,args: except PywbemCimOperations.CIMError as args:
if ( args[1].find('Socket error') >= 0 ): if ( args[1].find('Socket error') >= 0 ):
print "CRITICAL: %s" %args print("UNKNOWN: {}".format(args))
sys.exit (ExitCritical) sys.exit (ExitUnknown)
elif ( args[1].find('ThreadPool --- Failed to enqueue request') >= 0 ):
print("UNKNOWN: {}".format(args))
sys.exit (ExitUnknown)
else: else:
verboseoutput("Unknown CIM Error: %s" % args) verboseoutput("Unknown CIM Error: %s" % args)
except pywbem.cim_http.AuthError,arg: except PywbemExceptions.ConnectionError as args:
GlobalStatus = ExitUnknown
print("UNKNOWN: {}".format(args))
sys.exit (GlobalStatus)
except PywbemExceptions.HTTPError as args:
GlobalStatus = ExitUnknown
print("UNKNOWN: {}".format(args))
sys.exit (GlobalStatus)
except PywbemCimHttp.AuthError as arg:
verboseoutput("Global exit set to UNKNOWN") verboseoutput("Global exit set to UNKNOWN")
GlobalStatus = ExitCritical GlobalStatus = ExitUnknown
print "UNKNOWN: Authentication Error" print("UNKNOWN: Authentication Error")
sys.exit (GlobalStatus) sys.exit (GlobalStatus)
else: else:
# GlobalStatus = ExitOK #ARR # GlobalStatus = ExitOK #ARR
for instance in instance_list : for instance in instance_list :
sensor_value = ""
elementName = instance['ElementName'] elementName = instance['ElementName']
if elementName is None : if elementName is None :
elementName = 'Unknown' elementName = 'Unknown'
elementNameValue = elementName elementNameValue = elementName
verboseoutput(" Element Name = "+elementName) verboseoutput(" Element Name = "+elementName)
# Ignore element if we don't want it # Ignore element if we don't want it
if elementName in ignore_list : if (regex == True) and (len(ignore_list) > 0) :
for ignore in ignore_list :
if re.search(ignore, elementName, re.IGNORECASE) :
verboseoutput(" (ignored through regex)")
regex_ignore_list.append(elementName)
if (elementName in ignore_list) or (elementName in regex_ignore_list) :
verboseoutput(" (ignored)") verboseoutput(" (ignored)")
continue continue
@@ -650,6 +860,8 @@ for classe in ClassesToCheck :
+ str(instance[u'ReleaseDate'].datetime.date()) + str(instance[u'ReleaseDate'].datetime.date())
verboseoutput(" VersionString = "+instance[u'VersionString']) verboseoutput(" VersionString = "+instance[u'VersionString'])
xdata['Bios Info'] = bios_info
elif elementName == 'Chassis' : elif elementName == 'Chassis' :
man = instance[u'Manufacturer'] man = instance[u'Manufacturer']
if man is None : if man is None :
@@ -664,7 +876,7 @@ for classe in ClassesToCheck :
model = instance[u'Model'] model = instance[u'Model']
if model: if model:
verboseoutput(" Model = "+model) verboseoutput(" Model = "+model)
server_info += model + ' s/n:' server_info += model
elif elementName == 'Server Blade' : elif elementName == 'Server Blade' :
SerialNumber = instance[u'SerialNumber'] SerialNumber = instance[u'SerialNumber']
@@ -672,6 +884,8 @@ for classe in ClassesToCheck :
verboseoutput(" SerialNumber = "+SerialNumber) verboseoutput(" SerialNumber = "+SerialNumber)
isblade = "yes" isblade = "yes"
xdata['SerialNumber'] = SerialNumber
# Report detail of Numeric Sensors and generate nagios perfdata # Report detail of Numeric Sensors and generate nagios perfdata
if classe == "CIM_NumericSensor" : if classe == "CIM_NumericSensor" :
@@ -713,33 +927,38 @@ for classe in ClassesToCheck :
if units == 7: # Watts if units == 7: # Watts
if get_power: if get_power:
data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),1) ) data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),1) )
xdata[perf_el] = { 'Unit': 'Watt', 'Value': cr, 'warn' : utnc, 'crit': utc }
elif units == 6: # Current elif units == 6: # Current
if get_current: if get_current:
data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),3) ) data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),3) )
xdata[perf_el] = { 'Unit': 'Ampere', 'Value': cr, 'warn' : utnc, 'crit': utc }
# PSU Voltage # PSU Voltage
elif sensorType == 3: # Voltage elif sensorType == 3: # Voltage
if get_volts: if get_volts:
data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),2) ) data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),2) )
xdata[perf_el] = { 'Unit': 'Volt', 'Value': cr, 'warn' : utnc, 'crit': utc }
# Temperatures # Temperatures
elif sensorType == 2: # Temperature elif sensorType == 2: # Temperature
if get_temp: if get_temp:
data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),4) ) data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),4) )
xdata[perf_el] = { 'Value': cr, 'warn' : utnc, 'crit': utc }
# Fan speeds # Fan speeds
elif sensorType == 5: # Tachometer elif sensorType == 5: # Tachometer
if get_fan: if get_fan:
if units == 65: # percentage if units == 65: # percentage
data.append( ("%s=%g%%;%g;%g " % (perf_el, cr, utnc, utc),6) ) data.append( ("%s=%g%%;%g;%g " % (perf_el, cr, utnc, utc),6) )
xdata[perf_el] = { 'Unit': '%', 'Value': cr, 'warn' : utnc, 'crit': utc }
else: else:
data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),5) ) data.append( ("%s=%g;%g;%g " % (perf_el, cr, utnc, utc),5) )
xdata[perf_el] = { 'Value': cr, 'warn' : utnc, 'crit': utc }
elif classe == "CIM_Processor" : elif classe == "CIM_Processor" :
verboseoutput(" Family = %d" % instance['Family']) verboseoutput(" Family = %d" % instance['Family'])
verboseoutput(" CurrentClockSpeed = %dMHz" % instance['CurrentClockSpeed']) verboseoutput(" CurrentClockSpeed = %dMHz" % instance['CurrentClockSpeed'])
# HP Check # HP Check
if vendor == "hp" : if vendor == "hp" :
if instance['HealthState'] is not None : if instance['HealthState'] is not None :
@@ -755,11 +974,11 @@ for classe in ClassesToCheck :
30 : ExitCritical, # Non-recoverable Error 30 : ExitCritical, # Non-recoverable Error
}[elementStatus] }[elementStatus]
if (interpretStatus == ExitCritical) : if (interpretStatus == ExitCritical) :
verboseoutput("GLobal exit set to CRITICAL") verboseoutput("Global exit set to CRITICAL")
GlobalStatus = ExitCritical GlobalStatus = ExitCritical
ExitMsg += " CRITICAL : %s " % elementNameValue ExitMsg += " CRITICAL : %s " % elementNameValue
if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) : if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) :
verboseoutput("GLobal exit set to WARNING") verboseoutput("Global exit set to WARNING")
GlobalStatus = ExitWarning GlobalStatus = ExitWarning
ExitMsg += " WARNING : %s " % elementNameValue ExitMsg += " WARNING : %s " % elementNameValue
# Added the following for when GlobalStatus is ExitCritical and a warning is detected # Added the following for when GlobalStatus is ExitCritical and a warning is detected
@@ -776,9 +995,7 @@ for classe in ClassesToCheck :
elif (vendor == "dell" or vendor == "intel" or vendor == "ibm" or vendor=="unknown") : elif (vendor == "dell" or vendor == "intel" or vendor == "ibm" or vendor=="unknown") :
# Added 20121027 As long as Dell doesnt correct these CIM elements return code we have to ignore it # Added 20121027 As long as Dell doesnt correct these CIM elements return code we have to ignore it
ignore_list.append("System Board 1 Riser Config Err 0: Connected") ignore_list.append("System Board 1 Riser Config Err 0: Connected")
ignore_list.append("System Board 1 LCD Cable Pres 0: Connected") ignore_list.append("Add-in Card 4 PEM Presence 0: Connected")
ignore_list.append("System Board 1 VGA Cable Pres 0: Connected")
ignore_list.append("Add-in Card 4 PEM Presence 0: Connected")
if instance['OperationalStatus'] is not None : if instance['OperationalStatus'] is not None :
elementStatus = instance['OperationalStatus'][0] elementStatus = instance['OperationalStatus'][0]
verboseoutput(" Element Op Status = %d" % elementStatus) verboseoutput(" Element Op Status = %d" % elementStatus)
@@ -810,7 +1027,7 @@ for classe in ClassesToCheck :
GlobalStatus = ExitCritical GlobalStatus = ExitCritical
ExitMsg += " CRITICAL : %s " % elementNameValue ExitMsg += " CRITICAL : %s " % elementNameValue
if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) : if (interpretStatus == ExitWarning and GlobalStatus != ExitCritical) :
verboseoutput("GLobal exit set to WARNING") verboseoutput("Global exit set to WARNING")
GlobalStatus = ExitWarning GlobalStatus = ExitWarning
ExitMsg += " WARNING : %s " % elementNameValue ExitMsg += " WARNING : %s " % elementNameValue
# Added same logic as in 20100702 here, otherwise Dell servers would return UNKNOWN instead of OK # Added same logic as in 20100702 here, otherwise Dell servers would return UNKNOWN instead of OK
@@ -832,6 +1049,7 @@ if (urlise_country != '') :
# If this is a blade server, also output chassis serial number as additional info # If this is a blade server, also output chassis serial number as additional info
if (isblade == "yes") : if (isblade == "yes") :
SerialNumber += " Chassis S/N: %s " % (SerialChassis) SerialNumber += " Chassis S/N: %s " % (SerialChassis)
xdata['ChassisSerialNumber'] = SerialChassis
# Output performance data # Output performance data
perf = '|' perf = '|'
@@ -853,13 +1071,28 @@ if perfdata:
if perf == '|': if perf == '|':
perf = '' perf = ''
# Cleanup temporary openssl config
if sslproto:
os.remove(sslconfpath)
xdata['GlobalStatus'] = GlobalStatus
if GlobalStatus == ExitOK : if GlobalStatus == ExitOK :
print "OK - Server: %s %s %s%s" % (server_info, SerialNumber, bios_info, perf) if format == 'string':
print("OK - Server: %s s/n: %s %s%s" % (server_info, SerialNumber, bios_info, perf))
else:
xdataprint()
elif GlobalStatus == ExitUnknown : elif GlobalStatus == ExitUnknown :
print "UNKNOWN: %s" % (ExitMsg) #ARR if format == 'string':
print("UNKNOWN: %s" % (ExitMsg)) #ARR
else:
xdataprint()
else: else:
print "%s- Server: %s %s %s%s" % (ExitMsg, server_info, SerialNumber, bios_info, perf) if format == 'string':
print("%s - Server: %s %s %s%s" % (ExitMsg, server_info, 's/n: ' + SerialNumber, bios_info, perf))
else:
xdataprint()
sys.exit (GlobalStatus) sys.exit (GlobalStatus)