Monitor APC Temperature Sensor in CheckMK
Monitor APC Temperature Sensor in CheckMK

This script was originally written for CheckMK 2.3. If you’re looking for a version which works with CheckMK 2.4, please check out Kevin Siering’s updated script. He graciously modified this this script to work with version 2.4. Thanks Kevin!
If you’re attempting to monitor APC’s Network Management Card Version 3 (NMC3) via CheckMK you’ll find that the build-in checks do not include any support for monitoring external temperature sensors plugged in via the universal I/O ports. Below is a modified version of Marius Rieder’s script enabling this functionality.
All values for the tempurature sensor are pulled directly from SNMP, including the sensor’s name and warn/crit thresholds. To change these values, logon to the NMC’s WebUI and browse to Configuration » Universal I/O » Temp & Humidity.
Installation
- Logon to the CheckMK host via SSH.
- Switch to the user account running your CheckMK site. By default , this is
cmk.
sudo su - cmk- Browse to where custom agent-based plugins are kept.
cd ~/local/lib/check_mk/base/plugins/agent_based/- Create a new Python file and copy in the script.
vim apc_temperature_sensor.py- Within CheckMK run a service discovery on the host representing the UPS.
Script
# ./apc_temperature_sensor.py
from .agent_based_api.v1 import (
get_value_store,
all_of,
exists,
register,
Service,
SNMPTree,
startswith,
State,
)
from .utils.temperature import check_temperature
# Maps APC status codes to Checkmk states.
APC_SENSOR_STATUS = {
1: State.UNKNOWN,
2: State.OK,
3: State.WARN,
4: State.CRIT,
}
def parse_apc_rackpdu_sensor_temp_v2(string_table):
"""
Parses temperature sensor data from the new APC OID structure.
"""
parsed = {}
status_table, config_table = string_table
# Build dictionary from config first (for thresholds).
config_dict = {}
for index, sensor_name, port_name, _, temp_min, temp_high, temp_max, *_ in config_table:
config_dict[sensor_name] = {
'port': port_name,
'temp_min': int(temp_min),
'temp_high': int(temp_high),
'temp_max': int(temp_max),
}
# Add status (reading and status code).
for index, sensor_name, port_name, temp_f, _, _, status_code, *_ in status_table:
# Convert Fahrenheit to Celsius.
temp_c = (int(temp_f) - 32) * 5.0 / 9.0
status_code = int(status_code)
thresholds = config_dict.get(sensor_name, {})
parsed[sensor_name] = [
temp_c,
status_code,
thresholds.get('temp_high', 0),
thresholds.get('temp_max', 0)
]
return parsed
register.snmp_section(
name='apc_rackpdu_sensor_temp_v2',
detect=all_of(
startswith('.1.3.6.1.2.1.1.1.0', 'APC Web/SNMP'),
exists('.1.3.6.1.4.1.318.1.1.25.1.2.1.3.*'),
),
parse_function=parse_apc_rackpdu_sensor_temp_v2,
fetch=[
SNMPTree(
base='.1.3.6.1.4.1.318.1.1.25.1.2.1',
oids=[
'1', # index
'3', # sensor name
'4', # port name
'5', # current temperature (F)
'8', # reserved
'9', # reserved
'10', # sensor status
]
),
SNMPTree(
base='.1.3.6.1.4.1.318.1.1.25.1.4.1',
oids=[
'1', # index
'3', # sensor name
'4', # port name
'5', # reserved
'6', # min temp (C)
'7', # high temp (C)
'8', # max temp (C)
]
),
],
)
def discovery_apc_rackpdu_sensor_temp_v2(section):
for sensor in section:
yield Service(item=sensor)
def check_apc_rackpdu_sensor_temp_v2(item, params, section):
if item not in section:
return
temperature, status_code, warn, crit = section[item]
yield from check_temperature(
reading=temperature,
params=params,
unique_name='check_apc_rackpdu_sensor_temp_v2.%s' % item,
value_store=get_value_store(),
dev_levels=(warn, crit),
dev_status=APC_SENSOR_STATUS.get(status_code, State.UNKNOWN),
dev_status_name=item,
)
register.check_plugin(
name='apc_rackpdu_sensor_temp_v2',
service_name='%s Temperature',
discovery_function=discovery_apc_rackpdu_sensor_temp_v2,
check_function=check_apc_rackpdu_sensor_temp_v2,
check_default_parameters={},
check_ruleset_name='temperature',
)