Files
security-alert-center/backend/app/services/host_health.py
T
PTah 42f31b3bb5 fix: dashboard and hosts count SSH and RDP daily reports
analytics-a4: daily_reports_24h uses report.daily.*; sac-deploy enables timers
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-29 16:31:09 +10:00

65 lines
2.0 KiB
Python

"""Статус агентов по событиям agent.heartbeat / report.daily.* в БД."""
from datetime import datetime, timezone
from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.models import Event, Host
HEARTBEAT_TYPE = "agent.heartbeat"
DAILY_REPORT_SSH = "report.daily.ssh"
DAILY_REPORT_RDP = "report.daily.rdp"
DAILY_REPORT_TYPES = (DAILY_REPORT_SSH, DAILY_REPORT_RDP)
def max_event_time_by_host(db: Session, event_type: str) -> dict[int, datetime]:
rows = db.execute(
select(Event.host_id, func.max(Event.received_at))
.where(Event.type == event_type)
.group_by(Event.host_id)
).all()
return {int(host_id): ts for host_id, ts in rows if ts is not None}
def max_daily_report_time_by_host(db: Session) -> dict[int, datetime]:
rows = db.execute(
select(Event.host_id, func.max(Event.received_at))
.where(Event.type.in_(DAILY_REPORT_TYPES))
.group_by(Event.host_id)
).all()
return {int(host_id): ts for host_id, ts in rows if ts is not None}
def agent_status(
last_heartbeat_at: datetime | None,
*,
stale_minutes: int,
now: datetime | None = None,
) -> str:
"""
online — heartbeat не старше порога;
stale — был хост, но heartbeat устарел или отсутствует;
unknown — heartbeat ещё не приходил.
"""
if last_heartbeat_at is None:
return "unknown"
ref = now or datetime.now(timezone.utc)
hb = last_heartbeat_at
if hb.tzinfo is None:
hb = hb.replace(tzinfo=timezone.utc)
age_sec = (ref - hb).total_seconds()
if age_sec > stale_minutes * 60:
return "stale"
return "online"
def count_stale_hosts(db: Session, stale_minutes: int) -> int:
hb_map = max_event_time_by_host(db, HEARTBEAT_TYPE)
host_ids = db.scalars(select(Host.id)).all()
return sum(
1
for host_id in host_ids
if agent_status(hb_map.get(host_id), stale_minutes=stale_minutes) == "stale"
)