rsync_backup: Initial version of role

This commit is contained in:
Timo Makinen 2024-12-17 22:32:25 +00:00
parent 46c41d2d77
commit 3efe44b50b
3 changed files with 206 additions and 0 deletions

View file

@ -0,0 +1,150 @@
#!/bin/sh
set -eu
umask 077
ROTATED=30
CONFDIR="/etc/rsync-backup"
DESTDIR="/srv/backup"
LOGDIR="/var/log/rsync-backup"
RUNDIR="/var/run/rsync-backup"
find_rotated() {
# sort dailys from oldest to newest, daily.7 daily.6 daily.5 ...
find "$1" -mindepth 1 -maxdepth 1 -type d -name "daily.*" | sort -V -r
}
rotate_dirs() {
for host in "$@"; do
# rotate dailys starting from oldest
if [ ! -d "${DESTDIR}/${host}" ]; then
continue
fi
find_rotated "${DESTDIR}/${host}" | while read -r dir; do
ext="${dir##*.}"
next="${dir%.*}.$((ext+1))"
mv "$dir" "$next"
done
done
# compress logs over 1 day old
find "$LOGDIR" -type f -name '*.log' -mtime +1 -execdir gzip -f {} ';'
}
prune_dirs() {
for host in "$@"; do
# remove oldest dailys
find_rotated "${DESTDIR}/${host}" | while read -r dir ; do
num="$(basename "$dir" | sed -e 's/^daily.//')"
if [ "$num" -gt $ROTATED ]; then
rm -rf "$dir"
fi
done
done
# remove logs over ROTATED*2 days old
find "$LOGDIR" -type f -name '*.log.gz' -mtime +$((ROTATED*2)) -delete
}
rsync_pull() {
dirs=""
opts=""
host="$1"
conf="${CONFDIR}/${host}.conf"
if [ -s "$conf" ] && [ -x "$conf" ]; then
# shellcheck source=/dev/null
. "$conf" || return
else
echo "skipped: ${1}" 1>&2
return
fi
lockdir="${RUNDIR}/${host}.lock"
mkdir -m 0755 "$lockdir" || return
if [ "$host" = "$(hostname)" ]; then
# skip ssh for localhost
set -- $dirs
else
set -- $(for d in $dirs; do echo "${host}:${d}" ; done)
fi
base="${DESTDIR}/${host}"
if [ ! -d "$base" ]; then
mkdir -m 0700 "$base" || return
fi
dest="${base}/daily.0"
last="${base}/daily.1"
if [ ! -d "$dest" ]; then
mkdir -m 0700 "$dest" || return
fi
if [ -d "$last" ]; then
# hardlink unchanged files to previous daily
opts="--ignore-existing --link-dest=${last}"
fi
logfile="${LOGDIR}/${host}.$(date +%Y%m%d-%H%M%S).log"
if ! /usr/local/bin/rsync \
-e "ssh -o BatchMode=yes -i ${CONFDIR}/id_ed25519" \
-Raqxz --no-devices $opts \
--log-file="$logfile" \
"$@" "$dest"
then
echo "rsync log: ${logfile}" 1>&2
fi
rmdir "$lockdir"
}
if [ ! -d "$DESTDIR" ]; then
echo "ERROR: ${DESTDIR} does not exist" 1>&2
exit 1
fi
if [ ! -d "$LOGDIR" ]; then
echo "ERROR: ${LOGDIR} does not exist" 1>&2
exit 1
fi
if [ ! -d "$RUNDIR" ]; then
mkdir -m 0755 "$RUNDIR"
fi
ALL=false
PRUNE=false
ROTATE=false
while getopts "apr" OPT; do
case "$OPT" in
a)
ALL=true
;;
p)
PRUNE=true
;;
r)
ROTATE=true
;;
*)
echo "Usage: $(basename "$0") [-apr] [host ...]" 1>&2
exit 1
;;
esac
done
shift $((OPTIND-1))
mkdir -m 0755 "${RUNDIR}/daily.lock"
trap 'rmdir "${RUNDIR}/daily.lock"' EXIT
if [ $ALL ]; then
for conf in "${CONFDIR}"/*.conf ; do
host="$(basename "$conf" ".conf")"
set -- "$host" "$@"
done
fi
$ROTATE && rotate_dirs "$@"
for host in "$@" ; do
rsync_pull "$host"
done
$PRUNE && prune_dirs "$@"

View file

@ -0,0 +1,4 @@
---
dependencies:
- {role: backup_base}
- {role: ssh_known_hosts}

View file

@ -0,0 +1,52 @@
---
- name: Copy backup script
ansible.builtin.copy:
dest: /usr/local/sbin/backup-daily
src: backup-daily.sh
mode: "0755"
owner: root
group: "{{ ansible_wheel }}"
- name: Create config directory
ansible.builtin.file:
path: /etc/rsync-backup
state: directory
mode: "0755"
owner: root
group: "{{ ansible_wheel }}"
- name: Create logdir
ansible.builtin.file:
path: /var/log/rsync-backup
state: directory
mode: "0700"
owner: root
group: "{{ ansible_wheel }}"
- name: Create ssh keys
ansible.builtin.command:
argv:
- ssh-keygen
- -t
- ed25519
- -C
- "root@{{ inventory_hostname }}"
- -N
- ""
- -f
- /etc/rsync-backup/id_ed25519
creates: /etc/rsync-backup/id_ed25519
- name: Fetch ssh public key
ansible.builtin.fetch:
src: /etc/rsync-backup/id_ed25519.pub
dest: ../files/ssh/rsync-backup.pub
flat: true
- name: Install cron job
ansible.builtin.cron:
name: daily rsync backup
job: /usr/local/sbin/backup-daily -a -p -r
hour: "00"
minute: "30"