Skip to content

Commit d1dcd55

Browse files
Bugfix/Ckeanup/Stability: Raritan sends per default only a Crit value of 0,030A and no warn. This will be set to None and the check will crash. Becuase this is security sensitive, a WARN value will be set to CRIT and a CRIT value will be set also on WARN. So in all cases a CRIT Message will be set! I decided to make this solution because it is important to get an event in all cases because it can save lives.
I also merge the complete dataclasses to pydantic and add a funktion to easier get the capabilities of the Raritan device instead of check the bits itself. Funktions that are required as shared funktions when the legacy check will be rewirtten are moves to lib. The shared funktions can be included wir __init__.py: from cmk.plugins.raritan import ( InletSensorEnabledThresholds, InletDeviceCapabilities, InletPoleCapabilities, RaritanData, SensorValues, Sensor, PDU, TYPE_MAPPING, UNIT_MAPPING, )
1 parent ab4fd3f commit d1dcd55

3 files changed

Lines changed: 278 additions & 117 deletions

File tree

cmk/plugins/raritan/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,14 @@
1212
# (c) 2024 SWR
1313
# @author Frank Baier <frank.baier@swr.de>
1414
#
15+
from cmk.plugins.raritan.lib import (
16+
InletSensorEnabledThresholds,
17+
InletDeviceCapabilities,
18+
InletPoleCapabilities,
19+
RaritanData,
20+
SensorValues,
21+
Sensor,
22+
PDU,
23+
TYPE_MAPPING,
24+
UNIT_MAPPING,
25+
)

cmk/plugins/raritan/agent_based/raritan_px2_residual_operating_current.py

Lines changed: 28 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717

1818
from collections.abc import Callable, Sequence
19-
from dataclasses import dataclass, field
2019
from typing import (
2120
NotRequired,
2221
TypedDict,
@@ -41,65 +40,18 @@
4140
State,
4241
StringByteTable,
4342
)
43+
from cmk.plugins.raritan import (
44+
InletSensorEnabledThresholds,
45+
InletDeviceCapabilities,
46+
InletPoleCapabilities,
47+
RaritanData,
48+
SensorValues,
49+
Sensor,
50+
PDU,
51+
TYPE_MAPPING,
52+
UNIT_MAPPING,
53+
)
4454

45-
TYPE_MAPPING = {
46-
"1": ("current", "RMS"),
47-
"2": ("peak", "Peak"),
48-
"3": ("unbalanced", "Unbalanced"),
49-
"4": ("voltage", "RMS"),
50-
"5": ("power", "Active"),
51-
"6": ("appower", "Apparent"),
52-
# power factor is defined as the ratio of the real power flowing
53-
# to the load to the apparent power
54-
"7": ("power_factor", "Power Factor"),
55-
"8": ("energy", "Active"),
56-
"9": ("energy", "Apparent"),
57-
"10": ("temp", ""),
58-
"11": ("humidity", ""),
59-
"12": ("airflow", ""),
60-
"13": ("pressure_pa", "Air"),
61-
"14": ("binary", "On/Off"),
62-
"15": ("binary", "Trip"),
63-
"16": ("binary", "Vibration"),
64-
"17": ("binary", "Water Detector"),
65-
"18": ("binary", "Smoke Detector"),
66-
"19": ("binary", ""),
67-
"20": ("binary", "Contact"),
68-
"21": ("fanspeed", ""),
69-
"26": ("residual_current", "Residual Current"),
70-
"30": ("", "Other"),
71-
"31": ("", "None"),
72-
}
73-
74-
UNIT_MAPPING = {
75-
"-1": "",
76-
"0": " Other",
77-
"1": " V",
78-
"2": " A",
79-
"3": " W",
80-
"4": " VA",
81-
"5": " Wh",
82-
"6": " VAh",
83-
# for dev_unit in check_temperature
84-
"7": "c",
85-
"8": " hz",
86-
"9": "%",
87-
"10": " m/s",
88-
"11": " Pa",
89-
# 1 psi = 6894,757293168 Pa
90-
"12": " psi",
91-
"13": " g",
92-
# for dev_unit in check_temperature
93-
"14": "f",
94-
"15": " ft",
95-
"16": " inch",
96-
"17": " cm",
97-
"18": " m",
98-
"19": " RPM",
99-
}
100-
101-
102-
RESIDUAL_BITMASK = 0b01000000
10355

