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 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-10-04 14:06:31 -04:00
parent 6570b757a4
commit 7976df08d5
3 changed files with 78 additions and 5 deletions

View file

@ -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

View file

@ -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

View file

@ -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