From b938897e17f714420b8ad8f2259393eae9237a68 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 28 Sep 2026 17:51:53 -0400 Subject: [PATCH] sonde tableaux : la sante de chaque source de donnees de Grafana Elle etait verte pendant que les tableaux etaient vides. Eprouvee avec une source recreant le defaut du jour, puis retiree. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 14 ++++++ roles/serveur_grafana/meta/supervision.yml | 5 +- .../templates/sonde-tableaux.sh.j2 | 50 ++++++++++++++++++- 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a4d108..1275d6f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # CHANGELOG — Set-OPS +## 2026-09-28 (24) — La sonde « tableaux » vérifie aussi les sources de données + +Elle était verte pendant que les tableaux des locataires n'affichaient rien : elle +demandait si Grafana répond et si sa base tient — pas s'il peut montrer quelque chose. +Elle interroge désormais la santé de CHAQUE source de données (`/api/datasources/uid/…/health`) +et passe CRITIQUE en nommant celle qui est en défaut. Le mot de passe d'administration est +lu là où il est, dans le drop-in systemd de Grafana (0600 root), pas recopié ; illisible, la +sonde le dit (avertissement « sources non vérifiées ») au lieu de se taire. + +Éprouvée sur `obs-01` de Technolibre : saine ; puis une source Loki temporaire recréant le +défaut du jour (`http://localhost:3100` face à un Loki en HTTPS) → CRITIQUE, source nommée ; +source retirée → saine. Déployée sur les trois Grafana : `tableaux` au vert, « sources +saines (Loki, Prometheus) ». + ## 2026-09-28 (23) — Grafana des locataires : aucune donnée, parce que l'URL de Loki ne suivait pas son TLS **Signalé par l'exploitant** : une erreur de plugin et aucun panneau rempli sur les Grafana diff --git a/roles/serveur_grafana/meta/supervision.yml b/roles/serveur_grafana/meta/supervision.yml index fd2874f..0eb2bb7 100644 --- a/roles/serveur_grafana/meta/supervision.yml +++ b/roles/serveur_grafana/meta/supervision.yml @@ -3,5 +3,6 @@ sondes: - nom: tableaux ttl: 5400 - raison: 'Grafana repond-il, et sa base tient-elle ? Un Grafana dont la base est tombee sert encore - sa page de connexion : il a l''air en ligne et ne peut plus rien montrer.' + raison: 'Grafana repond-il, sa base tient-elle, et ses SOURCES DE DONNEES aussi ? Un Grafana + dont la base est tombee sert encore sa page de connexion ; un Grafana dont la source Loki + parle en clair a un Loki en HTTPS repond aussi — et n''affiche rien (2026-09-28).' diff --git a/roles/serveur_grafana/templates/sonde-tableaux.sh.j2 b/roles/serveur_grafana/templates/sonde-tableaux.sh.j2 index 3428ed3..c6b2878 100644 --- a/roles/serveur_grafana/templates/sonde-tableaux.sh.j2 +++ b/roles/serveur_grafana/templates/sonde-tableaux.sh.j2 @@ -1,17 +1,63 @@ #!/bin/bash # GENERE par Set-OPS (role serveur_grafana). Ne pas editer a la main. # -# SONDE « tableaux » — Grafana repond et sa base tient +# SONDE « tableaux » — Grafana repond, sa base tient, et ses SOURCES DE DONNEES aussi. # # Contrat : docs/supervision-conception.md (API des greffons Nagios) — une ligne, 0/1/2. # Mise en defaut PAR PARAMETRE : `serveur_grafana_sonde_url` (port ferme). +# +# LES SOURCES, DEPUIS LE 2026-09-28. Cette sonde etait verte pendant que les tableaux des +# locataires n'affichaient RIEN : Grafana repondait, sa base tenait — mais sa source Loki +# parlait en clair a un Loki qui servait en HTTPS. Les tableaux tirent leur liste d'hotes de +# Loki : vide, aucun panneau ne se remplissait. « Grafana repond » n'etait pas la question ; +# « Grafana peut-il montrer quelque chose » l'est. +# +# LE MOT DE PASSE EST LU LA OU IL EST, pas recopie : dans le drop-in systemd de Grafana, +# en 0600 root. La sonde tourne en root ; rien d'autre ne le voit, et il n'est pas affiche. set -uo pipefail URL={{ serveur_grafana_sonde_url }} +DROPIN=/etc/systemd/system/grafana-server.service.d/setops.conf s=$(curl -s --max-time 10 "${URL}/api/health" 2>/dev/null) [[ -n "${s}" ]] || { echo "Grafana ne repond pas sur ${URL}."; exit 2; } base=$(printf '%s' "${s}" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("database","?"))' 2>/dev/null) ver=$(printf '%s' "${s}" | python3 -c 'import json,sys; print(json.load(sys.stdin).get("version","?"))' 2>/dev/null) [[ "${base}" == "ok" ]] || { echo "Grafana repond mais sa base est en defaut (${base:-illisible})."; exit 2; } -echo "Grafana ${ver} : base ok." + +MDP=$(grep -hoP 'GF_SECURITY_ADMIN_PASSWORD=\K\S+' "${DROPIN}" 2>/dev/null | head -1) +if [[ -z "${MDP}" ]]; then + echo "Grafana ${ver} : base ok, mais sources NON verifiees (mot de passe illisible dans ${DROPIN})." + exit 1 +fi + +GRAFANA_MDP="${MDP}" python3 - "${URL}" "${ver}" <<'EOF' +import base64, json, os, sys, urllib.request +url, ver = sys.argv[1], sys.argv[2] +auth = "Basic " + base64.b64encode(f"admin:{os.environ['GRAFANA_MDP']}".encode()).decode() +def lire(chemin): + req = urllib.request.Request(url + chemin, headers={"Authorization": auth}) + try: + with urllib.request.urlopen(req, timeout=8) as r: + return json.loads(r.read()) + except urllib.error.HTTPError as e: + try: + return json.loads(e.read()) + except ValueError: + return {"status": "ERROR", "message": f"HTTP {e.code}"} + except OSError as e: + return {"status": "ERROR", "message": str(e)[:80]} +sources = lire("/api/datasources") +if not isinstance(sources, list): + print(f"Grafana {ver} : base ok, mais la liste des sources est illisible ({str(sources)[:80]}).") + sys.exit(1) +ko, ok = [], [] +for d in sources: + h = lire(f"/api/datasources/uid/{d['uid']}/health") + (ok if h.get("status") == "OK" else ko).append(f"{d['name']} ({str(h.get('message'))[:70]})" if h.get("status") != "OK" else d["name"]) +perf = f"| sources_ok={len(ok)} sources_ko={len(ko)}" +if ko: + print(f"Grafana {ver} repond, mais source(s) EN DEFAUT : {'; '.join(ko)} — les tableaux restent vides. {perf}") + sys.exit(2) +print(f"Grafana {ver} : base ok, sources saines ({', '.join(ok) or 'aucune'}). {perf}") +EOF