refactor: rename package directory fabledscryer → roundtable
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
import socket
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from fabledscryer.core.alerts import record_metric
|
||||
from fabledscryer.models.hosts import Host
|
||||
from fabledscryer.models.monitors import DnsResult, DnsStatus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def dns_check(host: Host, session: AsyncSession) -> None:
|
||||
"""Resolve host and write dns_result + metrics."""
|
||||
result = await _resolve(host.address)
|
||||
|
||||
if result["resolved"]:
|
||||
resolved_ip = result["ip"] # first A/AAAA record (stored in DB)
|
||||
all_ips = result["all_ips"]
|
||||
|
||||
# Check against expected IP: pass only if at least one returned record matches
|
||||
if host.dns_expected_ip and host.dns_expected_ip not in all_ips:
|
||||
status = DnsStatus.failed
|
||||
dns_resolved_metric = 0.0
|
||||
else:
|
||||
status = DnsStatus.resolved
|
||||
dns_resolved_metric = 1.0
|
||||
|
||||
# Detect IP change vs most recent successful resolution
|
||||
prev_result = await session.execute(
|
||||
select(DnsResult)
|
||||
.where(DnsResult.host_id == host.id, DnsResult.status == DnsStatus.resolved)
|
||||
.order_by(DnsResult.resolved_at.desc())
|
||||
.limit(1)
|
||||
)
|
||||
prev = prev_result.scalar_one_or_none()
|
||||
ip_changed = 1.0 if (prev and prev.resolved_ip != resolved_ip) else 0.0
|
||||
else:
|
||||
resolved_ip = None
|
||||
status = DnsStatus.failed
|
||||
dns_resolved_metric = 0.0
|
||||
ip_changed = 0.0
|
||||
|
||||
session.add(DnsResult(
|
||||
host_id=host.id,
|
||||
status=status,
|
||||
resolved_ip=resolved_ip,
|
||||
))
|
||||
await session.flush()
|
||||
|
||||
await record_metric(session, "dns", host.name, "resolved", dns_resolved_metric)
|
||||
await record_metric(session, "dns", host.name, "ip_changed", ip_changed)
|
||||
|
||||
|
||||
async def _resolve(address: str) -> dict:
|
||||
"""Resolve hostname. Returns all A/AAAA records; stores first as resolved_ip."""
|
||||
try:
|
||||
loop = asyncio.get_running_loop()
|
||||
infos = await loop.run_in_executor(
|
||||
None, socket.getaddrinfo, address, None, socket.AF_UNSPEC, socket.SOCK_STREAM
|
||||
)
|
||||
if infos:
|
||||
all_ips = [info[4][0] for info in infos]
|
||||
return {"resolved": True, "ip": all_ips[0], "all_ips": all_ips}
|
||||
return {"resolved": False, "ip": None, "all_ips": []}
|
||||
except Exception as exc:
|
||||
logger.debug(f"DNS resolution failed for {address}: {exc}")
|
||||
return {"resolved": False, "ip": None, "all_ips": []}
|
||||
Reference in New Issue
Block a user