Monitor APC Temperature Sensor in CheckMK

Monitor APC Temperature Sensor in CheckMK

September 13, 2025·Tyler Rasmussen
Tyler Rasmussen

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

  1. Logon to the CheckMK host via SSH.
  2. Switch to the user account running your CheckMK site. By default , this is cmk.
sudo su - cmk
  1. Browse to where custom agent-based plugins are kept.
cd ~/local/lib/check_mk/base/plugins/agent_based/
  1. Create a new Python file and copy in the script.
vim apc_temperature_sensor.py
  1. 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',
)