be4654fc72
- Add HTTP monitor plugin: endpoint checking with status history, latency tracking, time-range views - Add SNMP plugin: OID polling, device management, metric recording - Add Docker plugin: container status, resource usage, widget - Traefik: access log widget, request chart widget, expanded routes - UniFi: clients widget, devices widget, expanded poll routes - UPS: history widget, additional routes - Update plugin index with new entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
# plugins/http/scheduler.py
|
|
from __future__ import annotations
|
|
import asyncio
|
|
import json
|
|
import logging
|
|
from datetime import datetime, timezone, timedelta
|
|
|
|
from fabledscryer.core.scheduler import ScheduledTask
|
|
from fabledscryer.core.alerts import record_metric
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def make_task(app) -> ScheduledTask:
|
|
interval = int(
|
|
app.config["PLUGINS"]["http"].get("check_interval_seconds", 60)
|
|
)
|
|
|
|
async def check():
|
|
await _do_checks(app)
|
|
|
|
return ScheduledTask(
|
|
name="http_check",
|
|
coro_factory=check,
|
|
interval_seconds=interval,
|
|
run_on_startup=True,
|
|
)
|
|
|
|
|
|
async def _do_checks(app) -> None:
|
|
from sqlalchemy import select
|
|
from .models import HttpMonitor, HttpResult
|
|
from .checker import run_check
|
|
|
|
cfg = app.config["PLUGINS"]["http"]
|
|
global_interval = int(cfg.get("check_interval_seconds", 60))
|
|
global_timeout = int(cfg.get("default_timeout_seconds", 10))
|
|
global_follow = bool(cfg.get("follow_redirects", True))
|
|
global_verify = bool(cfg.get("verify_ssl", True))
|
|
|
|
now = datetime.now(timezone.utc)
|
|
|
|
async with app.db_sessionmaker() as session:
|
|
result = await session.execute(
|
|
select(HttpMonitor).where(HttpMonitor.enabled == True) # noqa: E712
|
|
)
|
|
monitors = list(result.scalars())
|
|
|
|
# Filter to monitors whose next check time has passed
|
|
due: list[HttpMonitor] = []
|
|
for m in monitors:
|
|
effective_interval = m.check_interval_seconds or global_interval
|
|
if m.last_checked_at is None:
|
|
due.append(m)
|
|
elif (now - m.last_checked_at).total_seconds() >= effective_interval:
|
|
due.append(m)
|
|
|
|
if not due:
|
|
return
|
|
|
|
async def _check_one(monitor: HttpMonitor) -> tuple[HttpMonitor, dict]:
|
|
try:
|
|
headers = json.loads(monitor.headers_json or "{}")
|
|
except (ValueError, TypeError):
|
|
headers = {}
|
|
result = await run_check(
|
|
url=monitor.url,
|
|
method=monitor.method,
|
|
expected_status=monitor.expected_status,
|
|
content_match=monitor.content_match,
|
|
headers=headers,
|
|
timeout_seconds=monitor.timeout_seconds or global_timeout,
|
|
follow_redirects=monitor.follow_redirects if monitor.follow_redirects is not None else global_follow,
|
|
verify_ssl=monitor.verify_ssl if monitor.verify_ssl is not None else global_verify,
|
|
)
|
|
return monitor, result
|
|
|
|
pairs = await asyncio.gather(*[_check_one(m) for m in due], return_exceptions=True)
|
|
|
|
check_time = datetime.now(timezone.utc)
|
|
|
|
async with app.db_sessionmaker() as session:
|
|
async with session.begin():
|
|
for item in pairs:
|
|
if isinstance(item, Exception):
|
|
logger.error("HTTP check task error: %s", item)
|
|
continue
|
|
monitor, res = item
|
|
|
|
session.add(HttpResult(
|
|
monitor_id=monitor.id,
|
|
checked_at=check_time,
|
|
status_code=res["status_code"],
|
|
response_ms=res["response_ms"],
|
|
is_up=res["is_up"],
|
|
content_matched=res["content_matched"],
|
|
error_msg=res["error_msg"],
|
|
tls_expires_at=res["tls_expires_at"],
|
|
))
|
|
|
|
# Update monitor's last_checked_at for next-run scheduling
|
|
db_monitor = await session.get(HttpMonitor, monitor.id)
|
|
if db_monitor:
|
|
db_monitor.last_checked_at = check_time
|
|
|
|
# Alert pipeline
|
|
if res["response_ms"] is not None:
|
|
await record_metric(
|
|
session=session,
|
|
source_module="http",
|
|
resource_name=monitor.name,
|
|
metric_name="response_ms",
|
|
value=res["response_ms"],
|
|
)
|
|
await record_metric(
|
|
session=session,
|
|
source_module="http",
|
|
resource_name=monitor.name,
|
|
metric_name="is_up",
|
|
value=1.0 if res["is_up"] else 0.0,
|
|
)
|