diff --git a/plugins/host_agent/routes.py b/plugins/host_agent/routes.py index fddd49a..510f361 100644 --- a/plugins/host_agent/routes.py +++ b/plugins/host_agent/routes.py @@ -90,6 +90,17 @@ def pick_host_address(current: str | None, reported: str | None) -> str | None: return None +def counts_as_hottest(label: str, celsius: float) -> bool: + """Whether a sensor's reading may set the host's temperature (#5759). + + A Super-I/O chip's AUXTIN inputs are spare thermistor pins, usually left + unconnected, and a floating one reads a junk constant (VDNT-Util01: 110 °C + flat at every load while its CPU package moved 49-87). No running host's + sensor reads 0 or below, and chips shut down before 125. Each sensor's own + row is kept either way, so a wired AUXTIN can still be charted by name.""" + return 0 < celsius < 125 and not label.upper().startswith("AUXTIN") + + def _expand_sample_to_metrics( sample: dict, host_name: str, recorded_at: datetime ) -> list[PluginMetric]: @@ -186,14 +197,15 @@ def _expand_sample_to_metrics( row("disk_read_bps", host_name, dr_total) row("disk_write_bps", host_name, dw_total) - # Temperatures — per sensor plus the host max for at-a-glance. + # Temperatures — per sensor plus the host max for at-a-glance. The max + # counts only readings that can be real (#5759). max_temp: float | None = None for t in sample.get("temps") or []: c = t.get("celsius") if c is None: continue row("temp_c", f"{host_name}:temp:{t['label']}", c) - if max_temp is None or c > max_temp: + if counts_as_hottest(str(t["label"]), c) and (max_temp is None or c > max_temp): max_temp = c if max_temp is not None: row("temp_c_max", host_name, max_temp) diff --git a/tests/plugins/host_agent/test_expand_metrics.py b/tests/plugins/host_agent/test_expand_metrics.py index e27bf55..5c75780 100644 --- a/tests/plugins/host_agent/test_expand_metrics.py +++ b/tests/plugins/host_agent/test_expand_metrics.py @@ -78,3 +78,18 @@ def test_every_resource_is_the_host_or_a_host_colon_part(): resources = {r.resource_name for r in _expand_sample_to_metrics(sample, "web01", datetime.now(timezone.utc))} assert len(resources) > 5 assert {r for r in resources if r != "web01" and not r.startswith("web01" + sep)} == set() + + +def test_a_sensor_with_nothing_wired_to_it_does_not_set_the_host_s_temperature(): + """#5759: VDNT-Util01's AUXTIN0 read 110 °C flat while its CPU package + moved 49-87, so the host read critical all week. Every sensor keeps its + own row; only readings that can be real set the max.""" + sample = {"ts": "2026-10-14T21:00:00+00:00", + "temps": [{"label": "AUXTIN0", "celsius": 110.0}, + {"label": "PCH_CHIP_CPU_MAX_TEMP", "celsius": 0.0}, + {"label": "AUXTIN3", "celsius": -1.0}, + {"label": "Package id 0", "celsius": 60.0}]} + idx = _index(_expand_sample_to_metrics(sample, "util", datetime.now(timezone.utc))) + assert idx[("temp_c_max", "util")] == 60.0 + assert idx[("temp_c", "util:temp:AUXTIN0")] == 110.0 + assert idx[("temp_c", "util:temp:PCH_CHIP_CPU_MAX_TEMP")] == 0.0