Repository
Munin (contrib)
Last change
2020-08-25
Graph Categories
Capabilities
Keywords
Language
Python (2.x)
Authors

esxi__sensors

Name

esxi__sensors - Munin plugin to monitor hardware sensors in a VMware ESXi installation.

Applicable Systems

VMware ESX and ESXi systems. Might work with other systems that use CIM and CIM_NumericSensors, probably with changes to the namespace.

The host on which the plugin is run must have the pywbem Python library installed.

Configuration

The remote host name is taken from the plugin name as the SNMP plugins are; to monitor host 192.168.1.15, symlink the plugin to esxi_192.168.1.15_sensors .

The username and password for accessing the ESXi system must be set in a config file:

[esxi_192.168.1.15_*]
env.user monitor
env.pass passw0rd

To create an account just for monitoring the ESXi, add a user to the host, make it a member of the ‘root’ group, and give it the ‘No access’ role. That will allow the account to connect via WBEM, but not via any of the management tools.

In the event that not all sensor types are desired, the plugin can be limited to a subset of the types available:

env.sensors 2 3 5

Check the sensor_data dictionary at the top of the script to see what sensor types are available.

Interpretation

This is a multigraph plugin that will generate separate graphs for each type of sensor. The names of the sensors are taken from the ESXi host.

If the host provides error thresholds for the sensor readings, those will be passed along to munin.

Version

1.0

Author

Phil Gold <phil_g@pobox.com>

License

To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide under a CC0 waiver. This software is distributed without any warranty.

http://creativecommons.org/publicdomain/zero/1.0/

#!/usr/bin/env python
# coding=iso-8859-1

"""
=head1 NAME

esxi__sensors - Munin plugin to monitor hardware sensors in a VMware ESXi
installation.

=head1 APPLICABLE SYSTEMS

VMware ESX and ESXi systems.  Might work with other systems that use CIM and
CIM_NumericSensors, probably with changes to the namespace.

The host on which the plugin is run must have the pywbem Python library
installed.

=head1 CONFIGURATION

The remote host name is taken from the plugin name as the SNMP plugins are;
to monitor host 192.168.1.15, symlink the plugin to esxi_192.168.1.15_sensors .

The username and password for accessing the ESXi system must be set in a config
file:

  [esxi_192.168.1.15_*]
  env.user monitor
  env.pass passw0rd

To create an account just for monitoring the ESXi, add a user to the host,
make it a member of the 'root' group, and give it the 'No access' role.
That will allow the account to connect via WBEM, but not via any of the
management tools.

In the event that not all sensor types are desired, the plugin can be limited
to a subset of the types available:

  env.sensors 2 3 5

Check the sensor_data dictionary at the top of the script to see what sensor
types are available.

=head1 INTERPRETATION

This is a multigraph plugin that will generate separate graphs for each type
of sensor.  The names of the sensors are taken from the ESXi host.

If the host provides error thresholds for the sensor readings, those will be
passed along to munin.

=head1 VERSION

1.0

=head1 AUTHOR

Phil Gold <phil_g@pobox.com>

=head1 LICENSE

To the extent possible under law, the author(s) have dedicated all copyright and
related and neighboring rights to this software to the public domain worldwide
under a CC0 waiver.  This software is distributed without any warranty.

http://creativecommons.org/publicdomain/zero/1.0/

=cut
"""

import os, sys
import re, string
import pywbem

NS = 'root/cimv2'

sensor_data = {
  2 : {'prefix':'temp', 'title':'Temperatures', 'unit':'°C'},
  3 : {'prefix':'volt', 'title':'Voltages',     'unit':'Volts'},
  4 : {'prefix':'amp',  'title':'Current',      'unit':'Amps'},
  5 : {'prefix':'fan',  'title':'Fans',         'unit':'RPM'}
}

def munin_id(instance):
  return sensor_data[instance['SensorType']]['prefix'] + \
      re.sub('[^a-zA-Z0-9]', '_', instance['DeviceId'])

def inst_val(instance, field):
  return instance[field] * 10 ** instance['UnitModifier']

def munin_warning(instance):
  if not instance['EnabledThresholds']:
    return ':'

  result = ''
  if 0 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'LowerThresholdNonCritical'))
  result += ':'
  if 1 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'UpperThresholdNonCritical'))
  return result

def munin_critical(instance):
  if not instance['EnabledThresholds']:
    return ':'

  result = ''
  if 2 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'LowerThresholdCritical'))
  elif 4 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'LowerThresholdFatal'))
  result += ':'
  if 3 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'UpperThresholdCritical'))
  elif 5 in instance['EnabledThresholds']:
    result += str(inst_val(instance, 'UpperThresholdFatal'))
  return result

def print_sensors(sensor_type, instances, config):
  print 'multigraph esxi_' + sensor_data[sensor_type]['prefix'] + 's'

  if config:
    print "graph_title ESXi " + sensor_data[sensor_type]['title']
    print "graph_args --base 1000 -l 0"
    print "graph_vlabel " + sensor_data[sensor_type]['unit']
    print "graph_category sensors"
    for i in instances:
      if i['SensorType'] == sensor_type:
        print '{0}.label {1}'.format(munin_id(i), i['Caption'])
        print '{0}.max {1}'.format(munin_id(i), inst_val(i, 'MaxReadable'))
        print '{0}.min {1}'.format(munin_id(i), inst_val(i, 'MinReadable'))
        if munin_warning(i) != ':':
          print '{0}.warning {1}'.format(munin_id(i), munin_warning(i))
        if munin_critical(i) != ':':
          print '{0}.critical {1}'.format(munin_id(i), munin_critical(i))

    print ''
    return

  for i in instances:
    if i['SensorType'] == sensor_type:
      print '{0}.value {1}'.format(munin_id(i), inst_val(i, 'CurrentReading'))
  print ''

plugin_name = list(os.path.split(sys.argv[0]))[1]
host = plugin_name[string.index(plugin_name, '_')+1:string.rindex(plugin_name, '_')]
if host == '':
  sys.stderr.write("No hostname found.\n")
  exit(1)
try:
  username = os.environ['user']
  password = os.environ['pass']
except KeyError:
  sys.stderr.write("Username and password must be specified.\n")
  exit(1)

try:
  sensors = map(int, re.split(',? +', os.environ['sensors']))
except:
  sensors = [2, 3, 4, 5]

config = len(sys.argv) > 1 and sys.argv[1] == 'config'

wbemclient = pywbem.WBEMConnection('https://' + host, (username, password), NS)
insts = sorted(wbemclient.EnumerateInstances('CIM_NumericSensor'))

print 'host_name ' + host
for sensor_type in sensors:
  print_sensors(sensor_type, insts, config)