10456
_RENDER_FUNCTION_AND_UNIT: dict[str, tuple[Callable | None, str]] = {
10557
"%": (
@@ -119,46 +71,6 @@ class Params(TypedDict):
11971
residual_levels: NotRequired[NoLevelsT | FixedLevelsT]
12072

12173

122-
@dataclass(frozen=True, kw_only=True)
123-
class PDU:
124-
pdu_index: str
125-
label: str
126-
name: str
127-
plug: str
128-
pole_count: str
129-
rated_voltage: str
130-
rated_current: str
131-
rated_frequency: str
132-
rated_va: str
133-
plug_descriptor: str
134-
enable_state: str
135-
device_capabilities: list[int]
136-
pole_capabilities: list[int]
137-
138-
139-
@dataclass(frozen=True, kw_only=True)
140-
class SensorValues:
141-
sensor_value: float
142-
sensor_upper_crit: float
143-
sensor_upper_warn: float
144-
145-
146-
@dataclass(frozen=True, kw_only=True)
147-
class Sensor:
148-
availability: str
149-
sensor_name: str
150-
sensor_type: str
151-
sensor_values: SensorValues
152-
sensor_unit: str
153-
sensor_thresholds: list[int]
154-
155-
156-
@dataclass(kw_only=True)
157-
class RaritanData:
158-
pdu: PDU
159-
sensors: dict[str, dict[str, Sensor]] = field(default_factory=dict)
160-
161-
16274
def _parse_pdu_data(raw_pdu_data: StringByteTable) -> PDU:
16375
pdu_data = raw_pdu_data[0]
16476

@@ -172,8 +84,8 @@ def _parse_pdu_data(raw_pdu_data: StringByteTable) -> PDU:
17284
rated_current=str(pdu_data[6]),
17385
rated_frequency=str(pdu_data[7]),
17486
rated_va=str(pdu_data[8]),
175-
device_capabilities=pdu_data[9] if isinstance(pdu_data[9], list) else [int(pdu_data[9])],
176-
pole_capabilities=pdu_data[10] if isinstance(pdu_data[10], list) else [int(pdu_data[10])],
87+
device_capabilities=InletDeviceCapabilities.from_snmp_bits(pdu_data[9]),
88+
pole_capabilities=InletPoleCapabilities.from_snmp_bits(pdu_data[10]),
17789
plug_descriptor=str(pdu_data[11]),
17890
enable_state=str(pdu_data[12]),
17991
)
@@ -235,9 +147,7 @@ def _parse_sensor_data(sensor_data: StringByteTable) -> dict[str, dict[str, Sens
235147
decimal_digits_int,
236148
),
237149
sensor_unit=sensor_unit.strip(),
238-
sensor_thresholds=enabled_thresholds
239-
if isinstance(enabled_thresholds, list)
240-
else [int(enabled_thresholds)],
150+
sensor_thresholds=InletSensorEnabledThresholds.from_snmp_bits(enabled_thresholds),
241151
)
242152

243153
return sensors
@@ -250,9 +160,9 @@ def parse_raritan_inlet_sensors(
250160
return None
251161

252162
section = RaritanData(pdu=_parse_pdu_data(raw_pdu_data))
253-
if int(section.pdu.device_capabilities[3]) & RESIDUAL_BITMASK:
163+
if section.pdu.device_capabilities.residualCurrent:
254164
section.sensors.update(_parse_sensor_data(string_table[1]))
255-
if int(section.pdu.pole_capabilities[3]) & RESIDUAL_BITMASK:
165+
if section.pdu.pole_capabilities.residualCurrent:
256166
section.sensors.update(_parse_sensor_data(string_table[2]))
257167

258168
return section
@@ -346,16 +256,19 @@ def _create_levels(
346256
params: Params,
347257
sensor_data: Sensor,
348258
) -> NoLevelsT | FixedLevelsT:
349-
thresholds = sensor_data.sensor_thresholds[0]
350-
has_warn = thresholds & 0b00100000 and sensor_data.sensor_values.sensor_upper_warn != 0
351-
has_crit = thresholds & 0b00010000 and sensor_data.sensor_values.sensor_upper_crit != 0
352-
353-
if has_warn or has_crit:
354-
levels_upper_warn = sensor_data.sensor_values.sensor_upper_warn if has_warn else None
355-
levels_upper_crit = sensor_data.sensor_values.sensor_upper_crit if has_crit else None
356-
return ("fixed", (levels_upper_warn, levels_upper_crit))
357259

358-
return params.get("residual_levels", ("no_levels", None))
260+
has_warn = sensor_data.sensor_thresholds.upperWarning and sensor_data.sensor_values.sensor_upper_warn != 0
261+
has_crit = sensor_data.sensor_thresholds.upperCritical and sensor_data.sensor_values.sensor_upper_crit != 0
262+
263+
if params.get("residual_levels", False):
264+
levels_upper = params.get("residual_levels", NoLevelsT)
265+
elif has_warn or has_crit:
266+
levels_upper_warn = sensor_data.sensor_values.sensor_upper_warn if has_warn else has_crit
267+
levels_upper_crit = sensor_data.sensor_values.sensor_upper_crit if has_crit else has_warn
268+
levels_upper = ("fixed", (levels_upper_warn, levels_upper_crit))
269+
else:
270+
levels_upper = NoLevelsT
271+
return levels_upper
359272

360273

361274
def _check_missing_levels(

0 commit comments

Comments
 (0)