mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 05:30:46 +08:00
Add shared CLI budget/timeout with force-close, parallel port-traffic dispatch, unified shutdown, bounded audit queue, output/log caps, and /metrics probes. Co-authored-by: Cursor <cursoragent@cursor.com>
115 lines
3.6 KiB
Python
115 lines
3.6 KiB
Python
"""Thin integrations / health routes (liveness vs readiness)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
from typing import Any
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from sqlalchemy import text as sql_text
|
|
from sqlalchemy.orm import Session
|
|
|
|
from .config import settings
|
|
from .db import get_db
|
|
from .oclaw_alarm_forwarder import forwarder_status
|
|
|
|
router = APIRouter(tags=["health"])
|
|
|
|
|
|
@router.get("/health/live", status_code=200)
|
|
def health_live() -> dict[str, str]:
|
|
"""Process liveness — no DB or upstream checks."""
|
|
return {"status": "ok", "probe": "live"}
|
|
|
|
|
|
@router.get("/health/ready", status_code=200)
|
|
def health_ready(db: Session = Depends(get_db)) -> dict[str, Any]:
|
|
"""Readiness — DB plus scheduler deployment hint."""
|
|
from .cli_budget import cli_budget_status
|
|
from .db import db_pool_status
|
|
|
|
out: dict[str, Any] = {"status": "ok", "probe": "ready"}
|
|
try:
|
|
db.execute(sql_text("select 1"))
|
|
out["db"] = "up"
|
|
except Exception as exc:
|
|
return {
|
|
"status": "down",
|
|
"probe": "ready",
|
|
"db": "down",
|
|
"error": str(exc)[:240],
|
|
}
|
|
out["db_pool"] = db_pool_status()
|
|
out["cli_budget"] = cli_budget_status()
|
|
inline = bool(getattr(settings, "run_inline_schedulers", True))
|
|
out["schedulers"] = {
|
|
"inline": inline,
|
|
"mode": "inline" if inline else "external_worker",
|
|
"hint": None
|
|
if inline
|
|
else "run `python -m netx_api.worker` for config_sync / lldp_collect / port_traffic",
|
|
}
|
|
return out
|
|
|
|
|
|
@router.get("/v1/integrations/status")
|
|
def integrations_status(db: Session = Depends(get_db)) -> dict:
|
|
"""netx API + DB + oclaw bridge status."""
|
|
netx_api = {"status": "up"}
|
|
|
|
db_status: dict = {"status": "unknown"}
|
|
try:
|
|
t0 = time.monotonic()
|
|
db.execute(sql_text("select 1"))
|
|
db_status = {"status": "up", "latency_ms": int((time.monotonic() - t0) * 1000)}
|
|
except Exception as exc:
|
|
db_status = {"status": "down", "error": str(exc)[:240]}
|
|
|
|
oclaw_status: dict = {"status": "unknown"}
|
|
fwd = forwarder_status()
|
|
if not bool(fwd.get("enabled")):
|
|
oclaw_status = {
|
|
"status": "unknown",
|
|
"mode": "ws",
|
|
"enabled": False,
|
|
"connected": False,
|
|
"error_kind": "disabled",
|
|
"error": "NETX_OCLAW_ALARM_WS_ENABLED=false or missing token/url",
|
|
"forwarder": fwd,
|
|
}
|
|
elif bool(fwd.get("paused")):
|
|
oclaw_status = {
|
|
"status": "unknown",
|
|
"mode": "ws",
|
|
"enabled": True,
|
|
"connected": False,
|
|
"error_kind": "paused",
|
|
"error": "oclaw_alarm_forwarder runtime task paused",
|
|
"forwarder": fwd,
|
|
}
|
|
elif bool(fwd.get("connected")):
|
|
oclaw_status = {
|
|
"status": "up",
|
|
"mode": "ws",
|
|
"enabled": True,
|
|
"connected": True,
|
|
"queue_size": int(fwd.get("queue_size") or 0),
|
|
"published_ok": int(fwd.get("published_ok") or 0),
|
|
"published_fail": int(fwd.get("published_fail") or 0),
|
|
"url": str(fwd.get("url") or ""),
|
|
"forwarder": fwd,
|
|
}
|
|
else:
|
|
oclaw_status = {
|
|
"status": "down",
|
|
"mode": "ws",
|
|
"enabled": True,
|
|
"connected": False,
|
|
"error_kind": "ws_disconnected",
|
|
"error": "oclaw netx-bridge WebSocket not connected",
|
|
"queue_size": int(fwd.get("queue_size") or 0),
|
|
"url": str(fwd.get("url") or ""),
|
|
"forwarder": fwd,
|
|
}
|
|
|
|
return {"netx_api": netx_api, "db": db_status, "oclaw_bridge": oclaw_status}
|