#5255 "SNMP — Devices: status in words and key readings". - Verdict first: "All N devices answering", "ups: no reply since 14m ago", "N of M devices not answering", or "N not polled yet". - Devices that stopped answering come first, then those never polled, then the rest. - One header row: Device / Readings / Polled. - The name is the link; the separate "detail →" is gone. - The hollow dot is replaced by words: "no data yet", or "no reply since …; readings are from then". - Key readings: the first two in config order, gauges ahead of counters. Units come from the label suffix: _centisecs as a duration, _pct, _dV as volts, _octets as bytes total. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
57 lines
2.8 KiB
Python
57 lines
2.8 KiB
Python
"""The dashboard SNMP widget (#5255): a verdict, devices that stopped answering
|
|
first, status in words, and key readings with their units."""
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import pytest
|
|
|
|
from plugins.snmp.routes import key_readings, snmp_reading
|
|
from tests._templates import render_plugin_template
|
|
|
|
|
|
@pytest.mark.parametrize("label, value, says", [
|
|
("uptime_centisecs", 105_120_000, ("uptime", "12d 4h")),
|
|
("battery_charge_pct", 99.6, ("battery charge", "100%")),
|
|
("input_voltage_dV", 1203, ("input voltage", "120.3 V")),
|
|
("if1_in_octets", 1536, ("if1 in", "1.5 KiB total")),
|
|
("battery_status", 2, ("battery status", "2")),
|
|
("temperature", 41.25, ("temperature", "41.25")),
|
|
])
|
|
def test_a_reading_reads_with_its_unit(label, value, says):
|
|
assert snmp_reading(label, value) == says
|
|
|
|
|
|
def test_key_readings_prefer_gauges_to_counters_in_config_order():
|
|
oids = [{"oid": "a", "label": "if1_in_octets"}, {"oid": "b", "label": "uptime_centisecs"},
|
|
{"oid": "c", "label": "if1_out_octets"}, {"oid": "1.2.3"}]
|
|
readings = {"if1_in_octets": 10, "uptime_centisecs": 6000, "if1_out_octets": 20, "1.2.3": 7}
|
|
assert [n for n, _ in key_readings(oids, readings)] == ["uptime", "1.2.3"]
|
|
assert [n for n, _ in key_readings(oids[:1] + oids[2:3], readings)] == ["if1 in", "if1 out"]
|
|
assert key_readings(oids, {}) == []
|
|
|
|
|
|
def _dev(name, reach, minutes_ago=None, key=()):
|
|
at = datetime.now(timezone.utc) - timedelta(minutes=minutes_ago) if minutes_ago is not None else None
|
|
return {"name": name, "reach": reach, "at": at, "readings": {}, "key": list(key)}
|
|
|
|
|
|
async def test_a_silent_device_leads_with_since_when(app):
|
|
devices = [_dev("ups", "stale", 14, [("battery charge", "100%")]), _dev("printer", "pending"),
|
|
_dev("switch", "up", 1, [("uptime", "12d 4h")])]
|
|
async with app.test_request_context("/plugins/snmp/widget"):
|
|
page = await render_plugin_template(app, "snmp", "snmp/widget.html", devices=devices, more=0,
|
|
counts={"stale": 1, "pending": 1, "up": 1}, total=3)
|
|
page = " ".join(page.split())
|
|
assert "ups: no reply since" in page and ">14m ago<" in page
|
|
assert page.count("<th>Device</th>") == 1
|
|
assert page.index('title="ups"') < page.index('title="printer"') < page.index('title="switch"')
|
|
assert "no data yet" in page and "uptime</span> 12d 4h" in page
|
|
assert "detail →" not in page and "○" not in page
|
|
|
|
|
|
async def test_all_answering_says_so(app):
|
|
async with app.test_request_context("/plugins/snmp/widget"):
|
|
page = await render_plugin_template(app, "snmp", "snmp/widget.html",
|
|
devices=[_dev("switch", "up", 1)], more=0,
|
|
counts={"stale": 0, "pending": 0, "up": 1}, total=1)
|
|
assert "All 1 device answering." in page and "—" in page
|