2026-10-07 11:08:22 +02:00
|
|
|
#!/bin/bash
|
|
|
|
|
# Géré par Ansible (rôle sauvegardes) — ne pas modifier à la main.
|
|
|
|
|
#
|
|
|
|
|
# Archive {{ sauvegardes_repertoires | join(', ') }} en tar.zst, chiffre avec age
|
|
|
|
|
# (clé publique du serveur — la clé privée ne quitte jamais le serveur, voir
|
|
|
|
|
# {{ sauvegardes_age_cle }}), envoie vers Dedibackup en FTP, puis applique la
|
|
|
|
|
# rotation ({{ sauvegardes_retention_quotidiennes }} quotidiennes,
|
|
|
|
|
# {{ sauvegardes_retention_hebdomadaires }} hebdomadaires).
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
# Les identifiants ne sont jamais affichés ni journalisés : on désactive
|
|
|
|
|
# l'option xtrace pour cette ligne précise, par précaution.
|
|
|
|
|
set +x
|
|
|
|
|
source /root/.dedibackup
|
|
|
|
|
|
|
|
|
|
AGE_KEY="{{ sauvegardes_age_cle }}"
|
|
|
|
|
AGE_PUB=$(grep -oP '(?<=public key: )age1\S+' "$AGE_KEY")
|
|
|
|
|
|
|
|
|
|
DATE=$(date +%Y%m%d)
|
|
|
|
|
DOW=$(date +%u)
|
|
|
|
|
SEMAINE=$(date +%G-W%V)
|
|
|
|
|
|
|
|
|
|
WORKDIR=$(mktemp -d)
|
|
|
|
|
trap 'rm -rf "$WORKDIR"' EXIT
|
|
|
|
|
|
|
|
|
|
ARCHIVE_QUOT="quotidienne-${DATE}.tar.zst.age"
|
|
|
|
|
|
2026-10-09 17:28:50 +02:00
|
|
|
# Portail (étape 13) : export cohérent de sa base (--single-transaction, sans
|
|
|
|
|
# bloquer le site) et ses fichiers d'état. Le mot de passe root est lu dans
|
|
|
|
|
# le conteneur : il n'apparaît jamais sur la ligne de commande de l'hôte.
|
|
|
|
|
# Absent sur un serveur neuf (la sauvegarde arrive avant le portail) : ignoré.
|
|
|
|
|
EN_PLUS=()
|
|
|
|
|
if [ "$(docker inspect -f '{% raw %}{{.State.Running}}{% endraw %}' {{ sauvegardes_portail_conteneur }} 2>/dev/null)" = "true" ]; then
|
|
|
|
|
docker exec {{ sauvegardes_portail_conteneur }} sh -c \
|
|
|
|
|
'MYSQL_PWD="$MARIADB_ROOT_PASSWORD" exec mariadb-dump -uroot --single-transaction --routines --triggers --databases {{ sauvegardes_portail_base }}' \
|
|
|
|
|
> "$WORKDIR/portail.sql"
|
|
|
|
|
EN_PLUS+=(-C "$WORKDIR" portail.sql)
|
|
|
|
|
fi
|
2026-10-12 18:01:50 +02:00
|
|
|
|
|
|
|
|
# OpenStack (bloc A, 2026-10-12) : export logique des bases de Kolla (Keystone,
|
|
|
|
|
# Nova, Neutron, Glance, Heat, Placement, Grafana), sans redémarrage ni
|
|
|
|
|
# verrou (--single-transaction). Les comptes MariaDB (base mysql) ne sont pas
|
|
|
|
|
# exportés : Kolla les recrée au déploiement. Le mot de passe est lu dans
|
|
|
|
|
# passwords.yml et passé par l'entrée standard, jamais en argument.
|
|
|
|
|
if [ "$(docker inspect -f '{% raw %}{{.State.Running}}{% endraw %}' {{ sauvegardes_openstack_conteneur }} 2>/dev/null)" = "true" ]; then
|
|
|
|
|
kolla_sql() {
|
|
|
|
|
printf '[client]\nuser=root\npassword="%s"\nhost=%s\n' \
|
|
|
|
|
"$(awk '/^database_password:/{print $2}' {{ sauvegardes_openstack_mots_de_passe }})" "{{ sauvegardes_openstack_hote }}" \
|
|
|
|
|
| docker exec -i {{ sauvegardes_openstack_conteneur }} "$@"
|
|
|
|
|
}
|
|
|
|
|
BASES=$(kolla_sql mariadb --defaults-extra-file=/dev/stdin -N -e \
|
|
|
|
|
"SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('mysql','information_schema','performance_schema','sys')")
|
|
|
|
|
# shellcheck disable=SC2086 # une base par mot
|
|
|
|
|
kolla_sql mariadb-dump --defaults-extra-file=/dev/stdin --single-transaction \
|
|
|
|
|
--routines --triggers --events --databases $BASES > "$WORKDIR/openstack.sql"
|
|
|
|
|
EN_PLUS+=(-C "$WORKDIR" openstack.sql)
|
|
|
|
|
fi
|
2026-10-09 17:28:50 +02:00
|
|
|
for chemin in {{ sauvegardes_portail_fichiers | join(' ') }}; do
|
|
|
|
|
if [ -e "$chemin" ]; then
|
|
|
|
|
EN_PLUS+=("$chemin")
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
2026-10-07 11:08:22 +02:00
|
|
|
tar --exclude="{{ sauvegardes_age_dir }}" --exclude='/root/.dedibackup' \
|
2026-10-09 17:28:50 +02:00
|
|
|
-I zstd -cf - {{ sauvegardes_repertoires | join(' ') }} "${EN_PLUS[@]}" \
|
2026-10-07 11:08:22 +02:00
|
|
|
| age -r "$AGE_PUB" -o "$WORKDIR/$ARCHIVE_QUOT"
|
|
|
|
|
|
|
|
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
|
|
|
set ftp:ssl-allow no
|
|
|
|
|
put $WORKDIR/$ARCHIVE_QUOT
|
|
|
|
|
quit
|
|
|
|
|
"
|
|
|
|
|
logger -t sauvegarde-dedibackup "Quotidienne envoyée : $ARCHIVE_QUOT"
|
|
|
|
|
|
2026-10-07 21:20:01 +02:00
|
|
|
# Métrique pour l'alerte Prometheus "SauvegardeAncienne" (étape 7) : écrite
|
|
|
|
|
# seulement une fois la sauvegarde confirmée envoyée, de façon atomique
|
|
|
|
|
# (fichier temporaire puis renommage) pour que node_exporter ne lise jamais
|
|
|
|
|
# un fichier à moitié écrit.
|
|
|
|
|
DOSSIER_TEXTFILE="{{ sauvegardes_dossier_textfile | default('/var/lib/node_exporter/textfile') }}"
|
|
|
|
|
mkdir -p "$DOSSIER_TEXTFILE"
|
|
|
|
|
cat > "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" << METRIQUE
|
|
|
|
|
# HELP syllabus_derniere_sauvegarde_reussie_timestamp Horodatage Unix de la dernière sauvegarde Dedibackup réussie.
|
|
|
|
|
# TYPE syllabus_derniere_sauvegarde_reussie_timestamp gauge
|
|
|
|
|
syllabus_derniere_sauvegarde_reussie_timestamp $(date +%s)
|
|
|
|
|
METRIQUE
|
|
|
|
|
mv "$DOSSIER_TEXTFILE/sauvegarde.prom.tmp" "$DOSSIER_TEXTFILE/sauvegarde.prom"
|
|
|
|
|
|
2026-10-07 11:08:22 +02:00
|
|
|
if [ "$DOW" = "{{ sauvegardes_jour_hebdomadaire }}" ]; then
|
|
|
|
|
ARCHIVE_HEBDO="hebdomadaire-${SEMAINE}.tar.zst.age"
|
|
|
|
|
cp "$WORKDIR/$ARCHIVE_QUOT" "$WORKDIR/$ARCHIVE_HEBDO"
|
|
|
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
|
|
|
set ftp:ssl-allow no
|
|
|
|
|
put $WORKDIR/$ARCHIVE_HEBDO
|
|
|
|
|
quit
|
|
|
|
|
"
|
|
|
|
|
logger -t sauvegarde-dedibackup "Hebdomadaire envoyée : $ARCHIVE_HEBDO"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# --- Rotation : on ne garde que les N plus récentes de chaque catégorie. ---
|
|
|
|
|
rotation() {
|
|
|
|
|
local motif="$1" garder="$2"
|
|
|
|
|
local liste a_supprimer
|
|
|
|
|
liste=$(lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
|
|
|
set ftp:ssl-allow no
|
|
|
|
|
cls -1 ${motif}
|
|
|
|
|
quit
|
|
|
|
|
" 2>/dev/null | sort)
|
|
|
|
|
a_supprimer=$(echo "$liste" | head -n -"$garder")
|
|
|
|
|
if [ -n "$a_supprimer" ]; then
|
|
|
|
|
local commandes=""
|
|
|
|
|
while IFS= read -r fichier; do
|
|
|
|
|
[ -n "$fichier" ] && commandes+="rm $fichier;"
|
|
|
|
|
done <<< "$a_supprimer"
|
|
|
|
|
lftp -u "$DEDIBACKUP_USER,$DEDIBACKUP_PASS" "$DEDIBACKUP_HOST" -e "
|
|
|
|
|
set ftp:ssl-allow no
|
|
|
|
|
$commandes
|
|
|
|
|
quit
|
|
|
|
|
"
|
|
|
|
|
logger -t sauvegarde-dedibackup "Rotation $motif : supprimé $(echo "$a_supprimer" | wc -l) fichier(s)."
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rotation "quotidienne-*.tar.zst.age" {{ sauvegardes_retention_quotidiennes }}
|
|
|
|
|
rotation "hebdomadaire-*.tar.zst.age" {{ sauvegardes_retention_hebdomadaires }}
|