"""Unified check dispatcher: Monitor -> normalised MonitorResult fields. One entry point, `run_monitor`, dispatches on Monitor.type to the per-type probes (ping/dns/http) and returns a dict with every MonitorResult field so the scheduler can persist a row uniformly regardless of type. """ from __future__ import annotations import logging from steward.models.monitors import Monitor, MonitorType from .ping import tcp_check, icmp_check, DEFAULT_TCP_PORT from .dns import dns_check from .http import http_check logger = logging.getLogger(__name__) # Every result carries these keys; type-specific probes fill the relevant ones. _BLANK = { "is_up": False, "response_ms": None, "status_code": None, "resolved_ip": None, "content_matched": None, "tls_expires_at": None, "error_msg": None, } async def run_monitor(monitor: Monitor) -> dict: """Run one monitor's check and return a full MonitorResult field dict.""" cfg = monitor.config result = dict(_BLANK) try: if monitor.type == MonitorType.icmp.value: result.update(await icmp_check(monitor.target)) elif monitor.type == MonitorType.tcp.value: result.update(await tcp_check(monitor.target, int(cfg.get("port") or DEFAULT_TCP_PORT))) elif monitor.type == MonitorType.dns.value: result.update(await dns_check(monitor.target, cfg.get("expected_ip") or None)) elif monitor.type == MonitorType.http.value: result.update(await http_check( url=monitor.target, method=cfg.get("method", "GET"), expected_status=int(cfg.get("expected_status", 200) or 200), content_match=cfg.get("content_match", "") or "", headers=cfg.get("headers") or {}, timeout_seconds=int(cfg.get("timeout_seconds", 10) or 10), follow_redirects=bool(cfg.get("follow_redirects", True)), verify_ssl=bool(cfg.get("verify_ssl", True)), )) else: result["error_msg"] = f"Unknown monitor type {monitor.type!r}" except Exception as exc: # a probe should never take the scheduler down logger.exception("Monitor %r (%s) check raised", monitor.name, monitor.type) result["error_msg"] = str(exc)[:512] return result