From 7976df08d5a3b17865638596b526acad4d8397af Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Sun, 4 Oct 2026 14:06:31 -0400 Subject: [PATCH] client_backup : un instantane vide dit pourquoi Devant un instantane vide, les sondes sauvegarde et restauration comptent ce que les chemins sauvegardes contiennent sur le noeud : rien (RIEN A SAUVEGARDER, avertissement), arrive apres l instantane (avertissement), ou deja la a sa prise (critique : la sauvegarde manque ses donnees). Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 27 ++++++++++++++ .../templates/verifier-mon-depot.sh.j2 | 35 ++++++++++++++++--- .../templates/verifier-restauration.sh.j2 | 21 ++++++++++- 3 files changed, 78 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e329a80..5daa142 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,32 @@ # CHANGELOG — Set-OPS +## 2026-10-04 (73) — Un instantané vide dit pourquoi + +**Demande de l'exploitant** : l'avertissement convient quand il n'y a rien à sauvegarder ; +seul le message doit dire lequel des cas on voit. La sonde `sauvegarde` de `web-dorsal-01` +écrivait « N'EMPORTE RIEN … Legitime si ce noeud n'a pas encore de donnees — a confirmer » : +la machine ne tranchait pas, et laissait l'humain aller voir. + +**Fait** (`client_backup`) : devant un instantané vide, les deux sondes comptent ce que les +chemins sauvegardés contiennent sur le nœud même (la liste de `setops-sauvegarde`). +- `sauvegarde` : **rien sur le nœud** → avertissement « RIEN A SAUVEGARDER » ; **des + fichiers tous arrivés après l'instantané** → avertissement « DONNEES ARRIVEES DEPUIS » (la + prochaine sauvegarde les emportera) ; **des fichiers déjà là quand l'instantané a été + pris** → **critique** « N'EMPORTE RIEN … La sauvegarde manque ses donnees ». Ce dernier + cas était un avertissement : c'est une vraie panne. +- `restauration` : avertissement dans les deux cas, message « RIEN A RESTAURER » ou renvoi + à la sonde `sauvegarde`. + +La sonde côté dépôt du site (`serveur_backup`) garde son message : le site ne voit pas le +disque du nœud, il ne peut pas trancher. + +**Éprouvé** sur `web-dorsal-01` (Chezlepro), scripts rendus avec le rapport à Icinga +remplacé par un `echo` : `/srv/webapp` vide → « RIEN A SAUVEGARDER » et « RIEN A +RESTAURER » ; un chemin aux fichiers anciens → critique ; un fichier créé après +l'instantané → « DONNEES ARRIVEES DEPUIS ». + +**Validation** : `--syntax-check` de `client_backup.yml`, `ansible-lint` (0 violation). + ## 2026-10-04 (72) — Les deux locataires reconstruits : les correctifs de (62) à (71) tiennent **La question de l'exploitant** : une reconstruction ramène-t-elle les problèmes corrigés ces diff --git a/roles/client_backup/templates/verifier-mon-depot.sh.j2 b/roles/client_backup/templates/verifier-mon-depot.sh.j2 index 380fdfb..2fe8862 100644 --- a/roles/client_backup/templates/verifier-mon-depot.sh.j2 +++ b/roles/client_backup/templates/verifier-mon-depot.sh.j2 @@ -22,6 +22,16 @@ AGE_CRIT={{ client_backup_age_crit_h }} MOI="{{ inventory_hostname }}" MOTDEPASSE="$(cat /etc/setops/icinga-api.pass)" +# Les chemins que `setops-sauvegarde` emporte : de quoi dire, devant un instantane vide, +# s'il n'y avait RIEN a emporter ou si la sauvegarde a manque des donnees bien presentes. +CHEMINS=( +{% for job in client_backup_jobs %} +{% for c in job.chemins %} + "{{ c }}" +{% endfor %} +{% endfor %} +) + rapporter() { # $1=code $2=texte local charge reponse charge=$(python3 -c 'import json,sys; print(json.dumps({ @@ -68,10 +78,27 @@ for l in sys.stdin: print(n)' 2>/dev/null || echo 0) if (( fichiers == 0 )); then - # AVERTISSEMENT et non CRITIQUE : la machine ne peut pas distinguer « les donnees ont - # disparu » de « il n'y en a pas encore » (un /srv/web sans webapp). C'est a un humain - # de trancher — mais il doit le VOIR. - rapporter 1 "N'EMPORTE RIEN : instantane sans aucun fichier (${taille} octets). Legitime si ce noeud n'a pas encore de donnees — a confirmer." + # INSTANTANE VIDE : on regarde ce que les chemins sauvegardes contiennent ICI, sur le + # noeud. Trois cas, trois messages (2026-10-04) : + # - rien sur le noeud : rien a sauvegarder, l'instantane vide est juste. Avertissement + # quand meme : un humain doit savoir que ce noeud n'est protege de rien ; + # - des fichiers posterieurs a l'instantane : ils sont arrives depuis, la prochaine + # sauvegarde les emportera. Avertissement ; + # - des fichiers deja la AU MOMENT de l'instantane : la sauvegarde les a manques. + # CRITIQUE — c'est une vraie panne. + sur_place=0; anciens=0 + for c in "${CHEMINS[@]}"; do + [[ -e "$c" ]] || continue + sur_place=$(( sur_place + $(find "$c" -type f 2>/dev/null | wc -l) )) + anciens=$(( anciens + $(find "$c" -type f ! -newermt "@${secondes}" 2>/dev/null | wc -l) )) + done + if (( sur_place == 0 )); then + rapporter 1 "RIEN A SAUVEGARDER : ${CHEMINS[*]} ne contient aucun fichier sur ce noeud ; instantane vide, comme attendu." + elif (( anciens == 0 )); then + rapporter 1 "DONNEES ARRIVEES DEPUIS : ${sur_place} fichier(s) dans ${CHEMINS[*]}, tous posterieurs au dernier instantane (il y a ${age_h} h, vide). La prochaine sauvegarde les emportera." + else + rapporter 2 "N'EMPORTE RIEN : instantane vide alors que ${CHEMINS[*]} contenait deja ${anciens} fichier(s) a sa prise. La sauvegarde manque ses donnees." + fi elif (( age_h >= AGE_CRIT )); then rapporter 2 "PERIME : dernier instantane il y a ${age_h} h (seuil ${AGE_CRIT} h), ${fichiers} fichier(s)." elif (( age_h >= AGE_WARN )); then diff --git a/roles/client_backup/templates/verifier-restauration.sh.j2 b/roles/client_backup/templates/verifier-restauration.sh.j2 index 04d00f5..f145d2e 100644 --- a/roles/client_backup/templates/verifier-restauration.sh.j2 +++ b/roles/client_backup/templates/verifier-restauration.sh.j2 @@ -26,6 +26,15 @@ PART="{{ client_backup_restauration_part }}" MOI="{{ inventory_hostname }}" MOTDEPASSE="$(cat /etc/setops/icinga-api.pass)" +# Les chemins que `setops-sauvegarde` emporte (voir `setops-verifier-mon-depot.sh`). +CHEMINS=( +{% for job in client_backup_jobs %} +{% for c in job.chemins %} + "{{ c }}" +{% endfor %} +{% endfor %} +) + rapporter() { # $1=code $2=texte local charge reponse charge=$(python3 -c 'import json,sys; print(json.dumps({ @@ -76,7 +85,17 @@ octets=$(du -sb "${CIBLE}" | cut -f1) if (( obtenus != attendus )); then rapporter 2 "RESTAURATION INCOMPLETE : ${obtenus} fichier(s) restaure(s) sur ${attendus} dans l'instantane." elif (( attendus == 0 )); then - rapporter 1 "Instantane restaurable mais VIDE : rien a restaurer. Legitime si ce noeud n'a pas encore de donnees." + # Avertissement dans les deux cas ; le message dit lequel. Le jugement d'une sauvegarde + # qui manque ses donnees revient a la sonde `sauvegarde`, qui sait dater l'instantane. + sur_place=0 + for c in "${CHEMINS[@]}"; do + [[ -e "$c" ]] && sur_place=$(( sur_place + $(find "$c" -type f 2>/dev/null | wc -l) )) + done + if (( sur_place == 0 )); then + rapporter 1 "RIEN A RESTAURER : instantane vide, et ${CHEMINS[*]} ne contient aucun fichier sur ce noeud — comme attendu." + else + rapporter 1 "Instantane restaurable mais VIDE, alors que ${CHEMINS[*]} contient ${sur_place} fichier(s) sur ce noeud : voir la sonde sauvegarde." + fi else rapporter 0 "Restauration verifiee : ${obtenus} fichier(s), ${octets} octets, contenu recompare ; integrite du depot relue sur ${PART}." fi