Compare commits
4 commits
master
...
zfs-backup
Author | SHA1 | Date | |
---|---|---|---|
![]() |
84e7fc30b6 | ||
df420920fc | |||
7a009ddc7e | |||
4017b57262 |
17 changed files with 354 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
||||||
*.retry
|
*.retry
|
||||||
tmp
|
tmp
|
||||||
ldap-password.txt
|
ldap-password.txt
|
||||||
|
__pycache__
|
||||||
|
|
4
hosts
4
hosts
|
@ -92,7 +92,7 @@ dns-fleming-backup.adm.auro.re
|
||||||
ntp-1.int.infra.auro.re
|
ntp-1.int.infra.auro.re
|
||||||
prometheus-fleming.adm.auro.re
|
prometheus-fleming.adm.auro.re
|
||||||
#prometheus-fleming-fo.adm.auro.re
|
#prometheus-fleming-fo.adm.auro.re
|
||||||
radius-fleming.adm.auro.re
|
#radius-fleming.adm.auro.re
|
||||||
dns-1.int.infra.auro.re
|
dns-1.int.infra.auro.re
|
||||||
isp-1.rtr.infra.auro.re
|
isp-1.rtr.infra.auro.re
|
||||||
isp-2.rtr.infra.auro.re
|
isp-2.rtr.infra.auro.re
|
||||||
|
@ -266,7 +266,7 @@ ps-4-3.borne.auro.re
|
||||||
caradoc.adm.auro.re
|
caradoc.adm.auro.re
|
||||||
|
|
||||||
[edc_pve]
|
[edc_pve]
|
||||||
chapalux.adm.auro.re
|
#chapalux.adm.auro.re
|
||||||
|
|
||||||
[edc_vm]
|
[edc_vm]
|
||||||
routeur-edc.adm.auro.re
|
routeur-edc.adm.auro.re
|
||||||
|
|
5
playbooks/zfsbackup.yml
Executable file
5
playbooks/zfsbackup.yml
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env ansible-playbook
|
||||||
|
---
|
||||||
|
- hosts: caradoc.adm.auro.re
|
||||||
|
roles:
|
||||||
|
- zfs-backup
|
5
playbooks/zfsprune.yml
Executable file
5
playbooks/zfsprune.yml
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env ansible-playbook
|
||||||
|
---
|
||||||
|
- hosts: perceval.adm.auro.re
|
||||||
|
roles:
|
||||||
|
- zfs-prune
|
4
roles/zfs-backup/defaults/main.yml
Normal file
4
roles/zfs-backup/defaults/main.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
zfs_backup:
|
||||||
|
scriptpath: /var/zfs-backup-nas
|
||||||
|
...
|
123
roles/zfs-backup/files/zfs-snapshot-nas
Executable file
123
roles/zfs-backup/files/zfs-snapshot-nas
Executable file
|
@ -0,0 +1,123 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]
|
||||||
|
then
|
||||||
|
echo "USAGE: $0 <tank>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TANK="$1"
|
||||||
|
|
||||||
|
IS_TANK_EXIST=$(zfs list | grep -c "tank/${TANK} ")
|
||||||
|
|
||||||
|
if [ "${IS_TANK_EXIST}" -ne 1 ]
|
||||||
|
then
|
||||||
|
echo "${TANK} n'existe pas. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
TODAY=$(date "+%Y-%m-%d")
|
||||||
|
TODAY_EPOCH=$(date -d "${TODAY}" +%s)
|
||||||
|
|
||||||
|
# # 1. On fait les snapshots sur le ZFS du NAS.
|
||||||
|
/sbin/zfs snapshot "tank/${TANK}@${TODAY}"
|
||||||
|
|
||||||
|
|
||||||
|
# 2. On envoie les snapshots sur le ZFS de backup.
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
|
||||||
|
# Au préalable, on regarde si un envoi a été interrompu.
|
||||||
|
TOKEN=$(ssh perceval.adm.auro.re zfs list -o receive_resume_token "tank/${TANK}_backup" | tail -n 1)
|
||||||
|
|
||||||
|
if [ "${#TOKEN}" -gt 15 ]
|
||||||
|
then
|
||||||
|
echo "Un envoi a été interrompu. Reprise."
|
||||||
|
|
||||||
|
zfs send -t "${TOKEN}" | pv -trb | ssh perceval.adm.auro.re zfs recv -s -u "tank/${TANK}_backup"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
echo "La reprise s'est mal déroulée. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# On récupère les dernières snapshots envoyées sur backup.
|
||||||
|
LAST_SNAPSHOT=$(ssh perceval.adm.auro.re zfs list -t snapshot \
|
||||||
|
| grep "tank/${TANK}_backup" \
|
||||||
|
| cut -d' ' -f1 | cut -d'@' -f2 \
|
||||||
|
| sort | tail -n1)
|
||||||
|
|
||||||
|
LAST_SNAPSHOT_EPOCH=$(date -d "${LAST_SNAPSHOT}" "+%s")
|
||||||
|
|
||||||
|
# Si la dernière backup envoyée est celle d'aujourd'hui: On sort.
|
||||||
|
if [ "${LAST_SNAPSHOT_EPOCH}" -ge "${TODAY_EPOCH}" ]
|
||||||
|
then
|
||||||
|
echo "La backup distance ${TANK} ${LAST_SNAPSHOT} est suffisament récente. Fin."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sinon, on envoie une backup supplémentaire à partir de la dernière snapshot.
|
||||||
|
NEW_SNAPSHOT=$(date -d "${LAST_SNAPSHOT} +1day" "+%Y-%m-%d")
|
||||||
|
|
||||||
|
echo "Envoi de la backup ${TANK} ${NEW_SNAPSHOT}."
|
||||||
|
|
||||||
|
zfs send -i "tank/${TANK}@${LAST_SNAPSHOT}" "tank/${TANK}@${NEW_SNAPSHOT}" \
|
||||||
|
| pv -trb | ssh perceval.adm.auro.re zfs recv -s -u "tank/${TANK}_backup"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
echo "L'envoi s'est mal déroulé. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# 3. On ne garde que les 15 dernières snapshots ZFS
|
||||||
|
LIMIT=$(date -d "${TODAY} -15days" "+%Y-%m-%d")
|
||||||
|
LIMIT_EPOCH=$(date -d "${LIMIT}" "+%s")
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
|
||||||
|
# On vérifie qu'il existe au moins 15 backups
|
||||||
|
COUNT=$(zfs list -t snapshot \
|
||||||
|
| grep -c "tank/${TANK}")
|
||||||
|
|
||||||
|
if [ "${COUNT}" -le 15 ]
|
||||||
|
then
|
||||||
|
echo "Il y a moins de 16 backups. Fin."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# On récupère la plus vieille snapshot
|
||||||
|
OLDEST_SNAPSHOT=$(zfs list -t snapshot \
|
||||||
|
| grep "tank/${TANK}" \
|
||||||
|
| cut -d' ' -f1 | cut -d'@' -f2 \
|
||||||
|
| sort | head -n1)
|
||||||
|
|
||||||
|
OLDEST_SNAPSHOT_EPOCH=$(date -d "${OLDEST_SNAPSHOT}" "+%s")
|
||||||
|
|
||||||
|
# Sanity-check: Si la plus vieille backup est celle d'il y a moins de 15 jours: On sort.
|
||||||
|
if [ "${OLDEST_SNAPSHOT_EPOCH}" -ge "${LIMIT_EPOCH}" ]
|
||||||
|
then
|
||||||
|
echo "La backup locale ${TANK} ${OLDEST_SNAPSHOT} est suffisament récente. Fin."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sinon, on supprime la plus vieille snapshot.
|
||||||
|
echo "Suppression de la backup ${TANK} ${OLDEST_SNAPSHOT}."
|
||||||
|
|
||||||
|
zfs destroy "tank/${TANK}@${OLDEST_SNAPSHOT}"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
echo "La suppression s'est mal déroulée. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
11
roles/zfs-backup/handlers/main.yml
Normal file
11
roles/zfs-backup/handlers/main.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
- name: Run systemd daemon-reload
|
||||||
|
systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Restart and enable ZFS-backup timer
|
||||||
|
systemd:
|
||||||
|
name: zfs-backup.timer
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
|
...
|
23
roles/zfs-backup/tasks/main.yml
Normal file
23
roles/zfs-backup/tasks/main.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
- name: Copy files for zfs-backup
|
||||||
|
template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: /etc/systemd/system/{{ item }}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rw,g=r,o=
|
||||||
|
loop:
|
||||||
|
- zfs-backup.service
|
||||||
|
- zfs-backup.timer
|
||||||
|
notify:
|
||||||
|
- Run systemd daemon-reload
|
||||||
|
- Restart and enable ZFS-backup timer
|
||||||
|
|
||||||
|
- name: Copie du script
|
||||||
|
copy:
|
||||||
|
src: files/zfs-snapshot-nas
|
||||||
|
dest: "{{ zfs_backup.scriptpath }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rx,g=r,o=
|
||||||
|
...
|
22
roles/zfs-backup/templates/zfs-backup.service.j2
Normal file
22
roles/zfs-backup/templates/zfs-backup.service.j2
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Service pour ZFS-backup
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
|
||||||
|
TimeoutStartSec = 7200
|
||||||
|
|
||||||
|
Nice=19
|
||||||
|
CPUSchedulingPolicy=batch
|
||||||
|
|
||||||
|
Restart=no
|
||||||
|
|
||||||
|
LogRateLimitIntervalSec=0
|
||||||
|
|
||||||
|
ExecStartPre=/usr/bin/bash {{ zfs_backup.scriptpath }} nextcloud
|
||||||
|
ExecStart=/usr/bin/bash {{ zfs_backup.scriptpath }} gitea
|
10
roles/zfs-backup/templates/zfs-backup.timer.j2
Normal file
10
roles/zfs-backup/templates/zfs-backup.timer.j2
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Timer for ZFS-backup
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
4
roles/zfs-prune/defaults/main.yml
Normal file
4
roles/zfs-prune/defaults/main.yml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
---
|
||||||
|
zfs_prune:
|
||||||
|
scriptpath: /var/zfs-prune-snapshot
|
||||||
|
...
|
66
roles/zfs-prune/files/zfs-remove-old-snapshot
Executable file
66
roles/zfs-prune/files/zfs-remove-old-snapshot
Executable file
|
@ -0,0 +1,66 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
if [ $# -ne 1 ]
|
||||||
|
then
|
||||||
|
echo "USAGE: $0 <tank>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TANK="$1"
|
||||||
|
|
||||||
|
IS_TANK_EXIST=$(zfs list | grep -c "tank/${TANK} ")
|
||||||
|
|
||||||
|
if [ "${IS_TANK_EXIST}" -ne 1 ]
|
||||||
|
then
|
||||||
|
echo "${TANK} n'existe pas. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
TODAY=$(date "+%Y-%m-%d")
|
||||||
|
|
||||||
|
# On ne garde que les 365 dernières snapshots ZFS
|
||||||
|
LIMIT=$(date -d "${TODAY} -1year" "+%Y-%m-%d")
|
||||||
|
LIMIT_EPOCH=$(date -d "${LIMIT}" "+%s")
|
||||||
|
|
||||||
|
while true
|
||||||
|
do
|
||||||
|
|
||||||
|
# On vérifie qu'il existe au moins 365 backups
|
||||||
|
COUNT=$(zfs list -t snapshot \
|
||||||
|
| grep -c "tank/${TANK}")
|
||||||
|
|
||||||
|
if [ "${COUNT}" -le 365 ]
|
||||||
|
then
|
||||||
|
echo "Il y a moins de 366 backups. Fin."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# On récupère la plus vieille snapshot
|
||||||
|
OLDEST_SNAPSHOT=$(zfs list -t snapshot \
|
||||||
|
| grep "tank/${TANK}" \
|
||||||
|
| cut -d' ' -f1 | cut -d'@' -f2 \
|
||||||
|
| sort | head -n1)
|
||||||
|
|
||||||
|
OLDEST_SNAPSHOT_EPOCH=$(date -d "${OLDEST_SNAPSHOT}" "+%s")
|
||||||
|
|
||||||
|
# Sanity-check: Si la plus vieille backup est celle d'il y a moins d'un an: On sort.
|
||||||
|
if [ "${OLDEST_SNAPSHOT_EPOCH}" -ge "${LIMIT_EPOCH}" ]
|
||||||
|
then
|
||||||
|
echo "La backup locale ${TANK} ${OLDEST_SNAPSHOT} est suffisament récente. Fin."
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Sinon, on supprime la plus vieille snapshot.
|
||||||
|
echo "Suppression de la backup ${TANK} ${OLDEST_SNAPSHOT}."
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
zfs destroy "tank/${TANK}@${OLDEST_SNAPSHOT}"
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
echo "La suppression s'est mal déroulée. Arrêt."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
done
|
11
roles/zfs-prune/handlers/main.yml
Normal file
11
roles/zfs-prune/handlers/main.yml
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
- name: Run systemd daemon-reload
|
||||||
|
systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Restart and enable ZFS-prune timer
|
||||||
|
systemd:
|
||||||
|
name: zfs-prune.timer
|
||||||
|
state: restarted
|
||||||
|
enabled: true
|
||||||
|
...
|
23
roles/zfs-prune/tasks/main.yml
Normal file
23
roles/zfs-prune/tasks/main.yml
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
---
|
||||||
|
- name: Copy files for zfs-prune
|
||||||
|
template:
|
||||||
|
src: "{{ item }}.j2"
|
||||||
|
dest: /etc/systemd/system/{{ item }}
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rw,g=r,o=
|
||||||
|
loop:
|
||||||
|
- zfs-prune.service
|
||||||
|
- zfs-prune.timer
|
||||||
|
notify:
|
||||||
|
- Run systemd daemon-reload
|
||||||
|
- Restart and enable ZFS-prune timer
|
||||||
|
|
||||||
|
- name: Copie du script
|
||||||
|
copy:
|
||||||
|
src: files/zfs-remove-old-snapshot
|
||||||
|
dest: "{{ zfs_prune.scriptpath }}"
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: u=rx,g=r,o=
|
||||||
|
...
|
23
roles/zfs-prune/templates/zfs-prune.service.j2
Normal file
23
roles/zfs-prune/templates/zfs-prune.service.j2
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Service pour ZFS-backup
|
||||||
|
Wants=network-online.target
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
|
||||||
|
TimeoutStartSec = 7200
|
||||||
|
|
||||||
|
Nice=19
|
||||||
|
CPUSchedulingPolicy=batch
|
||||||
|
|
||||||
|
Restart=no
|
||||||
|
|
||||||
|
LogRateLimitIntervalSec=0
|
||||||
|
|
||||||
|
ExecStartPre=/usr/bin/bash {{ zfs_prune.scriptpath }} nextcloud_backup
|
||||||
|
ExecStart=/usr/bin/bash {{ zfs_prune.scriptpath }} gitea_backup
|
||||||
|
|
11
roles/zfs-prune/templates/zfs-prune.timer.j2
Normal file
11
roles/zfs-prune/templates/zfs-prune.timer.j2
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{{ ansible_managed | comment }}
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=Timer for ZFS-prune
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnCalendar=daily
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
|
10
shell.nix
Normal file
10
shell.nix
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = with pkgs.buildPackages; [
|
||||||
|
ansible_2_16
|
||||||
|
python313Packages.jinja2
|
||||||
|
python313Packages.requests
|
||||||
|
python313Packages.pysocks
|
||||||
|
];
|
||||||
|
LANG="C.UTF-8";
|
||||||
|
}
|
Loading…
Reference in a new issue