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:
+11
-7
@@ -11,7 +11,7 @@ from steward.core.crypto import decrypt_secret, encrypt_secret
|
||||
|
||||
from .models import SnmpDevice
|
||||
from .poller import AUTH_PROTOCOLS, PRIV_PROTOCOLS, Credentials
|
||||
from .readings import PRESETS, Reading, device_readings, parse_interfaces
|
||||
from .readings import PRESETS, Reading, device_readings, parse_interfaces, tables_for
|
||||
|
||||
VERSIONS = ("1", "2c", "3")
|
||||
PRESET_IDS = set(PRESETS) | {"interfaces"}
|
||||
@@ -22,13 +22,15 @@ class FormError(ValueError):
|
||||
"""What is wrong with a submitted device form, said to the person."""
|
||||
|
||||
|
||||
def readings_for(device: SnmpDevice) -> list[Reading]:
|
||||
"""What the device is asked for. A row saved through the form always
|
||||
parses; one that somehow doesn't is asked for nothing rather than raising
|
||||
in the poll loop."""
|
||||
def readings_for(device: SnmpDevice, tables: dict | None = None) -> list[Reading]:
|
||||
"""What the device is asked for, its vendor tables' rows included (the
|
||||
stored ones, or `tables` when the poll has just found them). A row saved
|
||||
through the form always parses; one that somehow doesn't is asked for
|
||||
nothing rather than raising in the poll loop."""
|
||||
try:
|
||||
return device_readings(device.presets or [], parse_interfaces(device.interfaces),
|
||||
device.custom, device.version)
|
||||
device.custom, device.version,
|
||||
device.tables if tables is None else tables)
|
||||
except ValueError:
|
||||
return []
|
||||
|
||||
@@ -84,7 +86,9 @@ def parse_form(form, existing: SnmpDevice | None = None) -> dict:
|
||||
numbers = parse_interfaces(interfaces)
|
||||
if "interfaces" in presets and not numbers:
|
||||
raise FormError("Interface traffic needs the interface numbers to read, e.g. 1, 2 or 1-4.")
|
||||
if not device_readings(presets, numbers, custom, version):
|
||||
# A NAS preset's rows are found by the first poll, so it reads
|
||||
# nothing until then and still counts as a choice.
|
||||
if not device_readings(presets, numbers, custom, version) and not tables_for(presets):
|
||||
raise FormError("Choose at least one preset or add a custom reading.")
|
||||
except ValueError as exc:
|
||||
raise FormError(str(exc)) from None
|
||||
|
||||
Reference in New Issue
Block a user