from __future__ import annotations import json import time from typing import Any from urllib.error import URLError from urllib.parse import urlencode from urllib.request import urlopen from flask import jsonify from .. import settings _LAB_STATUS_CACHE: dict[str, Any] = {"ts": 0.0, "value": None} def _vm_query(expr: str) -> float | None: url = f"{settings.VM_BASE_URL}/api/v1/query?{urlencode({'query': expr})}" with urlopen(url, timeout=settings.VM_QUERY_TIMEOUT_SEC) as resp: payload = json.loads(resp.read().decode("utf-8")) if payload.get("status") != "success": return None result = (payload.get("data") or {}).get("result") or [] if not result: return None values: list[float] = [] for item in result: try: values.append(float(item["value"][1])) except (KeyError, IndexError, TypeError, ValueError): continue if not values: return None return max(values) def _http_ok(url: str, expect_substring: str | None = None) -> bool: try: with urlopen(url, timeout=settings.HTTP_CHECK_TIMEOUT_SEC) as resp: if getattr(resp, "status", 200) != 200: return False if expect_substring: chunk = resp.read(4096).decode("utf-8", errors="ignore") return expect_substring in chunk return True except (URLError, TimeoutError, ValueError): return False def register(app) -> None: @app.route("/api/lab/status") def lab_status() -> Any: now = time.time() cached = _LAB_STATUS_CACHE.get("value") if cached and (now - float(_LAB_STATUS_CACHE.get("ts", 0.0)) < settings.LAB_STATUS_CACHE_SEC): return jsonify(cached) connected = False atlas_up = False atlas_known = False atlas_source = "unknown" oceanus_up = False oceanus_known = False oceanus_source = "unknown" # Atlas try: atlas_grafana_ok = _http_ok(settings.GRAFANA_HEALTH_URL, expect_substring="ok") if atlas_grafana_ok: connected = True atlas_up = True atlas_known = True atlas_source = "grafana" except Exception: pass if not atlas_known: try: value = _vm_query("up") if value is not None: connected = True atlas_known = True atlas_up = value > 0 atlas_source = "victoria-metrics" except Exception: pass # Oceanus (node-exporter direct probe) try: if _http_ok(settings.OCEANUS_NODE_EXPORTER_URL): connected = True oceanus_known = True oceanus_up = True oceanus_source = "node-exporter" except Exception: pass payload = { "connected": connected, "atlas": {"up": atlas_up, "known": atlas_known, "source": atlas_source}, "oceanus": {"up": oceanus_up, "known": oceanus_known, "source": oceanus_source}, "checked_at": int(now), } _LAB_STATUS_CACHE["ts"] = now _LAB_STATUS_CACHE["value"] = payload return jsonify(payload)