diff --git a/cmk/plugins/hp_proliant/agent_based/hp_proliant_da_cntlr.py b/cmk/plugins/hp_proliant/agent_based/hp_proliant_da_cntlr.py index ff408ef5c9a..c10cf770bf2 100644 --- a/cmk/plugins/hp_proliant/agent_based/hp_proliant_da_cntlr.py +++ b/cmk/plugins/hp_proliant/agent_based/hp_proliant_da_cntlr.py @@ -4,7 +4,7 @@ # conditions defined in the file COPYING, which is part of this source code package. from collections.abc import Mapping from enum import StrEnum -from typing import assert_never, NamedTuple, Self +from typing import assert_never, NamedTuple, Self, TypedDict from cmk.agent_based.v2 import ( CheckPlugin, @@ -182,6 +182,29 @@ def from_line(cls, line: list[str]) -> Self | None: ) +class Params(TypedDict): + condition_other_state: int + board_condition_other_state: int + board_status_other_state: int + + +# Defaults reproduce the historic behaviour (the "other" value maps to WARN). +# HPE ProLiant Gen11 / iLO 6 firmware tends to report the board condition as +# "other" for perfectly healthy controllers, which makes the service WARN +# forever; the check ruleset lets users remap each "other" value independently. +DEFAULT_PARAMETERS: Params = { + "condition_other_state": State.WARN.value, + "board_condition_other_state": State.WARN.value, + "board_status_other_state": State.WARN.value, +} + + +def _monitoring_state(value: SNMPCondition | SNMPState, other_state: State) -> State: + if value in (SNMPCondition.OTHER, SNMPState.OTHER): + return other_state + return value.to_state() + + def parse_hp_proliant_da_cntlr(string_table: StringTable) -> ParsedSection: return {line[0]: ControllerData.from_line(line) for line in string_table} @@ -191,7 +214,9 @@ def discovery_hp_proliant_da_cntlr(section: ParsedSection) -> DiscoveryResult: yield from (Service(item=item) for item in section) -def check_hp_proliant_da_cntlr(item: ControllerID, section: ParsedSection) -> CheckResult: +def check_hp_proliant_da_cntlr( + item: ControllerID, params: Params, section: ParsedSection +) -> CheckResult: if not (subsection := section.get(item)): yield Result(state=State.UNKNOWN, summary="Controller not found in SNMP data") return @@ -201,9 +226,16 @@ def check_hp_proliant_da_cntlr(item: ControllerID, section: ParsedSection) -> Ch "Board-Condition": subsection.b_cond, "Board-Status": subsection.b_status, } + other_states: Mapping[str, State] = { + "Condition": State(params["condition_other_state"]), + "Board-Condition": State(params["board_condition_other_state"]), + "Board-Status": State(params["board_status_other_state"]), + } yield Result( - state=State.worst(*(state.to_state() for state in states.values())), + state=State.worst( + *(_monitoring_state(state, other_states[label]) for label, state in states.items()) + ), summary=( f"{', '.join(f'{label}: {state}' for label, state in states.items())} " f"(Role: {subsection.role}, Model: {subsection.model}, Slot: {subsection.slot}, " @@ -231,4 +263,6 @@ def check_hp_proliant_da_cntlr(item: ControllerID, section: ParsedSection) -> Ch service_name="HW Controller %s", discovery_function=discovery_hp_proliant_da_cntlr, check_function=check_hp_proliant_da_cntlr, + check_default_parameters=DEFAULT_PARAMETERS, + check_ruleset_name="hp_proliant_da_cntlr", ) diff --git a/cmk/plugins/hp_proliant/rulesets/hp_proliant_da_cntlr.py b/cmk/plugins/hp_proliant/rulesets/hp_proliant_da_cntlr.py new file mode 100644 index 00000000000..6ab0e1af631 --- /dev/null +++ b/cmk/plugins/hp_proliant/rulesets/hp_proliant_da_cntlr.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +# Copyright (C) 2026 Checkmk GmbH - License: GNU General Public License v2 +# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and +# conditions defined in the file COPYING, which is part of this source code package. + +from cmk.rulesets.v1 import Help, Title +from cmk.rulesets.v1.form_specs import ( + DefaultValue, + DictElement, + Dictionary, + ServiceState, +) +from cmk.rulesets.v1.rule_specs import CheckParameters, HostAndItemCondition, Topic + + +def _make_form() -> Dictionary: + return Dictionary( + help_text=Help( + "The RAID controllers of HPE ProLiant servers report a condition, a " + "board condition and a board status. Each of these can take the value " + "other, meaning the instrument agent does not recognize the " + "status. HPE ProLiant Gen11 / iLO 6 firmware reports the board " + "condition as other for perfectly healthy controllers, which " + "makes the service WARN permanently. Here you can remap the monitoring " + "state used for the other value of each field independently." + ), + elements={ + "condition_other_state": DictElement( + required=False, + parameter_form=ServiceState( + title=Title("State when the controller condition is other"), + prefill=DefaultValue(ServiceState.WARN), + ), + ), + "board_condition_other_state": DictElement( + required=False, + parameter_form=ServiceState( + title=Title("State when the board condition is other"), + prefill=DefaultValue(ServiceState.WARN), + ), + ), + "board_status_other_state": DictElement( + required=False, + parameter_form=ServiceState( + title=Title("State when the board status is other"), + prefill=DefaultValue(ServiceState.WARN), + ), + ), + }, + ) + + +rule_spec_hp_proliant_da_cntlr = CheckParameters( + name="hp_proliant_da_cntlr", + title=Title("HPE ProLiant RAID controller"), + topic=Topic.STORAGE, + parameter_form=_make_form, + condition=HostAndItemCondition(item_title=Title("Controller index")), +) diff --git a/tests/unit/cmk/plugins/hp_proliant/agent_based/test_hp_proliant_da_cntlr.py b/tests/unit/cmk/plugins/hp_proliant/agent_based/test_hp_proliant_da_cntlr.py index 99b446da284..c0deba798af 100644 --- a/tests/unit/cmk/plugins/hp_proliant/agent_based/test_hp_proliant_da_cntlr.py +++ b/tests/unit/cmk/plugins/hp_proliant/agent_based/test_hp_proliant_da_cntlr.py @@ -8,6 +8,7 @@ from cmk.plugins.hp_proliant.agent_based.hp_proliant_da_cntlr import ( check_hp_proliant_da_cntlr, ControllerID, + DEFAULT_PARAMETERS, discovery_hp_proliant_da_cntlr, parse_hp_proliant_da_cntlr, ) @@ -73,7 +74,11 @@ def test_discovery() -> None: def test_check(item: ControllerID, expected: list[Result]) -> None: assert ( list( - check_hp_proliant_da_cntlr(item=item, section=parse_hp_proliant_da_cntlr(STRING_TABLE)) + check_hp_proliant_da_cntlr( + item=item, + params=DEFAULT_PARAMETERS, + section=parse_hp_proliant_da_cntlr(STRING_TABLE), + ) ) == expected )