A preset can now read a vendor's tables: one row per disk, volume, fan or
sensor. The scheduled poll discovers the rows by walking the device: on its
first poll, after a save, and then hourly, and only while it answers. Each row
becomes readings named by the device's own labels ("Disk 1 status"). Only the
columns a row answered are asked for.
A vendor status, whether a code (Synology) or a word (Asustor), is a state
reading. It is stored as a level (0 OK, 1 busy, 2 warning, 3 failed), so one
alert rule covers any vendor, and shown in the device's own word.
- Synology: system, power and fan status and temperature; per disk: status,
health, temperature and bad sectors; per volume: RAID state (scrubbing,
parity check, rebuild, degraded), free space and size.
- Asustor: per disk: status, SMART and temperature; per volume: status, free
space and size; CPU and system temperature, free memory and fans.
From ADM's ASUSTOR-DISK-MIB and ASUSTOR-SYSTEM-MIB.
- Linux server: plus each physical disk's 1-minute busy % (UCD-DISKIO-MIB).
Migration snmp_002_table_rows adds tables, tables_at and texts to
snmp_devices. Milestone 530.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -65,7 +65,8 @@ def test_one_get_carries_every_oid_and_answers_come_back_by_key(monkeypatch):
|
||||
[("1.1", "uptime"), ("1.2", "fan"), ("1.3", "label")])
|
||||
assert len(calls) == 1 and calls[0][1] == ["1.1", "1.2", "1.3"] and calls[0][2] == {"lookupMib": False}
|
||||
assert calls[0][0] == ("community", "public", 1)
|
||||
assert result.readings == {"uptime": 4200.0} and result.missing == ["fan", "label"] and result.error is None
|
||||
assert result.readings == {"uptime": 4200.0} and result.missing == ["fan"] and result.error is None
|
||||
assert result.texts == {"label": "eth0"} # a word, for a state reading
|
||||
|
||||
|
||||
def test_v1_drops_the_oid_it_does_not_know_and_asks_again(monkeypatch):
|
||||
@@ -151,3 +152,42 @@ def test_close_engine_never_raises():
|
||||
|
||||
poller._close_engine(Engine())
|
||||
poller._close_engine(object())
|
||||
|
||||
|
||||
def _walk(monkeypatch, columns, error=None):
|
||||
"""Walk tables against a device whose columns are {column oid: {index: value}}."""
|
||||
walked = []
|
||||
|
||||
async def walk(engine, auth, transport, ctx, obj, **opts):
|
||||
walked.append(obj)
|
||||
if error:
|
||||
yield error, None, None, []
|
||||
return
|
||||
for index, value in columns.get(obj, {}).items():
|
||||
yield None, None, None, [(f"{obj}.{index}", value)]
|
||||
|
||||
monkeypatch.setattr(poller, "_pysnmp_available", lambda: True)
|
||||
api = _fake_api(lambda oids: (None, None, None, []), [])
|
||||
api.walk = walk
|
||||
monkeypatch.setattr(poller, "_api", lambda: api)
|
||||
return walked
|
||||
|
||||
|
||||
def test_a_table_walk_finds_each_row_its_label_and_the_columns_it_answered(monkeypatch):
|
||||
from plugins.snmp.readings import Column, Table
|
||||
disks = Table("t", "disk", "9.2", "Disk", (Column("9.5", "status", "status"), Column("9.6", "temp", "temp")),
|
||||
rows_like=r"Disk \d+")
|
||||
walked = _walk(monkeypatch, {"9.2": {"1": "Disk 1", "0": "Disk 0", "7": "Cache"},
|
||||
"9.5": {"0": 1, "1": 1, "7": 1}, "9.6": {"1": 38}})
|
||||
scan = asyncio.run(poller.walk_tables("10.0.0.2", 161, Credentials(community="public"), [disks]))
|
||||
assert walked == ["9.2", "9.5", "9.6"] and scan.error is None
|
||||
assert scan.rows == {"t": [{"index": "0", "label": "Disk 0", "columns": ["status"]},
|
||||
{"index": "1", "label": "Disk 1", "columns": ["status", "temp"]}]}
|
||||
|
||||
|
||||
def test_a_table_walk_with_no_reply_says_what_to_check(monkeypatch):
|
||||
from plugins.snmp.readings import Column, Table
|
||||
_walk(monkeypatch, {}, error="No SNMP response received before timeout")
|
||||
scan = asyncio.run(poller.walk_tables("10.0.0.2", 161, Credentials(community="public"),
|
||||
[Table("t", "", "", "Fan", (Column("9.2", "fan", "Fan speed"),))]))
|
||||
assert scan.rows == {} and scan.error.startswith("No reply from 10.0.0.2:161")
|
||||
|
||||
Reference in New Issue
Block a user