sondes : le contrat ETAIT celui de Nagios, sans le savoir
Question posee : « tu connais le paquet monitoring-plugins ? » Oui — et le contrat que docs/supervision-conception.md decrivait quelques heures plus tot EST le sien, mot pour mot. Je l avais reinvente sans le nommer, alors que positionnement.md dit l inverse : adopter aux seuils, ne pas reimplementer. Le document nomme desormais l API des greffons Nagios, ajoute le code 3 (INCONNU) et la partie « | metriques » qui manquaient, et dit la consequence : un greffon standard EST une sonde valide, sans colle. Le paquet en fournit 54. On n ecrit du shell que lorsque la verite a mesurer est propre a Set-OPS. Le porteur separe maintenant texte et performance_data. ESSAYER UN VRAI GREFFON A REVELE DEUX DEFAUTS DU PORTEUR. Un envoi refuse faisait taire TOUTES les sondes suivantes : rapporter sortait en exit 1. Une sonde deposee mais non declaree supprimait le rapport des autres, sante comprise — le tableau ne devenait pas rouge, il devenait vide, et le ttl le perimait des heures plus tard sans dire pourquoi. Aucun delai de garde sur les sondes. check_disk 2.4.0-3+deb13u1 sur mon-01 tourne sans fin (etat R) quels que soient ses arguments : un greffon STANDARD, sur une machine saine, qui boucle. Sans garde il figeait le rapport entier toutes les quinze minutes. Chaque sonde tourne desormais sous timeout 20 ; au-dela on rapporte INCONNU en le disant. La lecon n est pas que les greffons standards sont mauvais : c est qu adopter un standard ne dispense pas de l eprouver, et que le porteur doit survivre a une sonde qui se comporte mal. CONSTATE AU PASSAGE, NON CORRIGE : la configuration d exemple d Icinga crie en permanence sur mon-01 — swap sur une VM sans swap, http sur un port ou rien n ecoute, apt pour un paquet. Trois alarmes qui ne peuvent que rester rouges, dans le seul endroit qui doit rester lisible. Etat : certificat 14/14, sante 14/14 au tenant ; 7/7 au site. make prouver : CONFORME, 64 OK, 0 echec, 0 saute. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
This commit is contained in:
parent
90228cb55e
commit
988d35745e
4 changed files with 119 additions and 11 deletions
53
CHANGELOG.md
53
CHANGELOG.md
|
|
@ -1,5 +1,58 @@
|
|||
# CHANGELOG — Set-OPS
|
||||
|
||||
## 2026-09-09 (7) — Le contrat des sondes ETAIT celui de Nagios, sans le savoir
|
||||
|
||||
Question posee : « tu connais le paquet `monitoring-plugins` ? »
|
||||
|
||||
Oui — et le contrat que `docs/supervision-conception.md` decrivait quelques heures plus
|
||||
tot EST le sien, mot pour mot : une ligne sur stdout, `0/1/2` en code de sortie. Je l'avais
|
||||
donc **reinvente sans le nommer**, alors que `positionnement.md` dit exactement l'inverse :
|
||||
*adopter aux seuils, ne pas reimplementer*.
|
||||
|
||||
Le document nomme desormais l'**API des greffons Nagios**, ajoute `3 = INCONNU` et la
|
||||
partie `| metriques` qui manquaient, et dit la consequence : **un greffon standard EST une
|
||||
sonde valide, sans la moindre colle**. Le paquet en fournit 54 — `check_disk`,
|
||||
`check_load`, `check_procs`, `check_ntp_time`, `check_smtp`, `check_pgsql`... On n'ecrit du
|
||||
shell que lorsque la verite a mesurer est propre a Set-OPS, comme
|
||||
`client_pki/certificat`, qui compare l'empreinte SERVIE a celle du disque : aucun greffon
|
||||
ne sait ca.
|
||||
|
||||
Le porteur separe maintenant le texte des metriques (`performance_data`), parce que c'est
|
||||
ce que le contrat porte et qu'Icinga sait les tracer.
|
||||
|
||||
### Essayer un vrai greffon a revele DEUX defauts du porteur
|
||||
|
||||
**1. Un envoi refuse faisait taire toutes les sondes suivantes.** `rapporter` sortait en
|
||||
`exit 1` : une sonde deposee mais non declaree — donc refusee par le filtre d'API —
|
||||
supprimait le rapport de toutes les autres, `sante` comprise. Le tableau ne devenait pas
|
||||
rouge, il devenait VIDE, et le `ttl` le perimait des heures plus tard sans dire pourquoi.
|
||||
*Un rapporteur qui ne peut pas dire UNE chose doit quand meme dire les autres.*
|
||||
|
||||
**2. Aucun delai de garde sur les sondes.** Et ce n'est pas theorique :
|
||||
|
||||
check_disk 2.4.0-3+deb13u1 sur mon-01 : etat R, ne rend jamais la main
|
||||
quels que soient ses arguments (-p /, sans -p, seuils en % ou en G)
|
||||
|
||||
Un greffon STANDARD, sur une machine saine, qui **boucle**. Sans delai de garde il figeait
|
||||
le rapport entier, toutes les quinze minutes, indefiniment. Chaque sonde tourne desormais
|
||||
sous `timeout 20` ; au-dela, on rapporte INCONNU en le disant.
|
||||
|
||||
*La lecon n'est pas « les greffons standards sont mauvais ».* C'est qu'adopter un standard
|
||||
ne dispense pas de l'eprouver — et que le porteur doit survivre a une sonde qui se comporte
|
||||
mal, standard ou maison.
|
||||
|
||||
### Trois voyants rouges permanents, sur l'hote de supervision
|
||||
|
||||
Constate en cherchant : la configuration d'exemple livree par Icinga surveille `localhost`
|
||||
et crie en permanence sur `mon-01` —
|
||||
|
||||
swap : SWAP CRITICAL - 0% free (une VM sans swap)
|
||||
http : connect to 127.0.0.1:80 (rien n'ecoute la)
|
||||
apt : 1 package upgradable
|
||||
|
||||
Trois alarmes qui ne peuvent que rester rouges, dans le seul endroit qui doit rester
|
||||
lisible. **Non corrige** — c'est une decision d'exploitation, pas une correction de code.
|
||||
|
||||
## 2026-09-09 (6) — La supervision se DECLARE dans le role, comme les flux
|
||||
|
||||
**64 preuves (P01-P64).** Nouveau document : `docs/supervision-conception.md`.
|
||||
|
|
|
|||
|
|
@ -28,18 +28,34 @@ C'est la classe d'échec que ce dépôt nomme partout ailleurs, en version docum
|
|||
carte dit ce qui est surveillé, et personne ne surveille. *Une intention écrite n'est pas
|
||||
une mesure.*
|
||||
|
||||
## Le contrat d'une sonde
|
||||
## Le contrat d'une sonde : c'est celui des greffons Nagios
|
||||
|
||||
Une sonde est un **script local**, déposé par le rôle qui la possède, dans
|
||||
`/usr/local/lib/setops/sondes/<nom>.sh` (0750, root).
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **sortie standard** | une ligne, le message lisible par un humain |
|
||||
| **code de sortie** | `0` OK · `1` AVERTISSEMENT · `2` CRITIQUE |
|
||||
| **sortie standard** | `TEXTE lisible` puis, optionnellement, `\| métriques` |
|
||||
| **code de sortie** | `0` OK · `1` AVERTISSEMENT · `2` CRITIQUE · `3` INCONNU |
|
||||
| **réseau** | aucun besoin : c'est le porteur qui pousse le résultat |
|
||||
| **durée** | courte ; le porteur passe toutes les 15 min |
|
||||
|
||||
> **Ce contrat n'est pas de nous.** C'est l'**API des greffons Nagios**, que
|
||||
> `monitoring-plugins` implémente depuis vingt ans et qu'Icinga parle nativement. La
|
||||
> première rédaction de ce document la décrivait sans la nommer — autrement dit la
|
||||
> réinventait. `positionnement.md` dit l'inverse : *adopter aux seuils, ne pas
|
||||
> réimplémenter*.
|
||||
|
||||
**La conséquence pratique est grande.** Un greffon standard **est** une sonde valide, sans
|
||||
la moindre colle : `check_disk`, `check_load`, `check_procs`, `check_ntp_time`,
|
||||
`check_file_age`, `check_smtp`, `check_pgsql`… Le paquet en fournit **54**. Une sonde ne
|
||||
s'écrit à la main que lorsque la vérité à mesurer est propre à Set-OPS — et c'était le cas
|
||||
pour `client_pki/certificat`, qui compare l'empreinte *servie* à celle du disque : aucun
|
||||
greffon ne sait ça.
|
||||
|
||||
Écrire du shell là où un greffon existe, c'est se donner du code à maintenir *et* se priver
|
||||
de vingt ans de cas limites déjà rencontrés par d'autres.
|
||||
|
||||
Le rôle qui possède la sonde la **déploie lui-même** : il connaît ses chemins, ses
|
||||
secrets, sa vérité de terrain. Il la **déclare** dans `meta/supervision.yml`, et c'est
|
||||
cette déclaration que le moteur lit.
|
||||
|
|
|
|||
|
|
@ -36,3 +36,7 @@ client_sante_unites_tolerees: []
|
|||
# plus lente (une poignee TLS, une requete), et on ne veut pas qu'un hoquet la perime.
|
||||
# Six passages du porteur.
|
||||
client_sante_ttl_sondes: 5400
|
||||
|
||||
# Delai de garde de CHAQUE sonde. Une sonde est cense repondre vite ; celle qui depasse
|
||||
# n'a pas de verdict, et on le dit (INCONNU) plutot que de figer tout le rapport.
|
||||
client_sante_delai_sonde: 20
|
||||
|
|
|
|||
|
|
@ -28,20 +28,41 @@ API="https://{{ client_sante_icinga_hote }}.{{ domaine_interne }}:5665"
|
|||
TTL={{ client_sante_ttl_icinga }}
|
||||
MOI="{{ inventory_hostname }}"
|
||||
MOTDEPASSE="$(cat /etc/setops/icinga-api.pass)"
|
||||
# Compte des envois refuses : l'unite systemd doit finir en echec si le rapporteur n'a
|
||||
# pas pu parler, sinon rien ne distingue « tout va bien » de « personne n'ecoute ».
|
||||
ECHECS_RAPPORT=0
|
||||
|
||||
rapporter() { # $1=service $2=code $3=texte $4=ttl
|
||||
rapporter() { # $1=service $2=code $3=texte[|metriques] $4=ttl
|
||||
local charge reponse
|
||||
charge=$(python3 -c 'import json,sys; print(json.dumps({
|
||||
"type": "Service", "service": sys.argv[1], "exit_status": int(sys.argv[2]),
|
||||
"plugin_output": sys.argv[3], "ttl": int(sys.argv[4])}))' \
|
||||
# LES METRIQUES SONT SEPAREES DU TEXTE, parce que le contrat des greffons Nagios les
|
||||
# separe : `TEXTE | cle=valeur;avert;crit;min;max`. Tout pousser dans `plugin_output`
|
||||
# afficherait une ligne illisible a l'ecran ET priverait Icinga de donnees qu'il sait
|
||||
# tracer. C'est en adoptant le contrat qu'on herite de ce qu'il porte.
|
||||
charge=$(python3 -c 'import json,sys
|
||||
texte, _, perf = sys.argv[3].partition("|")
|
||||
charge = {"type": "Service", "service": sys.argv[1], "exit_status": int(sys.argv[2]),
|
||||
"plugin_output": texte.strip() or "(aucun message)", "ttl": int(sys.argv[4])}
|
||||
if perf.strip():
|
||||
charge["performance_data"] = perf.split()
|
||||
print(json.dumps(charge))' \
|
||||
"${MOI}!$1" "$2" "$3" "$4")
|
||||
reponse=$(curl -sS --cacert "{{ client_sante_ca_verification }}" --max-time 20 \
|
||||
-u "{{ client_sante_icinga_utilisateur }}:${MOTDEPASSE}" \
|
||||
-H 'Accept: application/json' -H 'Content-Type: application/json' \
|
||||
-X POST "${API}/v1/actions/process-check-result" -d "${charge}" 2>&1)
|
||||
if ! printf '%s' "${reponse}" | grep -q '"code": *200'; then
|
||||
echo "ECHEC du rapport Icinga : ${reponse}" >&2
|
||||
exit 1
|
||||
# ON N'INTERROMPT PAS — mesure du 2026-09-09.
|
||||
#
|
||||
# `exit 1` ici faisait taire TOUTES LES SONDES SUIVANTES. Une sonde inconnue d'Icinga
|
||||
# (deposee mais pas declaree, donc refusee par le filtre d'API) suffisait a supprimer
|
||||
# le rapport de toutes les autres — y compris `sante`. Le tableau ne devenait pas
|
||||
# rouge : il devenait VIDE, et le `ttl` le perimait des heures plus tard sans que rien
|
||||
# ne dise pourquoi.
|
||||
#
|
||||
# Un rapporteur qui ne peut pas dire UNE chose doit quand meme dire les autres.
|
||||
echo "ECHEC du rapport Icinga pour « $1 » : ${reponse}" >&2
|
||||
ECHECS_RAPPORT=$((ECHECS_RAPPORT + 1))
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -93,7 +114,20 @@ sondes() {
|
|||
for f in /usr/local/lib/setops/sondes/*.sh; do
|
||||
[[ -x "$f" ]] || continue
|
||||
nom=$(basename "$f" .sh)
|
||||
sortie=$("$f" 2>&1); code=$?
|
||||
# DELAI DE GARDE SUR CHAQUE SONDE — mesure du 2026-09-09.
|
||||
#
|
||||
# Sans lui, une sonde qui BOUCLE fige le rapport entier, toutes les quinze minutes,
|
||||
# indefiniment. Ce n'est pas theorique : `check_disk` 2.4.0-3+deb13u1 tourne sans fin
|
||||
# sur `mon-01`, quels que soient ses arguments (etat `R`, pas un blocage d'E/S).
|
||||
# Un greffon STANDARD, sur une machine saine.
|
||||
#
|
||||
# Une sonde qui n'a pas rendu son verdict en 20 s n'a pas de verdict : on rapporte
|
||||
# INCONNU (3) en le disant, plutot que de laisser le porteur mourir en silence.
|
||||
sortie=$(timeout {{ client_sante_delai_sonde }} "$f" 2>&1); code=$?
|
||||
if (( code == 124 )); then
|
||||
rapporter "$nom" 3 "Sonde ${nom} sans reponse apres {{ client_sante_delai_sonde }} s — aucun verdict." "{{ client_sante_ttl_sondes }}"
|
||||
continue
|
||||
fi
|
||||
if (( code > 3 )); then
|
||||
rapporter "$nom" 2 "Sonde ${nom} en erreur (code ${code}) : ${sortie:0:160}" "{{ client_sante_ttl_sondes }}"
|
||||
else
|
||||
|
|
@ -106,7 +140,7 @@ n=${#echecs[@]}
|
|||
if [[ $n -eq 0 ]]; then
|
||||
rapporter "sante" 0 "Aucune unite systemd en echec." "${TTL}"
|
||||
sondes
|
||||
exit 0
|
||||
exit $(( ECHECS_RAPPORT > 0 ? 1 : 0 ))
|
||||
fi
|
||||
|
||||
# CRITIQUE des la premiere, et non un seuil. Une unite en echec est soit un vrai
|
||||
|
|
@ -114,3 +148,4 @@ fi
|
|||
# ferait vivre le bruit indefiniment, et c'est exactement ce qu'on vient de corriger.
|
||||
rapporter "sante" 2 "$n unite(s) systemd en echec : ${echecs[*]}" "${TTL}"
|
||||
sondes
|
||||
exit $(( ECHECS_RAPPORT > 0 ? 1 : 0 ))
|
||||
|
|
|
|||
Loading…
Reference in a new issue