Add SNMP monitor collection
This commit is contained in:
+45
-4
@@ -7,11 +7,12 @@ from sqlalchemy.exc import SQLAlchemyError
|
||||
from sqlalchemy.orm import Session
|
||||
import httpx
|
||||
|
||||
from app.collectors.snmp import SnmpCheckConfig, SnmpCheckResult, run_snmp_check
|
||||
from app.collectors.website import WebsiteCheckConfig, run_website_check
|
||||
from app.collectors.network import PingCheckConfig, TcpCheckConfig, run_ping_check, run_tcp_check
|
||||
from app.config import settings
|
||||
from app.db import session_scope
|
||||
from app.models import AlertRule, Asset, CheckResult, Incident, Monitor, NotificationChannel
|
||||
from app.models import AlertRule, Asset, CheckResult, Credential, Incident, Metric, Monitor, NotificationChannel
|
||||
from app.secrets import decrypt_secret
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -47,7 +48,7 @@ class Scheduler:
|
||||
def _load_due_monitors(self, db: Session) -> list[Monitor]:
|
||||
now = datetime.now(UTC)
|
||||
monitors = db.scalars(
|
||||
select(Monitor).where(Monitor.monitor_type.in_(["http", "ping", "tcp"])).order_by(Monitor.id).limit(50)
|
||||
select(Monitor).where(Monitor.monitor_type.in_(["http", "ping", "tcp", "snmp"])).order_by(Monitor.id).limit(50)
|
||||
).all()
|
||||
due: list[Monitor] = []
|
||||
for monitor in monitors:
|
||||
@@ -60,7 +61,7 @@ class Scheduler:
|
||||
return due
|
||||
|
||||
async def _run_monitor(self, db: Session, monitor: Monitor) -> None:
|
||||
result = await self._collect_monitor_result(monitor)
|
||||
result = await self._collect_monitor_result(db, monitor)
|
||||
now = datetime.now(UTC)
|
||||
|
||||
monitor.status = result.status
|
||||
@@ -76,6 +77,18 @@ class Scheduler:
|
||||
)
|
||||
db.flush()
|
||||
|
||||
for metric in getattr(result, "metrics", []):
|
||||
db.add(
|
||||
Metric(
|
||||
monitor_id=monitor.id,
|
||||
name=metric.name,
|
||||
value=metric.value,
|
||||
unit=metric.unit,
|
||||
observed_at=now,
|
||||
)
|
||||
)
|
||||
db.flush()
|
||||
|
||||
if monitor.asset_id is not None:
|
||||
asset = db.get(Asset, monitor.asset_id)
|
||||
if asset is not None:
|
||||
@@ -87,7 +100,7 @@ class Scheduler:
|
||||
|
||||
logger.info("Checked %s: %s (%s ms)", monitor.name, result.status, result.response_time_ms)
|
||||
|
||||
async def _collect_monitor_result(self, monitor: Monitor):
|
||||
async def _collect_monitor_result(self, db: Session, monitor: Monitor):
|
||||
if monitor.monitor_type == "http":
|
||||
config = WebsiteCheckConfig(
|
||||
url=monitor.target,
|
||||
@@ -115,8 +128,36 @@ class Scheduler:
|
||||
)
|
||||
return await run_tcp_check(config)
|
||||
|
||||
if monitor.monitor_type == "snmp":
|
||||
return await self._collect_snmp_monitor_result(db, monitor)
|
||||
|
||||
raise ValueError(f"Unsupported monitor type: {monitor.monitor_type}")
|
||||
|
||||
async def _collect_snmp_monitor_result(self, db: Session, monitor: Monitor) -> SnmpCheckResult:
|
||||
profile_id = monitor.config.get("credential_profile_id")
|
||||
if not isinstance(profile_id, int):
|
||||
return SnmpCheckResult(status="down", response_time_ms=None, message="SNMP credential profile is not configured")
|
||||
|
||||
profile = db.get(Credential, profile_id)
|
||||
if profile is None or profile.credential_type != "snmp":
|
||||
return SnmpCheckResult(status="down", response_time_ms=None, message="SNMP credential profile was not found")
|
||||
|
||||
community = decrypt_secret(profile.encrypted_secret)
|
||||
if not community:
|
||||
return SnmpCheckResult(status="down", response_time_ms=None, message="SNMP credential profile has no usable community string")
|
||||
|
||||
extra = dict(profile.extra or {})
|
||||
config = SnmpCheckConfig(
|
||||
host=monitor.target,
|
||||
community=community,
|
||||
item_id=str(monitor.config.get("item_id") or ""),
|
||||
item_type=str(monitor.config.get("item_type") or ""),
|
||||
port=int(extra.get("port") or 161),
|
||||
timeout_seconds=float(extra.get("timeout_seconds") or 5),
|
||||
retries=int(extra.get("retries") or 1),
|
||||
)
|
||||
return await run_snmp_check(config)
|
||||
|
||||
async def _evaluate_rule(self, db: Session, monitor: Monitor, rule: AlertRule, now: datetime, message: str) -> None:
|
||||
open_incident = db.scalar(
|
||||
select(Incident).where(
|
||||
|
||||
Reference in New Issue
Block a user