# Supervision dérivée des rôles > **Pour qui :** celui qui ajoute un rôle à Set-OPS et se demande comment sa supervision > arrive dans Icinga — et celui qui exploite et veut savoir d'où sortent les services > qu'il voit. > **La règle en une phrase.** Un rôle déclare les sondes de sa propre supervision ; le > moteur en dérive les objets Icinga, la permission d'API et les preuves. Comme > `meta/flux.yml` engendre nftables *et* OPNsense. ## Pourquoi Le 2026-09-09, mesure du dépôt sur lui-même : ``` au 2026-09-09 : 39 rôles déclarent leurs flux -> nftables + OPNsense, dérivés 32 déclarent leur empreinte -> ressources des VM, dérivées 32 déclarent leur authentification -> habilitations, dérivées 19 groupes déclarent `surveillance:` -> RIEN ``` Au 2026-09-09, les dix-neuf lignes `surveillance:` de `docs/dependances-groupes.yml` sont écrites, versionnées, relues — et **aucune n'est exécutée**. Icinga surveillait deux choses : `sauvegarde` et `sante`. C'est la classe d'échec que ce dépôt nomme partout ailleurs, en version documentaire : la carte dit ce qui est surveillé, et personne ne surveille. *Une intention écrite n'est pas une mesure.* ## 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/.sh` (0750, root). | | | |---|---| | **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`… 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. ### Ce que la flotte a vraiment sous la main — et ce qu'elle n'a pas `client_sante` installe **`monitoring-plugins-basic`** : **53** greffons, mesurés le 2026-09-14 sur la flotte. Ce document a d'abord annoncé « 54 » et cité `check_pgsql` en exemple. **`check_pgsql` n'en fait pas partie**, ni `check_dns`, ni `check_ldap` : ils sont dans `monitoring-plugins-standard`. Et ce paquet-là ne s'ajoute pas : apt-get install -s monitoring-plugins-standard → samba, python3-samba, smbclient, snmp, tdb-tools… Une pile Samba et une pile SNMP sur **chaque machine** de la flotte, pour obtenir trois greffons. Le durcissement dit le contraire de ça. **La conséquence pour qui écrit une sonde :** vérifier que le greffon appelé est dans `-basic` avant de s'appuyer dessus. **Un greffon absent sort en 127** — qui n'est pas un code Nagios (0/1/2/3) : la sonde devient illisible au lieu d'échouer proprement. Quand le greffon manque, prendre l'outil natif du service (`dig` sur un résolveur, `psql` sur une base) ; c'est déjà ce que fait `serveur_resolveur/resolution`. É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. ```yaml # roles//meta/supervision.yml sondes: - nom: certificat ttl: 5400 raison: "..." ``` ## Passif, et à durée de vie — jamais actif Un contrôle **actif** ne voit pas la machine **muette** : si elle ne répond plus, la sonde échoue et on met ça sur le compte du réseau. Ici c'est le nœud qui parle, et le `ttl` de son envoi fait la fraîcheur — sans nouvelle, Icinga périme le service tout seul. **Le silence alerte autant que l'échec.** C'est exactement ce qui a manqué à `openipmi.service` : une unité en échec à chaque démarrage pendant une semaine, et `systemctl --failed` à zéro partout parce qu'aucune machine n'avait redémarré. C'est aussi ce qu'impose *la vérification suit la clé* : la sonde tourne là où vit la vérité, pas sur le superviseur. Un dépôt de sauvegarde chiffré côté client ne peut être jugé que par qui détient la clé. ## Ce qui n'a rien à faire ici Une **métrique à seuil** — durée de collecte, volume de journaux, taux d'occupation — appartient à Prometheus et Grafana. Icinga répond à une seule question : *est-ce cassé ?* Mélanger les deux rendrait les deux moins lisibles. ## L'autre moitié : `meta/metriques.yml` Cette phrase appelle son pendant, et il porte le même patron — **le rôle déclare, le moteur dérive**. | fichier | ce qu'il produit | la question | | --- | --- | --- | | `meta/supervision.yml` | une **sonde** → un verdict, avec un TTL | *est-ce cassé ?* | | `meta/metriques.yml` | un **exportateur** → une série ; des **panneaux** → un tableau | *depuis quand, et vers où ?* | **Deux fichiers, deux consommateurs.** `serveur_prometheus` lit l'`exportateur` pour en tirer sa cible de scrutation ; `serveur_grafana` lit les `panneaux` pour en assembler un tableau par rôle. Les deux moitiés sont indépendantes : un rôle peut déclarer un exportateur sans panneau (des séries collectées, regardées ailleurs), et des panneaux sans exportateur (`client_metrique` — le job `node` est universel, écrit une fois, et le dériver le ferait exister deux fois). **Le flux n'est pas dérivé d'ici, et c'est voulu.** Le port de l'exportateur doit s'ouvrir depuis l'observatoire, et c'est `meta/flux.yml` qui le déclare — là où vivent déjà tous les flux du rôle. Deux fichiers pour un même fait finiraient par diverger. ### Le critère d'un panneau *Une série a sa place si elle **précède** un verdict, ou si elle n'en aura **jamais**.* Ce qui bascule d'un coup appartient à Icinga ; ce qui dérive lentement n'a que le graphe pour se faire voir. Une base qui passe de 20 à 60 connexions en trois semaines n'a rien cassé — elle annonce la date où elle cassera. ### La `raison` devient la description du panneau C'est le seul champ qui ne produit aucun pixel de graphe, et c'est le plus important. Grafana l'affiche au survol du titre. Sans elle, celui qui regarde six mois plus tard voit une courbe sans savoir ce qu'elle annonce. ### Agréger, toujours node_exporter publie une série par interface, par point de montage, par cœur : **339 séries** pour le seul trafic réseau du site, mesuré le 2026-09-14. Un panneau qui les montre toutes ne montre rien. Et se méfier de `min()` / `max()` : un seul montage pathologique — `/var/lib/lxcfs`, qui rapporte toujours zéro — suffit à rendre un panneau faux **pour toujours**, avec une courbe plate parfaitement lisible. Filtrer par **liste blanche** : un montage inconnu manque au graphe, ce qui se voit, au lieu de l'écraser, ce qui ne se voit pas. ### Ce que les gardes tiennent **P77** exige de chaque panneau un titre, une expression, une unité et une raison — et que l'unité figure dans la table de traduction de `serveur_grafana`. Une unité inventée ne casse rien : le panneau retombe sur `short`, et un graphe d'octets gradué en unités brutes reste parfaitement lisible et parfaitement faux. Ce que P77 **ne** dit pas : si l'expression répond. Aucune lecture statique ne le dira. Un panneau se prouve comme une sonde — en le regardant rendre des données, et en le notant au CHANGELOG. ## Chaque sonde vient avec son contrôle négatif Une sonde qu'on n'a jamais vue échouer n'est pas une sonde, c'est une habitude. Dix-neuf voyants verts non éprouvés seraient un recul par rapport à deux voyants prouvés : ils rassureraient. Toute sonde ajoutée doit donc être accompagnée de la manière de la faire échouer **pour de vrai**, et cette manière doit être rejouée au moins une fois, sur une machine réelle. Le CHANGELOG en porte la trace. **Et contre un état sain, tout autant.** La première version de la sonde du certificat a rendu **14 machines sur 14 en CRITIQUE** — sur une PKI qui se portait très bien. Elle utilisait `openssl verify -CAfile racine`, alors que nos certificats sont signés par un *intermédiaire* que seul `step certificate verify` sait retrouver. Une alarme toujours allumée ne vaut pas mieux qu'une alarme jamais allumée : elle apprend à ne plus regarder. Une sonde se prouve donc **deux fois** — verte sur le sain, rouge sur le cassé. ## Combien de sondes : une par CAUSE D'ACTION Ni une par rôle, ni une par vérification. **Une par geste que le verdict appelle.** Les quatorze premières sondes agrègent chacune de deux à six chemins d'échec, et ce choix avait été fait **sans être écrit** — d'où cette section, ajoutée le 2026-09-10 après la question : *« est-ce parce qu'elles agrègent plusieurs vérifications ? »* **Ce que coûte une agrégation abusive**, dans le modèle d'Icinga : un service = **un état, un historique, un acquittement**. Fondre deux causes qui appellent des gestes différents, c'est ne plus pouvoir acquitter l'une pendant que l'autre alerte — et lire comme *un service instable* ce qui est en réalité *deux faits distincts qui alternent*. **Ce que coûte l'excès inverse** : un voyant de plus est un voyant de moins regardé. Le dépôt le répète assez — *une supervision creuse est pire qu'aucune*. Le test pratique, à appliquer sans état d'âme : > Les deux échecs appellent-ils **le même geste, de la même personne, dans le même délai** ? > Si oui, une seule sonde, et le message nomme lequel des deux a parlé. > Si non, deux sondes. **Exemple d'agrégation légitime** — `moteur` : `icinga2` mort, `icingadb` mort, état figé en base. Trois causes, un seul geste : *va regarder la supervision*. Le message dit laquelle. **Exemple d'agrégation abusive** — `cache-apt` : « ne répond pas » arrête tout `apt` de l'écosystème, « volume à 90 % » est un billet pour demain. Même personne, gestes et **délais** différents : deux sondes. ## Une sonde doit pouvoir être mise en défaut *par paramètre* Corollaire des deux règles précédentes, et c'est ce qui rend la discipline tenable à l'échelle : une sonde dont on ne peut prouver le rouge qu'en **cassant un service** ne sera prouvée qu'une fois, puis plus jamais. Chaque sonde expose donc sa cible et ses seuils en variables du rôle. On la met en défaut en lui donnant un port fermé, un nom qui n'existe pas, un seuil impossible — sur une machine réelle, sans rien abîmer, et aussi souvent qu'on veut. ## Où vit la sonde : là où vit la vérité, pas là où vit le symptôme *« La vérification suit la clé. »* Un nœud sait qu'il a **lancé** sa sauvegarde ; seul le dépôt sait qu'elle a **abouti**. Un nœud sait qu'il **expose** ses métriques ; seul Prometheus sait qu'il les **collecte**. D'où un choix qui peut surprendre : *« ce nœud est-il bien collecté ? »* est une sonde de **`serveur_prometheus`**, pas de `client_metrique`. Une seule sonde y voit les N nœuds à la fois — et surtout, elle voit le cas **silencieux** : celui qui a cessé d'être collecté et qui, par définition, ne peut pas s'en plaindre lui-même. ## Un contrôle négatif ne doit pas pouvoir abîmer Éprouver la sonde du certificat, le 2026-09-09, a consisté à **remplacer le certificat d'hôte** par un auto-signé. La sonde a bien viré au rouge — et le script de synchronisation a propagé ce certificat vers `node_exporter`, dont la clé était restée l'ancienne : ``` failed to load X509KeyPair: tls: private key does not match public key ``` Un service réel est tombé pour éprouver une sonde. Le contrôle avait un **rayon d'action** que je ne lui avais pas donné volontairement. La règle qui en découle : un contrôle négatif se fait sur une **copie**, ou sur un chemin que la sonde accepte en paramètre — jamais en substituant l'artefact que d'autres consomment. Quand c'est impossible, on le fait sur la machine la moins critique, on l'annonce, et on vérifie l'état des consommateurs **après**, pas seulement celui de la sonde. ## Le matériel : un catalogue, deux lecteurs (2026-09-17) Les hyperviseurs et la frontière n'ont aucun rôle Set-OPS pour déclarer leurs capteurs, et ils ne peuvent rien pousser. Prometheus les **tire** déjà ; on juge donc ce qu'il a récolté. `scripts/materiel.py` est la seule source : familles de températures (processeur, carte mère, NVMe, disques SATA, DDR5, cartes réseau, frontière) avec leurs seuils et leur raison, et contrôles de santé (ventilateur arrêté, SMART en échec, secteurs défaillants ou **en progression**, alerte et usure NVMe, relevés SMART périmés). Deux lecteurs : | lecteur | ce qu'il en fait | |---|---| | Grafana — tableau « Set-OPS — Matériel » | tuiles colorées aux seuils, courbes avec seuils tracés, ventilateurs, puissance, usure et âge des disques | | Icinga — hôte par équipement, service `materiel` | `check_materiel.py` lit Prometheus avec les mêmes seuils ; l'hôte est jugé par `up`, pas par un ping qu'aucun flux ne permet | Règles apprises en le construisant : - **Le matériel ment sur ses limites.** Le Super I/O annonce 125 °C « critique » pour la carte mère : les seuils sont déclarés, avec leur raison. - **Un connecteur vide n'est pas un ventilateur arrêté** : on ne crie que pour un ventilateur qui a tourné dans les 24 h. - **La progression, pas le compte.** Un disque aux secteurs défaillants stables est un avertissement ; c'est un compte qui **monte** qui est critique. Une alarme toujours rouge est une alarme qu'on cesse de lire. - **Le côté droit d'une jointure PromQL est agrégé.** Un réétiquetage laisse cinq minutes deux séries pour une même clé, et la requête est refusée. - **Une requête refusée n'est pas un Prometheus injoignable** : la sonde rapporte l'erreur.