#!/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 =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)