La zone publie l empreinte de ce que le plancher doit porter ; chaque machine la compare a son /etc/hosts. Au site, elle a revele que sauvegarde, pki et dns.genese.internal pointaient vers l edge (regression de mon deploiement du matin) : la derivation ne donne plus a l edge ce qui n a pas de port. Zone du site corrigee, SFTP des locataires verifie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
233 lines
9.2 KiB
YAML
233 lines
9.2 KiB
YAML
---
|
|
# LE NOEUD RAPPORTE SES UNITES EN ECHEC. Voir le gabarit du script pour le pourquoi.
|
|
#
|
|
# CE ROLE N'A PAS A SE DEMANDER S'IL Y A UNE SUPERVISION — IL EN A UNE (2026-09-09).
|
|
#
|
|
# Il portait une branche « aucun serveur_icinga : rien a poser », et un `when` sur tout le
|
|
# bloc. Ni l'une ni l'autre ne pouvait s'executer : `client_sante` est une INTEGRATION
|
|
# UNIVERSELLE, et `instancier` ne pose une integration universelle que si son serveur
|
|
# existe dans l'ecosysteme. Eprouve sur le modele public — sans Icinga au plan, le groupe
|
|
# `client_sante` est ABSENT de l'inventaire genere ; avec, les quatre noeuds y entrent.
|
|
# Le role n'est donc jamais appele sans destinataire.
|
|
#
|
|
# Une garde qui ne peut pas se declencher n'est pas une garde : elle rassure sans rien
|
|
# tenir. Ce qui la remplace se declenche vraiment — si ce role s'execute, un destinataire
|
|
# ET son secret sont exiges, et l'echec dit LEQUEL des deux manque.
|
|
- name: Exiger un destinataire et de quoi lui parler
|
|
ansible.builtin.assert:
|
|
that:
|
|
- client_sante_icinga_hote | length > 0
|
|
- client_sante_icinga_motdepasse | length > 0
|
|
fail_msg: >-
|
|
Rapport de santé impossible :
|
|
{{ "aucun hôte `serveur_icinga` dans l'inventaire"
|
|
if not client_sante_icinga_hote
|
|
else "`vault_icinga_api_depot` absent de la voûte" }}.
|
|
Le nœud verrait ses unités en échec sans pouvoir le dire.
|
|
|
|
- name: Assurer le répertoire des secrets Set-OPS
|
|
ansible.builtin.file:
|
|
path: /etc/setops
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Déposer le mot de passe d'API Icinga
|
|
ansible.builtin.copy:
|
|
content: "{{ client_sante_icinga_motdepasse }}\n"
|
|
dest: /etc/setops/icinga-api.pass
|
|
owner: root
|
|
group: root
|
|
mode: "0600"
|
|
no_log: true
|
|
|
|
# L'AC d'ICINGA, PAS CELLE DE step-ca : Icinga refuse de servir un certificat qu'il
|
|
# n'a pas emis (il renouvelle tout ce qui expire sous 30 jours, nos certificats vivent
|
|
# 24 h). Domaine de confiance ferme, pair authentifie malgre tout.
|
|
- name: L'AC d'Icinga est-elle déjà disponible ?
|
|
ansible.builtin.stat:
|
|
path: "{{ client_sante_icinga_ca_source }}"
|
|
delegate_to: "{{ client_sante_icinga_hote }}"
|
|
register: client_sante_ca_presente
|
|
|
|
- name: Récupérer l'AC d'Icinga depuis l'hôte de supervision
|
|
when: client_sante_ca_presente.stat.exists
|
|
ansible.builtin.slurp:
|
|
src: "{{ client_sante_icinga_ca_source }}"
|
|
delegate_to: "{{ client_sante_icinga_hote }}"
|
|
register: client_sante_ca_icinga
|
|
|
|
- name: Déposer l'AC d'Icinga pour la vérification du pair
|
|
when: client_sante_ca_presente.stat.exists
|
|
ansible.builtin.copy:
|
|
content: "{{ client_sante_ca_icinga.content | b64decode }}"
|
|
dest: "{{ client_sante_ca_verification }}"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
|
|
# LES GREFFONS STANDARD, POSES PAR LE PORTEUR ET PAS PAR CHAQUE ROLE (2026-09-14).
|
|
#
|
|
# `docs/supervision-conception.md` dit qu'un greffon Nagios EST une sonde valide, « sans
|
|
# la moindre colle », et compte sur les 54 que fournit `monitoring-plugins`. Mesure du
|
|
# 2026-09-14 sur la flotte : **aucun n'etait installe**. Le document decrivait une
|
|
# possibilite qui n'existait pas — et chaque role qui avait besoin d'un controle HTTP
|
|
# n'avait d'autre choix que d'ecrire du shell, exactement ce que la doctrine refuse.
|
|
#
|
|
# ICI, ET PAS DANS CHAQUE ROLE. C'est l'affaire du PORTEUR de pouvoir executer des
|
|
# sondes ; les poser role par role en installerait autant de copies de la decision, et
|
|
# laisserait sans greffon les machines dont aucun role n'en reclame — alors qu'elles en
|
|
# ont tout autant besoin le jour ou on leur en ajoute un.
|
|
#
|
|
# 1,2 Mo par machine, pour `check_http`, `check_tcp`, `check_disk`, `check_procs`,
|
|
# `check_ntp_time`, `check_file_age`… et vingt ans de cas limites deja rencontres par
|
|
# d'autres.
|
|
- name: Installer les outils du rapport passif et les greffons standard
|
|
ansible.builtin.apt:
|
|
name: "{{ client_sante_paquets }}"
|
|
state: present
|
|
|
|
# --- UN ROLE QU'ON RETIRE DOIT POUVOIR DEFAIRE CE QU'IL A FAIT (2026-09-10) -----------
|
|
#
|
|
# Chaque role depose SES sondes dans `/usr/local/lib/setops/sondes/`. Quand un ecosysteme
|
|
# cesse de declarer un service — `serveur_artefacts` retire du plan le 2026-09-10, parce
|
|
# que le SITE fournit les paquets — l'hote quitte le groupe, le role ne tourne plus, et
|
|
# PERSONNE ne retire ses sondes. Le porteur continue alors de les executer et de pousser
|
|
# un resultat pour un service qu'Icinga ne definit plus :
|
|
#
|
|
# ECHEC du rapport Icinga pour « cache-apt » : {"error":404,"status":"No objects found."}
|
|
#
|
|
# Ce n'est pas qu'un bruit : une sonde orpheline consomme le delai du porteur a chaque
|
|
# passage, et son echec ressemble a celui d'une vraie sonde en panne.
|
|
#
|
|
# CE QUI EST ATTENDU SE DERIVE, comme chez `serveur_icinga` : les `meta/supervision.yml`
|
|
# des roles, croises avec les GROUPES de cet hote. On ne retire donc jamais la sonde d'un
|
|
# role qui s'applique — seulement celles dont plus aucun groupe ne repond.
|
|
- name: Relever les sondes que les roles declarent
|
|
ansible.builtin.find:
|
|
paths: "{{ role_path }}/.."
|
|
patterns: supervision.yml
|
|
recurse: true
|
|
depth: 3
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
register: client_sante_metas
|
|
|
|
- name: Lire chaque declaration de supervision
|
|
ansible.builtin.slurp:
|
|
src: "{{ item.path }}"
|
|
delegate_to: localhost
|
|
become: false
|
|
run_once: true
|
|
loop: "{{ client_sante_metas.files }}"
|
|
loop_control:
|
|
label: "{{ item.path | dirname | dirname | basename }}"
|
|
register: client_sante_declarations
|
|
|
|
- name: Assembler le registre des sondes (role -> sondes)
|
|
ansible.builtin.set_fact:
|
|
client_sante_registre: >-
|
|
{{ dict(client_sante_declarations.results
|
|
| map(attribute='item.path') | map('dirname') | map('dirname') | map('basename')
|
|
| zip(client_sante_declarations.results
|
|
| map(attribute='content') | map('b64decode') | map('from_yaml')
|
|
| map(attribute='sondes'))) }}
|
|
run_once: true
|
|
|
|
- name: Deriver les sondes attendues sur CET hote
|
|
ansible.builtin.set_fact:
|
|
client_sante_attendues: >-
|
|
{{ group_names | map('extract', client_sante_registre) | select('defined')
|
|
| select('sequence') | flatten | map(attribute='nom') | list }}
|
|
|
|
# LA SONDE « disque » EST POSEE ICI, PAS PAR `common_packages` (2026-09-28). Elle est
|
|
# DECLAREE par ce role (`meta/supervision.yml`) — tout hote qui rapporte la porte. Mais
|
|
# `common_packages` fait un `upgrade: full` a chaque passage : la poser par lui aurait
|
|
# impose une mise a jour de toute la flotte pour deposer un script. Ce role-ci execute
|
|
# deja les sondes, et se redeploie sans toucher au systeme.
|
|
- name: Assurer le repertoire des sondes
|
|
ansible.builtin.file:
|
|
path: /usr/local/lib/setops/sondes
|
|
state: directory
|
|
owner: root
|
|
group: root
|
|
mode: "0755"
|
|
|
|
- name: Deposer la sonde de l'espace disque
|
|
ansible.builtin.template:
|
|
src: sonde-disque.sh.j2
|
|
dest: /usr/local/lib/setops/sondes/disque.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0750"
|
|
|
|
# LA SONDE « plancher » (2026-09-29), sous la condition meme du plancher : sans plancher
|
|
# gere (`hosts_statiques_actif: false`), il n'y a rien a comparer. Posee ici pour la raison
|
|
# de « disque » : ce role tourne sur tout hote qui rapporte, sans toucher au systeme.
|
|
- name: Deposer la sonde du plancher de resolution
|
|
ansible.builtin.template:
|
|
src: sonde-plancher.sh.j2
|
|
dest: /usr/local/lib/setops/sondes/plancher.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0750"
|
|
when: hosts_statiques_actif | default(true) | bool
|
|
|
|
- name: Relever les sondes reellement posees
|
|
ansible.builtin.find:
|
|
paths: "{{ client_sante_repertoire_sondes | default('/usr/local/lib/setops/sondes') }}"
|
|
patterns: "*.sh"
|
|
register: client_sante_posees
|
|
|
|
- name: Retirer les sondes qu'aucun groupe de cet hote ne declare plus
|
|
ansible.builtin.file:
|
|
path: "{{ item.path }}"
|
|
state: absent
|
|
loop: "{{ client_sante_posees.files }}"
|
|
loop_control:
|
|
label: "{{ item.path | basename }}"
|
|
when: (item.path | basename | regex_replace('\.sh$', '')) not in client_sante_attendues
|
|
|
|
- name: Déployer le script de rapport de santé
|
|
ansible.builtin.template:
|
|
src: setops-sante.sh.j2
|
|
dest: /usr/local/sbin/setops-sante.sh
|
|
owner: root
|
|
group: root
|
|
mode: "0750"
|
|
|
|
- name: Déployer l'unité systemd du rapport
|
|
ansible.builtin.template:
|
|
src: setops-sante.service.j2
|
|
dest: /etc/systemd/system/setops-sante.service
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Recharger systemd
|
|
|
|
- name: Déployer le minuteur du rapport
|
|
ansible.builtin.template:
|
|
src: setops-sante.timer.j2
|
|
dest: /etc/systemd/system/setops-sante.timer
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
notify: Recharger systemd
|
|
|
|
- name: Activer le rapport de santé
|
|
ansible.builtin.systemd:
|
|
name: setops-sante.timer
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
# PREMIER RAPPORT IMMEDIAT, et ce n'est pas une commodite : sans lui, le service
|
|
# reste « en attente » jusqu'au premier declenchement, et un deploiement qui vient
|
|
# d'installer la supervision laisserait un tableau vide qu'on prendrait pour un
|
|
# tableau sain.
|
|
- name: Rapporter tout de suite (ne pas laisser un tableau vide passer pour sain)
|
|
ansible.builtin.command: /usr/local/sbin/setops-sante.sh
|
|
changed_when: false
|
|
failed_when: false
|