Funk-lab/ansible/roles/alertmanager_webhook/files/webhook.py
alkatrazz 3a013834aa fix(webhook): réponse HTTP 200 immédiate + ask-agent asynchrone
BrokenPipeError corrigé : send_response(200) envoyé avant subprocess.
ask-agent lancé en threading.Thread daemon — AlertManager n'attend plus.
NO_COLOR=1 + TERM=dumb pour éviter les codes ANSI dans les logs.

docs: alertmanager-webhook.md — architecture, flux, test, dépannage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 21:58:39 +02:00

95 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""
AlertManager webhook → ask-agent monitor
Reçoit les alertes Prometheus et les délègue au profil monitor de Hermes.
"""
import json
import os
import subprocess
import logging
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
log = logging.getLogger(__name__)
PORT = int(os.environ.get("WEBHOOK_PORT", 9093))
def format_alert(alert: dict) -> str:
name = alert.get("labels", {}).get("alertname", "unknown")
severity = alert.get("labels", {}).get("severity", "unknown")
instance = alert.get("labels", {}).get("instance", "unknown")
summary = alert.get("annotations", {}).get("summary", "")
description = alert.get("annotations", {}).get("description", "")
status = alert.get("status", "firing")
return f"[{status.upper()}] {name} — severity={severity} instance={instance}\n{summary}\n{description}".strip()
class WebhookHandler(BaseHTTPRequestHandler):
def do_POST(self):
if self.path != "/webhook":
self.send_response(404)
self.end_headers()
return
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
try:
payload = json.loads(body)
except json.JSONDecodeError:
log.error("Invalid JSON payload")
self.send_response(400)
self.end_headers()
return
alerts = payload.get("alerts", [])
firing = [a for a in alerts if a.get("status") == "firing"]
resolved = [a for a in alerts if a.get("status") == "resolved"]
if not alerts:
self.send_response(200)
self.end_headers()
return
lines = [f"Alertes Prometheus reçues à {datetime.now().strftime('%H:%M:%S')} :"]
for a in firing:
lines.append(format_alert(a))
for a in resolved:
lines.append(format_alert(a))
message = "\n".join(lines) + "\n\nAnalyse ces alertes : sont-elles critiques ? Quelles actions recommandes-tu ?"
log.info(f"Forwarding {len(firing)} firing + {len(resolved)} resolved alerts to ask-agent monitor")
self.send_response(200)
self.end_headers()
self.wfile.write(b"ok")
def run_agent():
try:
env = {**os.environ, "NO_COLOR": "1", "TERM": "dumb"}
result = subprocess.run(
["/usr/local/bin/ask-agent", "monitor", message],
capture_output=True, text=True, timeout=120, env=env
)
log.info(f"ask-agent response: {result.stdout[:200]}")
if result.returncode != 0:
log.error(f"ask-agent stderr: {result.stderr[:200]}")
except subprocess.TimeoutExpired:
log.error("ask-agent timed out")
except Exception as e:
log.error(f"ask-agent error: {e}")
threading.Thread(target=run_agent, daemon=True).start()
def log_message(self, format, *args):
pass # silence HTTP access logs
if __name__ == "__main__":
log.info(f"AlertManager webhook listening on :{PORT}")
server = HTTPServer(("0.0.0.0", PORT), WebhookHandler)
server.serve_forever()