96 lines
3.6 KiB
Python
Executable File
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()
|