Set-OPS-Public/roles/client_backup/tasks/main.yml
Daniel Allaire 11b5bb5733 temoins : l'instantane d'avant rasage est garde, et l'etat remis lui est compare
M4 a reconstruit Technolibre par le site qui la nomme ; les 7 jeux sont revenus
de l'instantane de 11:34, mais le premier depot d'apres reconstruction l'a chasse
par --keep-daily le jour meme : plus rien a quoi comparer.

Le depot d'avant rasage est etiquete avant-raser et garde jusqu'a la
reconstruction suivante. Le bilan dit ce qui a ete restaure et si c'est encore
au depot. setops-restaurer temoins et make temoins-etat comparent l'etat vivant
a cet instantane : une perte ou une identite changee est un ecart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 12:48:00 -04:00

154 lines
6.3 KiB
YAML

---
- name: Exiger les secrets de sauvegarde (Vault)
ansible.builtin.assert:
that:
- client_backup_password | length > 0
- client_backup_ssh_privkey | length > 0
fail_msg: "vault_restic_password et vault_backup_ssh_privkey requis."
# Un noeud peut legitimement ne rien detenir de non regenerable (nginx, PowerDNS, Icinga :
# leur configuration se REDEPLOIE). Le defaut mesure le 2026-08-11 n'etait pas la, il etait
# dans l'unite qui MENT : 11 hotes deployaient un timer qui echouait chaque nuit sur
# « nothing to backup », invisible depuis le 2026-07-03. Une sauvegarde qui ne sauvegarde
# rien est pire que pas de sauvegarde — elle rassure.
#
# On ne refuse donc pas le deploiement : on refuse d'installer une sauvegarde vide, et on
# RETIRE celle qui existerait. Que tout detenteur d'etat porte bien `client_backup` est
# lisible dans le plan, donc prouve statiquement (D-75, P36) — pas ici.
- name: Refuser une porte d'entree du catalogue devenue fausse
ansible.builtin.assert:
that:
- (client_backup_catalogue.keys() | list | sort) == (client_backup_groupes_etat | sort)
fail_msg: >-
`client_backup_groupes_etat` ne decrit plus `client_backup_catalogue` :
catalogue={{ client_backup_catalogue.keys() | list | sort }},
liste={{ client_backup_groupes_etat | sort }}. La supervision et P36 lisent la
liste — la laisser diverger, c'est sauvegarder sans surveiller, ou surveiller ce
qui n'existe pas.
- name: Etat de la sauvegarde pour ce noeud
ansible.builtin.debug:
msg: >-
{{ (client_backup_jobs | length > 0)
| ternary(client_backup_jobs | length | string + ' jeu(x) : '
+ (client_backup_jobs | map(attribute='nom') | join(', ')),
'aucun etat non regenerable — aucune sauvegarde installee') }}
# L'ACCES AU DEPOT vit a part : les roles proprietaires l'incluent aussi, pour RESTAURER
# avant meme que ce role-ci ait tourne (l'AC se restaure dans la couche `pki_racine`).
- name: Accès au dépôt (restic, clé, mot de passe, SSH) et outil de restauration
ansible.builtin.include_tasks: acces.yml
- name: Assurer le répertoire de préparation des dumps
ansible.builtin.file:
path: "{{ client_backup_staging }}"
state: directory
owner: root
group: root
mode: "0700"
- name: Déployer le script de sauvegarde
when: client_backup_jobs | length > 0
ansible.builtin.template:
src: sauvegarder.sh.j2
dest: /usr/local/sbin/setops-sauvegarder.sh
owner: root
group: root
mode: "0700"
- name: Déployer l'unité et le timer systemd
when: client_backup_jobs | length > 0
ansible.builtin.template:
src: "{{ item.s }}"
dest: "/etc/systemd/system/{{ item.d }}"
owner: root
group: root
mode: "0644"
loop:
- { s: setops-sauvegarde.service.j2, d: setops-sauvegarde.service }
- { s: setops-sauvegarde.timer.j2, d: setops-sauvegarde.timer }
- { s: setops-sauvegarde-avant-raser.service.j2, d: setops-sauvegarde-avant-raser.service }
- name: Activer le timer de sauvegarde
when:
- client_backup_jobs | length > 0
- not ansible_check_mode
ansible.builtin.systemd:
name: setops-sauvegarde.timer
enabled: true
state: started
daemon_reload: true
# --- Retrait d'une sauvegarde vide heritee (le noeud ne detient plus rien) ---
- name: Arreter le timer d'une sauvegarde devenue vide
when:
- client_backup_jobs | length == 0
- not ansible_check_mode
ansible.builtin.systemd:
name: setops-sauvegarde.timer
enabled: false
state: stopped
failed_when: false
# LES VERIFICATIONS PARTENT AVEC LA SAUVEGARDE (2026-09-30). Retirees seules, les unites de
# sauvegarde laissaient derriere elles celles qui VERIFIENT le depot et la restauration :
# elles continuaient de rapporter a des services qu'Icinga ne declare plus (le noeud n'est
# plus detenteur d'etat) — un 404 toutes les quatre heures, et une unite rouge. Vu en
# retirant le web frontal du catalogue.
- name: Arreter les verifications d'une sauvegarde devenue vide
when:
- client_backup_jobs | length == 0
- not ansible_check_mode
ansible.builtin.systemd:
name: "{{ item }}"
enabled: false
state: stopped
loop:
- setops-verification-depot.timer
- setops-verification-restauration.timer
failed_when: false
- name: Retirer le script et les unites d'une sauvegarde vide
when: client_backup_jobs | length == 0
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /usr/local/sbin/setops-sauvegarder.sh
- /etc/systemd/system/setops-sauvegarde.service
- /etc/systemd/system/setops-sauvegarde.timer
- /etc/systemd/system/setops-sauvegarde-avant-raser.service
- /usr/local/sbin/setops-verifier-mon-depot.sh
- /usr/local/sbin/setops-verifier-restauration.sh
- /etc/systemd/system/setops-verification-depot.service
- /etc/systemd/system/setops-verification-depot.timer
- /etc/systemd/system/setops-verification-restauration.service
- /etc/systemd/system/setops-verification-restauration.timer
notify: Recharger systemd
# --- LE NOEUD VERIFIE SON PROPRE DEPOT ----------------------------------------
#
# Conditionne a l'existence d'un `serveur_icinga` : sans destinataire, le rapport
# n'irait nulle part, et un timer qui echoue chaque nuit apprend a ignorer le rouge.
#
# Et conditionne a `client_backup_jobs` : un noeud qui n'emporte rien n'a pas de depot
# a verifier — c'est la meme liste qui decide des deux, pour qu'elles ne divergent pas.
- name: Vérifier mon dépôt distant, et le dire à Icinga
ansible.builtin.include_tasks: verifier.yml
when:
- client_backup_jobs | length > 0
- client_backup_icinga_hote | length > 0
# DIRE CE QU'ON NE FAIT PAS. Sans supervision dans l'ecosysteme, la sauvegarde tourne et
# personne ne constate jamais qu'elle a abouti. Ce n'est pas une erreur — c'est une dette
# qui doit se voir au deploiement plutot que le jour de la restauration.
- name: Dire que personne ne constatera cette sauvegarde
ansible.builtin.debug:
msg: >-
Aucun `serveur_icinga` dans cet écosystème : la sauvegarde de {{ inventory_hostname }}
tournera sans que personne ne constate qu'elle aboutit. Une unité verte sur un dépôt
vide est exactement ce qui a menti pendant un mois.
when:
- client_backup_jobs | length > 0
- client_backup_icinga_hote | length == 0