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>
49 lines
2.7 KiB
Python
49 lines
2.7 KiB
Python
# plugins/snmp/models.py
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
from sqlalchemy import JSON, DateTime, Integer, String, Text
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from steward.core.ids import time_ordered_id
|
|
from steward.models.base import Base
|
|
|
|
|
|
class SnmpDevice(Base):
|
|
"""A device Steward polls over SNMP, edited on the SNMP page.
|
|
|
|
Secrets (the community, the v3 keys) are stored encrypted. The last_*
|
|
columns are the newest poll's outcome, written by the poller, so a page
|
|
can say why a device isn't answering without asking the device itself.
|
|
`tables` is the rows the poller found in the presets' vendor tables (a
|
|
disk or volume each), as of `tables_at`; `texts` is each state reading's
|
|
newest answer in the device's own word.
|
|
"""
|
|
__tablename__ = "snmp_devices"
|
|
|
|
id: Mapped[str] = mapped_column(String(36), primary_key=True, default=time_ordered_id)
|
|
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
|
|
address: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
port: Mapped[int] = mapped_column(Integer, nullable=False, default=161)
|
|
version: Mapped[str] = mapped_column(String(3), nullable=False, default="2c")
|
|
community: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
v3_user: Mapped[str] = mapped_column(String(100), nullable=False, default="")
|
|
v3_auth: Mapped[str] = mapped_column(String(16), nullable=False, default="none")
|
|
v3_auth_key: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
v3_priv: Mapped[str] = mapped_column(String(16), nullable=False, default="none")
|
|
v3_priv_key: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
presets: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
|
interfaces: Mapped[str] = mapped_column(String(255), nullable=False, default="")
|
|
custom: Mapped[str] = mapped_column(Text, nullable=False, default="")
|
|
host_id: Mapped[str | None] = mapped_column(String(36), nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), nullable=False, default=lambda: datetime.now(timezone.utc))
|
|
last_polled_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
last_ok_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
last_error: Mapped[str | None] = mapped_column(Text, nullable=True)
|
|
missing: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
|
tables: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
|
tables_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
|
texts: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|