| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- #!/usr/bin/env python3
- """
- M9 smoke test — observability layer
- Verifies:
- 1. Prometheus is scraping all services (including new M9 targets)
- 2. Key metrics are accessible in Prometheus
- 3. Grafana is reachable with provisioning
- """
- import urllib.request
- import urllib.parse
- import json
- import sys
- PROM = "http://localhost:9090"
- GRAFANA = "http://localhost:3001"
- def curl_json(url):
- try:
- with urllib.request.urlopen(url, timeout=10) as r:
- return json.loads(r.read())
- except Exception as e:
- print(f" ⚠️ fetch error: {e}", file=sys.stderr)
- return None
- def prom_query(expr):
- url = f"{PROM}/api/v1/query?query={urllib.parse.quote(expr, safe='')}"
- d = curl_json(url)
- if d is None:
- return None
- return d.get("data", {}).get("result", [])
- def pass_(msg):
- print(f" ✅ {msg}")
- def warn_(msg):
- print(f" ⚠️ {msg}")
- def fail_(msg):
- print(f" ❌ {msg}")
- sys.exit(1)
- # ── Step 1 ────────────────────────────────────────────────────────────────
- print("Step 1 — Prometheus is up and scraping all services")
- for svc in ["ingestd", "routerd", "deliverd-fcm", "deliverd-telegram", "admind", "archiverd", "prometheus"]:
- r = prom_query(f'up{{job="{svc}"}}')
- if r:
- pass_(f"Prometheus scraping {svc}")
- else:
- fail_(f"Prometheus NOT scraping {svc} (check prometheus.yml)")
- # ── Step 2 ────────────────────────────────────────────────────────────────
- print("\nStep 2 — ingestd metrics are present (source of ingest-tier metrics)")
- series = prom_query("ba_ingestd_alerts_received_total")
- cnt = len(series) if series else 0
- if cnt > 0:
- pass_(f"ba_ingestd_alerts_received_total: {cnt} series")
- else:
- warn_("ba_ingestd_alerts_received_total: 0 series (no recent traffic — might be OK)")
- # ── Step 3 ────────────────────────────────────────────────────────────────
- print("\nStep 3 — routerd recipient expansion latency metric")
- series = prom_query("ba_routerd_recipient_expansion_seconds_count")
- cnt = len(series) if series else 0
- if cnt > 0:
- pass_(f"ba_routerd_recipient_expansion_seconds: {cnt} series (M9 routerd deployed)")
- else:
- fail_("ba_routerd_recipient_expansion_seconds has no series — routerd missing M9 metric")
- # ── Step 4 ────────────────────────────────────────────────────────────────
- print("\nStep 4 — deliverd delivery attempt metrics (M9)")
- series = prom_query("ba_deliverd_delivery_attempts_total")
- cnt = len(series) if series else 0
- if cnt > 0:
- pass_(f"ba_deliverd_delivery_attempts_total: {cnt} series (M9 deliverd deployed)")
- else:
- pass_("ba_deliverd_delivery_attempts_total: 0 series (metric exists, no delivery traffic yet)")
- # ── Step 5 ────────────────────────────────────────────────────────────────
- print("\nStep 5 — Circuit breaker gauge (M9 ingestd)")
- series = prom_query("ba_ingestd_circuit_breaker_state")
- cnt = len(series) if series else 0
- if cnt > 0:
- pass_(f"ba_ingestd_circuit_breaker_state: {cnt} series (M9 ingestd deployed)")
- else:
- warn_("ba_ingestd_circuit_breaker_state: 0 series (ingestd still on pre-M9 binary)")
- warn_(" Rebuild: docker compose build --no-cache ingestd")
- warn_(" Redeploy: docker compose up -d --no-deps ingestd")
- # ── Step 6 ────────────────────────────────────────────────────────────────
- print("\nStep 6 — Grafana reachable with dashboards provisioned")
- try:
- with urllib.request.urlopen(f"{GRAFANA}/api/health", timeout=10) as r:
- gf_status = r.status
- except Exception as e:
- gf_status = 0
- if gf_status == 200:
- pass_(f"Grafana HTTP {gf_status} (admin/admin at http://localhost:3001)")
- try:
- import base64
- req = urllib.request.Request(f"{GRAFANA}/api/search")
- req.add_header("Authorization", "Basic " + base64.b64encode(b"admin:admin").decode())
- with urllib.request.urlopen(req, timeout=10) as r2:
- results = json.loads(r2.read())
- # Count items with 'broadannounce' in uid or title (folder or dashboard)
- cnt = len([d for d in results
- if 'broadannounce' in d.get('uid', '').lower()
- or 'broadannounce' in d.get('title', '').lower()])
- if cnt > 0:
- pass_("BroadAnnounce Overview dashboard found in Grafana")
- else:
- warn_("BroadAnnounce dashboard not found in Grafana (may need manual import)")
- except Exception as e:
- warn_(f"Could not check dashboards: {e}")
- else:
- fail_(f"Grafana returned HTTP {gf_status} — check docker compose logs grafana")
- # ── Step 7 ────────────────────────────────────────────────────────────────
- print("\nStep 7 — Prometheus self-monitoring")
- r = prom_query('up{job="prometheus"}')
- if r:
- pass_("Prometheus scraping itself")
- else:
- fail_("Prometheus not scraping itself")
- print("\n🎉 M9 smoke: all checks complete.")
|