Add asset-based monitor setup

This commit is contained in:
Keith Smith
2026-05-23 21:07:05 -06:00
parent 8b5dea152e
commit bd6c508c94
9 changed files with 858 additions and 74 deletions
+88 -20
View File
@@ -4,14 +4,42 @@ from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.api.credentials import SNMP_CREDENTIAL_TYPE
from app.auth.dependencies import get_current_user, require_role
from app.db.session import get_db
from app.models import AlertRule, Asset, CheckResult, Incident, Monitor, User
from app.schemas.core import CheckResultRead, MonitorCreate, MonitorRead, MonitorUpdate, PingMonitorCreate, TcpMonitorCreate, WebsiteMonitorCreate
from app.models import AlertRule, Asset, CheckResult, Credential, Incident, Monitor, User
from app.schemas.core import (
CheckResultRead,
MonitorCreate,
MonitorRead,
MonitorUpdate,
PingMonitorCreate,
SnmpMonitorsCreate,
TcpMonitorCreate,
WebsiteMonitorCreate,
)
router = APIRouter(prefix="/monitors", tags=["monitors"])
def _get_asset_or_404(db: Session, asset_id: int) -> Asset:
asset = db.get(Asset, asset_id)
if asset is None:
raise HTTPException(status_code=404, detail="Asset not found")
return asset
def _resolve_asset_id(db: Session, asset_id: int | None, create_asset: bool, asset: Asset) -> int | None:
if asset_id is not None:
_get_asset_or_404(db, asset_id)
return asset_id
if not create_asset:
return None
db.add(asset)
db.flush()
return asset.id
@router.get("", response_model=list[MonitorRead])
def list_monitors(_: User = Depends(get_current_user), db: Session = Depends(get_db)) -> list[Monitor]:
return list(db.scalars(select(Monitor).order_by(Monitor.name)).all())
@@ -23,6 +51,8 @@ def create_monitor(
_: User = Depends(require_role("admin")),
db: Session = Depends(get_db),
) -> Monitor:
if payload.asset_id is not None:
_get_asset_or_404(db, payload.asset_id)
monitor = Monitor(**payload.model_dump())
db.add(monitor)
db.commit()
@@ -36,12 +66,12 @@ def create_website_monitor(
_: User = Depends(require_role("admin")),
db: Session = Depends(get_db),
) -> Monitor:
asset_id: int | None = None
if payload.create_asset:
asset = Asset(name=payload.name, asset_type="website", address=payload.url, status="unknown", extra={})
db.add(asset)
db.flush()
asset_id = asset.id
asset_id = _resolve_asset_id(
db,
payload.asset_id,
payload.create_asset,
Asset(name=payload.name, asset_type="website", address=payload.url, status="unknown", extra={}),
)
monitor = Monitor(
asset_id=asset_id,
@@ -86,12 +116,12 @@ def create_ping_monitor(
_: User = Depends(require_role("admin")),
db: Session = Depends(get_db),
) -> Monitor:
asset_id: int | None = None
if payload.create_asset:
asset = Asset(name=payload.name, asset_type="host", address=payload.host, status="unknown", extra={})
db.add(asset)
db.flush()
asset_id = asset.id
asset_id = _resolve_asset_id(
db,
payload.asset_id,
payload.create_asset,
Asset(name=payload.name, asset_type="host", address=payload.host, status="unknown", extra={}),
)
monitor = Monitor(
asset_id=asset_id,
@@ -129,13 +159,13 @@ def create_tcp_monitor(
_: User = Depends(require_role("admin")),
db: Session = Depends(get_db),
) -> Monitor:
asset_id: int | None = None
target = f"{payload.host}:{payload.port}"
if payload.create_asset:
asset = Asset(name=payload.name, asset_type="tcp_service", address=target, status="unknown", extra={})
db.add(asset)
db.flush()
asset_id = asset.id
asset_id = _resolve_asset_id(
db,
payload.asset_id,
payload.create_asset,
Asset(name=payload.name, asset_type="tcp_service", address=target, status="unknown", extra={}),
)
monitor = Monitor(
asset_id=asset_id,
@@ -167,6 +197,44 @@ def create_tcp_monitor(
return monitor
@router.post("/snmp/from-discovery", response_model=list[MonitorRead])
def create_snmp_monitors_from_discovery(
payload: SnmpMonitorsCreate,
_: User = Depends(require_role("admin")),
db: Session = Depends(get_db),
) -> list[Monitor]:
asset = _get_asset_or_404(db, payload.asset_id)
profile = db.get(Credential, payload.credential_profile_id)
if profile is None or profile.credential_type != SNMP_CREDENTIAL_TYPE:
raise HTTPException(status_code=404, detail="SNMP credential profile not found")
monitors: list[Monitor] = []
for item in payload.selected_items:
monitor = Monitor(
asset_id=asset.id,
name=f"{asset.name} {item.label}",
monitor_type="snmp",
target=payload.host,
config={
"credential_profile_id": payload.credential_profile_id,
"item_id": item.item_id,
"item_type": item.item_type,
"group": item.group,
"label": item.label,
"unit": item.unit,
},
interval_seconds=payload.interval_seconds,
status="unknown",
)
db.add(monitor)
monitors.append(monitor)
db.commit()
for monitor in monitors:
db.refresh(monitor)
return monitors
@router.get("/{monitor_id}", response_model=MonitorRead)
def get_monitor(monitor_id: int, _: User = Depends(get_current_user), db: Session = Depends(get_db)) -> Monitor:
monitor = db.get(Monitor, monitor_id)