Files
weval-l99/screens-health-check.py.v1
2026-04-16 13:33:37 +02:00

96 lines
3.6 KiB
Python
Executable File

#!/usr/bin/env python3
# /opt/weval-l99/screens-health-check.py
# Check health de tous les ecrans cartographies, ecrit JSON atomique.
# Zero regression: standalone, pas de deps externes, timeout court.
import json, os, sys, time, re, tempfile
from concurrent.futures import ThreadPoolExecutor, as_completed
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
CARTO_FILE = "/var/www/html/cartographie-screens.html"
OUT_FILE = "/var/www/html/api/screens-health.json"
WORKERS = 40 # parallelism
TIMEOUT = 4 # per-check timeout (seconds)
SLOW_MS = 1500 # >1500ms = SLOW
def extract_data():
"""Extract DATA array from cartographie-screens.html."""
if not os.path.exists(CARTO_FILE):
return []
with open(CARTO_FILE, "r", encoding="utf-8") as f:
html = f.read()
m = re.search(r"const DATA = (\[.*?\]);", html, re.DOTALL)
if not m:
return []
try:
return json.loads(m.group(1))
except Exception:
return []
def check_one(entry):
url = entry.get("url")
name = entry.get("name")
t0 = time.time()
status = "DOWN"
code = 0
elapsed_ms = 0
try:
req = Request(url, method="HEAD", headers={"User-Agent": "WEVIA-HealthCheck/1.0"})
resp = urlopen(req, timeout=TIMEOUT)
code = resp.status
elapsed_ms = int((time.time() - t0) * 1000)
if 200 <= code < 400:
status = "SLOW" if elapsed_ms > SLOW_MS else "UP"
else:
status = "DOWN"
except HTTPError as e:
code = e.code
elapsed_ms = int((time.time() - t0) * 1000)
# 401/403 = UP but protected, still alive
status = "UP" if code in (401, 403) else "DOWN"
except (URLError, Exception):
elapsed_ms = int((time.time() - t0) * 1000)
status = "DOWN"
return {"name": name, "url": url, "server": entry.get("server"), "status": status, "code": code, "ms": elapsed_ms}
def main():
data = extract_data()
if not data:
print("NO_DATA extracted from carto, abort")
sys.exit(1)
print(f"Checking {len(data)} screens with {WORKERS} workers, timeout {TIMEOUT}s")
t_start = time.time()
results = []
with ThreadPoolExecutor(max_workers=WORKERS) as ex:
futures = {ex.submit(check_one, e): e for e in data}
for fut in as_completed(futures):
try:
results.append(fut.result())
except Exception as ex:
e = futures[fut]
results.append({"name": e.get("name"), "url": e.get("url"), "server": e.get("server"), "status": "ERROR", "code": 0, "ms": 0})
elapsed = time.time() - t_start
# Count by status
counts = {"UP": 0, "SLOW": 0, "DOWN": 0, "ERROR": 0}
for r in results:
counts[r["status"]] = counts.get(r["status"], 0) + 1
# Index by URL for fast lookup in frontend
out = {
"generated_at": time.strftime("%Y-%m-%dT%H:%M:%S%z"),
"elapsed_sec": round(elapsed, 1),
"total": len(results),
"counts": counts,
"by_url": {r["url"]: {"status": r["status"], "code": r["code"], "ms": r["ms"]} for r in results}
}
# Atomic write (rename)
tmp = tempfile.NamedTemporaryFile(mode="w", delete=False, dir="/var/www/html/api/", suffix=".tmp", encoding="utf-8")
json.dump(out, tmp, separators=(",", ":"))
tmp.close()
os.replace(tmp.name, OUT_FILE)
os.chmod(OUT_FILE, 0o644)
print(f"DONE in {elapsed:.1f}s: UP={counts['UP']} SLOW={counts['SLOW']} DOWN={counts['DOWN']} ERROR={counts['ERROR']}")
print(f"Written: {OUT_FILE}")
if __name__ == "__main__":
main()