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

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',
)