#5060 "One evaluation path: record_metrics batch API; host-agent ingest evaluates rules". - record_metrics(session, rows) writes metric rows and evaluates the rules that watch them, reading rules, states and maintenance windows once per batch. Each rule sees its key's samples oldest first, so an agent catching up after an outage counts consecutive checks as they happened. - Host-agent ingest now goes through it. Before, it wrote plugin_metrics directly and no host_agent rule could ever fire. The catalog gains the per-mount disk_used_pct ("<host>:<mount>"). - MetricBatch gathers a transaction's samples; the docker ingest, traefik, unifi, snmp, monitor and self-monitor writers record one batch each instead of a rule query per sample. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
71 lines
2.2 KiB
Python
71 lines
2.2 KiB
Python
# plugins/snmp/scheduler.py
|
|
from __future__ import annotations
|
|
import logging
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from quart import Quart
|
|
|
|
from steward.core.scheduler import ScheduledTask
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def make_poll_task(app: "Quart") -> ScheduledTask:
|
|
interval = app.config["PLUGINS"]["snmp"].get("poll_interval_seconds", 60)
|
|
|
|
async def poll() -> None:
|
|
await _do_poll(app)
|
|
|
|
return ScheduledTask(
|
|
name="snmp_poll",
|
|
coro_factory=poll,
|
|
interval_seconds=int(interval),
|
|
run_on_startup=True,
|
|
)
|
|
|
|
|
|
async def _do_poll(app: "Quart") -> None:
|
|
from .poller import poll_device
|
|
from steward.core.alerts import MetricBatch
|
|
|
|
devices: list[dict] = app.config["PLUGINS"]["snmp"].get("devices", [])
|
|
if not devices:
|
|
return
|
|
|
|
async with app.db_sessionmaker() as session:
|
|
async with session.begin():
|
|
batch = MetricBatch()
|
|
for device in devices:
|
|
if not isinstance(device, dict):
|
|
continue
|
|
name = device.get("name") or device.get("host", "unknown")
|
|
host = device.get("host", "")
|
|
port = int(device.get("port", 161))
|
|
community = device.get("community", "public")
|
|
version = str(device.get("version", "2c"))
|
|
oids = device.get("oids", [])
|
|
|
|
if not host or not oids:
|
|
continue
|
|
|
|
try:
|
|
readings = await poll_device(host, port, community, version, oids)
|
|
except Exception:
|
|
logger.exception("SNMP poll failed for device %s (%s)", name, host)
|
|
continue
|
|
|
|
for label, value in readings.items():
|
|
batch.add(
|
|
source_module="snmp",
|
|
resource_name=name,
|
|
metric_name=label,
|
|
value=value,
|
|
)
|
|
|
|
if readings:
|
|
logger.debug("SNMP polled %s (%s): %d OID(s)", name, host, len(readings))
|
|
else:
|
|
logger.debug("SNMP polled %s (%s): no readings", name, host)
|
|
await batch.record(session)
|