snap-sync: enable snapper to admin snap-sync backups on target
- create and verify snapper compatible structure on target host - create snapper config (snap-$selected_config) on target host if not already available. use a snap-sync template. - change userdata for snapper listings on target config subvolid, uuid and hostname reflact the values from the source system - introduce a snapper template (/etc/snapper/config-templates/snap-sync) - adapt Makefile to support an initial snap-sync template per default, this templates excludes snap-sync backup configs from timeline and cleanup tasks Signed-off-by: Ralf Zerres <ralf.zerres@networkx.de>
This commit is contained in:
2
Makefile
2
Makefile
@@ -18,6 +18,7 @@
|
||||
PKGNAME = snap-sync
|
||||
PREFIX ?= /usr
|
||||
SNAPPER_CONFIG ?= /etc/sysconfig/snapper
|
||||
SNAPPER_TEMPLATES ?= /etc/snapper/config-templates
|
||||
|
||||
BIN_DIR = $(DESTDIR)$(PREFIX)/bin
|
||||
SYSTEMD_DIR = $(DESTDIR)$(PREFIX)/lib/systemd/system
|
||||
@@ -27,3 +28,4 @@ SYSTEMD_DIR = $(DESTDIR)$(PREFIX)/lib/systemd/system
|
||||
install:
|
||||
@./find_snapper_config || sed -i 's@^SNAPPER_CONFIG.*@SNAPPER_CONFIG='$(SNAPPER_CONFIG)'@g' bin/$(PKGNAME)
|
||||
@install -Dm755 bin/* -t $(BIN_DIR)/
|
||||
@install -Dm644 ./$(SNAPPER_TEMPLATES)/* -t $(SNAPPER_TEMPLATES)/
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
progname="`basename v$0`"
|
||||
version="0.4.4"
|
||||
|
||||
# The following line is modified by the Makefile or
|
||||
# The following lines are modified by the Makefile or
|
||||
# find_snapper_config script
|
||||
SNAPPER_CONFIG=/etc/conf.d/snapper
|
||||
SNAPPER_TEMPLATES=/etc/snapper/config-templates
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
PIPE=$TMPDIR/$progname.out
|
||||
@@ -88,6 +89,7 @@ get_disk_infos () {
|
||||
|
||||
# get mounted BTRFS infos
|
||||
if [ "$(findmnt --noheadings --nofsroot --target / --output FSTYPE)" = "btrfs" ]; then
|
||||
# root filesystem is never seen as valid target location
|
||||
exclude_uuid=$(findmnt --noheadings --nofsroot --types btrfs --target / --output UUID)
|
||||
disk_uuids=$($ssh findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list | grep -v $exclude_uuid | awk '{print $1}')
|
||||
disk_targets=$($ssh findmnt --noheadings --nofsroot --types btrfs --output UUID,TARGET --list | grep -v $exclude_uuid | awk '{print $2}')
|
||||
@@ -394,9 +396,9 @@ run_config () {
|
||||
|
||||
if [ "$verbose" ]; then
|
||||
if [ -n "$ssh" ];then
|
||||
printf "Backup-Path on remote %s: %s/%s\n" "$remote" "$backup_root" "$backupdir"
|
||||
printf "Backup-Path on remote %s: %s\n" "$remote" "$backup_root"
|
||||
else
|
||||
printf "Backup-Path: %s/%s\n" "$backup_root" "$backupdir"
|
||||
printf "Backup-Path: %s\n" "$backup_root"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -409,16 +411,17 @@ run_config () {
|
||||
sync
|
||||
else
|
||||
printf "dryrun: Creating new snapshot with snapper config '%s' ...\n" "$selected_config" | tee $PIPE
|
||||
snapper_new_id="<new_snapper_id>"
|
||||
fi
|
||||
|
||||
# if we want to use snapper on the target to supervise the synced snapshots
|
||||
# the backup_location needs to be in a subvol ".snapshots" inside $selected_config (hardcoded in snapper)
|
||||
snapper_target_subvol=.snapshots
|
||||
snapper_target_snapshot=$backup_root/$selected_config/$snapper_target_subvol/$snapper_new_id
|
||||
snapper_target_subvol="snap-$selected_config"
|
||||
snapper_target_snapshots=$backup_root/$snapper_target_subvol/.snapshots/$snapper_new_id
|
||||
if [ -z "$ssh" ]; then
|
||||
printf "Will backup %s to %s\n" "$snapper_new_snapshot" "$snapper_target_snapshot/snapshot" | tee $PIPE
|
||||
printf "Will backup %s to %s\n" "$snapper_new_snapshot" "$snapper_target_snapshots/snapshot" | tee $PIPE
|
||||
else
|
||||
printf "Will backup %s to %s\n" "$snapper_new_snapshot" "$remote":"$snapper_target_snapshot/snapshot" | tee $PIPE
|
||||
printf "Will backup %s to %s\n" "$snapper_new_snapshot" "$remote":"$snapper_target_snapshots/snapshot" | tee $PIPE
|
||||
fi
|
||||
|
||||
# save in config specific infos in pseudo Arrays
|
||||
@@ -427,7 +430,7 @@ run_config () {
|
||||
eval "snapper_new_info_$i='$snapper_new_info'"
|
||||
eval "snapper_config_$i='$selected_config'"
|
||||
eval "snapper_target_subvol_$i='$snapper_target_subvol'"
|
||||
eval "snapper_target_snapshot_$i='$snapper_target_snapshot'"
|
||||
eval "snapper_target_snapshots_$i='$snapper_target_snapshots'"
|
||||
|
||||
cont_backup="K"
|
||||
eval "snapper_activate_$i=yes"
|
||||
@@ -510,7 +513,7 @@ run_backup () {
|
||||
snapper_new_snapshot=$(eval echo \$snapper_new_snapshot_$i)
|
||||
snapper_new_info=$(eval echo \$snapper_new_info_$i)
|
||||
snapper_target_subvol=$(eval echo \$snapper_target_subvol_$i)
|
||||
snapper_target_snapshot=$(eval echo \$snapper_target_snapshot_$i)
|
||||
snapper_target_snapshots=$(eval echo \$snapper_target_snapshots_$i)
|
||||
|
||||
if [ ! "$dryrun" ]; then
|
||||
verify_snapper_structure $backup_root $snapper_config $snapper_target_subvol $snapper_new_id
|
||||
@@ -520,16 +523,16 @@ run_backup () {
|
||||
fi
|
||||
|
||||
if [ -z "$snapper_sync_id" ]; then
|
||||
cmd="btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshot"
|
||||
cmd="btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshots"
|
||||
printf "Sending first snapshot for snapper config '%s' ...\n" "$selected_config" | tee $PIPE
|
||||
if [ "$verbose" ]; then
|
||||
echo "btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshot"
|
||||
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshot"
|
||||
echo "btrfs send $snapper_new_snapshot | $ssh btrfs receive $snapper_target_snapshots"
|
||||
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshots"
|
||||
fi
|
||||
if [ ! "$dryrun" ]; then
|
||||
btrfs send "$snapper_new_snapshot" | $ssh btrfs receive "$snapper_target_snapshot" &>/dev/null
|
||||
btrfs send "$snapper_new_snapshot" | $ssh btrfs receive "$snapper_target_snapshots" &>/dev/null
|
||||
else
|
||||
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshot"
|
||||
cmd="btrfs send -v $snapper_new_snapshot | $ssh btrfs receive -v $snapper_target_snapshots"
|
||||
printf "dryrun: %s\n" "$cmd"
|
||||
fi
|
||||
else
|
||||
@@ -540,7 +543,7 @@ run_backup () {
|
||||
# location where it can get its data. This helps speed up the transfer.
|
||||
verbose_flag="-v"
|
||||
if [ ! "$dryrun" ]; then
|
||||
btrfs send "$verbose_flag" -c "$snapper_sync_snapshot" "$snapper_new_snapshot" | $ssh btrfs receive "$verbose_flag" "$snapper_target_snapshot"
|
||||
btrfs send "$verbose_flag" -c "$snapper_sync_snapshot" "$snapper_new_snapshot" | $ssh btrfs receive "$verbose_flag" "$snapper_target_snapshots"
|
||||
if [ "$verbose" ]; then
|
||||
printf "Deleting sync snapshot for %s ...\n" "$selected_config" | tee $PIPE
|
||||
fi
|
||||
@@ -548,23 +551,23 @@ run_backup () {
|
||||
else
|
||||
printf "dryrun: btrfs send %s -c %s %s | %s btrfs receive %s %s\n" \
|
||||
"$verbose_flag" "$snapper_sync_snapshot" "$snapper_new_snapshot" \
|
||||
"$ssh" "$verbose_flag" "$snapper_target_snapshot"
|
||||
"$ssh" "$verbose_flag" "$snapper_target_snapshots"
|
||||
printf "dryrun: snapper -c %s delete %a\n" "$selected_config" "$snapper_sync_id"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$ssh" ]; then
|
||||
if [ ! "$dryrun" ]; then
|
||||
cp "$snapper_new_info" "$snapper_target_snapshot"
|
||||
cp "$snapper_new_info" "$snapper_target_snapshots"
|
||||
else
|
||||
cmd="cp $snapper_new_info $snapper_target_snapshot"
|
||||
cmd="cp $snapper_new_info $snapper_target_snapshots"
|
||||
printf "dryrun: %s\n" "$cmd"
|
||||
fi
|
||||
else
|
||||
if [ ! "$dryrun" ]; then
|
||||
rsync -avzq "$snapper_new_info" "$remote":"$snapper_target_snapshot"
|
||||
rsync -avzq "$snapper_new_info" "$remote":"$snapper_target_snapshots"
|
||||
else
|
||||
cmd="rsync -avzq $snapper_new_info $remote:$snapper_target_snapshot"
|
||||
cmd="rsync -avzq $snapper_new_info $remote:$snapper_target_snapshots"
|
||||
printf "dryrun: %s\n" "$cmd"
|
||||
fi
|
||||
fi
|
||||
@@ -574,14 +577,22 @@ run_backup () {
|
||||
# This is how we find the parent.
|
||||
|
||||
userdata="backupdir=$backup_dir, subvolid=$selected_subvol, uuid=$selected_uuid"
|
||||
src_host=$(eval cat /etc/hostname)
|
||||
src_uuid=$(eval findmnt --noheadings --output UUID $SUBVOLUME)
|
||||
src_subvolid=$(eval findmnt --noheadings --output OPTIONS $SUBVOLUME | sed -e 's/.*subvolid=\([0-9]*\).*/\1/')
|
||||
target_description="snap-sync backup"
|
||||
target_userdata="subvolid=$src_subvolid, uuid=$src_uuid, host=$src_host"
|
||||
|
||||
# Tag new snapshot as the latest
|
||||
printf "Tagging new snapshot as latest backup for '%s' ...\n" "$selected_config" | tee $PIPE
|
||||
if [ ! "$dryrun" ]; then
|
||||
snapper -v -c "$selected_config" modify -d "$description" -u "$userdata" "$snapper_new_id"
|
||||
$ssh snapper -v -c "$snapper_target_subvol" modify -d \"$target_description\" -u \"$target_userdata\" "$snapper_new_id"
|
||||
else
|
||||
cmd="snapper -v -c $selected_config modify -d $description -u $userdata $snapper_new_id"
|
||||
printf "dryrun: %s\n" "$cmd"
|
||||
cmd="$ssh snapper -v -c $snapper_target_subvol modify -d $target_description -u $target_userdata $snapper_new_id"
|
||||
printf "dryrun: %s\n" "$cmd"
|
||||
fi
|
||||
printf "Backup complete for snapper configuration '%s'.\n" "$selected_config" > $PIPE
|
||||
done
|
||||
@@ -731,38 +742,41 @@ verify_snapper_structure () {
|
||||
local snapper_subvol=$3
|
||||
local snapper_id=$4
|
||||
|
||||
local snapper_snapshots=".snapshots"
|
||||
|
||||
if [ "$verbose" ]; then
|
||||
echo "Verify snapper filesystem structure on target ..."
|
||||
fi
|
||||
|
||||
# if not accessible, create backup-path
|
||||
if $ssh [ ! -d $backup_root/$snapper_config ]; then
|
||||
if $ssh [ ! -d $backup_root ]; then
|
||||
if [ "$verbose" ]; then
|
||||
echo "Create backup-path $backup_root/$snapper_config"
|
||||
echo "Create backup-path $backup_root ..."
|
||||
fi
|
||||
$ssh mkdir --mode=0700 --parents $backup_root/$snapper_config
|
||||
$ssh mkdir --mode=0700 --parents $backup_root
|
||||
fi
|
||||
|
||||
# if not accessible, create subvolume to hold snappers snapshot structure
|
||||
create_subvol="btrfs subvolume create $backup_root/$snapper_config/$snapper_subvol"
|
||||
|
||||
# check if given snapper_subvol is a subvol
|
||||
if $ssh [ ! -d $backup_root/$snapper_config/$snapper_subvol ]; then
|
||||
if [ "$verbose" ]; then
|
||||
echo "Create new subvolume $backup_root/$snapper_config/$snapper_subvol"
|
||||
if $ssh [ ! -d $backup_root/$snapper_subvol ]; then
|
||||
if $ssh [ ! -f $SNAPPER_TEMPLATES/snap-sync ]; then
|
||||
die "A snapper template %s to configure the snapper subvolume %s is missing in %s. Did you miss to install the package default template?\n" "snap-sync" "$snapper_config" "$SNAPPER_TEMPLATES"
|
||||
fi
|
||||
if [ "$verbose" ]; then
|
||||
printf "Create new snapper capable subvolume in '%s' ...\n" "$backup_root/$snapper_subvol"
|
||||
fi
|
||||
create_subvol="btrfs subvolume create $backup_root/$snapper_subvol"
|
||||
$ssh $create_subvol || die "BTRFS subvolume %s to hold snapshots for config %s could not be created in directory on %s.\n" "$snapper_subvol" "$snapper_config" "$backup_root"
|
||||
$ssh snapper --config $snapper_subvol create-config --template snap-sync $backup_root/$snapper_subvol
|
||||
$ssh chmod --mode=0700 $backup_root/$snapper_config
|
||||
else
|
||||
if $ssh [ `stat --format=%i $backup_root/$snapper_config/$snapper_subvol` -ne 256 ]; then
|
||||
die "%s needs to be a BTRFS subvolume. But given %s is just a directory.\n" "$snapper_subvol" "$backup_root/$snapper_config/$snapper_subvol"
|
||||
if $ssh [ `stat --format=%i $backup_root/$snapper_subvol` -ne 256 ]; then
|
||||
die "%s needs to be a BTRFS subvolume. But given %s is just a directory.\n" "$snapper_subvol" "$backup_root/$snapper_subvol"
|
||||
fi
|
||||
fi
|
||||
|
||||
if $ssh [ ! -d $backup_root/$snapper_config/$snapper_subvol/$snapper_id ]; then
|
||||
if $ssh [ ! -d $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id ]; then
|
||||
if [ "$verbose" ]; then
|
||||
echo "Create backup-path $backup_root/$snapper_config/$snapper_subvol/$snapper_id"
|
||||
echo "Create backup-path $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id"
|
||||
fi
|
||||
$ssh mkdir --mode=0700 $backup_root/$snapper_config/$snapper_subvol/$snapper_id
|
||||
$ssh mkdir --mode=0700 $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
58
etc/snapper/config-templates/snap-sync
Normal file
58
etc/snapper/config-templates/snap-sync
Normal file
@@ -0,0 +1,58 @@
|
||||
###
|
||||
# snapper template for snap-sync handling
|
||||
###
|
||||
|
||||
# subvolume to snapshot
|
||||
SUBVOLUME="/var/lib/snap-sync"
|
||||
|
||||
# filesystem type
|
||||
FSTYPE="btrfs"
|
||||
|
||||
|
||||
# users and groups allowed to work with config
|
||||
ALLOW_USERS=""
|
||||
ALLOW_GROUPS="adm"
|
||||
|
||||
# sync users and groups from ALLOW_USERS and ALLOW_GROUPS to .snapshots
|
||||
# directory
|
||||
SYNC_ACL="yes"
|
||||
|
||||
|
||||
# start comparing pre- and post-snapshot in background after creating
|
||||
# post-snapshot
|
||||
BACKGROUND_COMPARISON="yes"
|
||||
|
||||
|
||||
# run daily number cleanup
|
||||
NUMBER_CLEANUP="no"
|
||||
|
||||
# limit for number cleanup
|
||||
NUMBER_MIN_AGE="1800"
|
||||
NUMBER_LIMIT="50"
|
||||
NUMBER_LIMIT_IMPORTANT="10"
|
||||
|
||||
# "no": we will use systemd.timer
|
||||
TIMELINE_CREATE="no"
|
||||
|
||||
# create cron based cleanup entries
|
||||
# "no": we will use systemd.timer
|
||||
TIMELINE_CLEANUP="no"
|
||||
|
||||
# snap-sync: timeline settings
|
||||
TIMELINE_MIN_AGE="1800"
|
||||
TIMELINE_LIMIT_HOURLY="1"
|
||||
TIMELINE_LIMIT_DAILY="2"
|
||||
TIMELINE_LIMIT_MONTHLY="1"
|
||||
TIMELINE_LIMIT_YEARLY="1"
|
||||
|
||||
|
||||
# cleanup empty pre-post-pairs
|
||||
EMPTY_PRE_POST_CLEANUP="yes"
|
||||
|
||||
# limits for empty pre-post-pair cleanup
|
||||
EMPTY_PRE_POST_MIN_AGE="1800"
|
||||
|
||||
# uncomment to exclude this subvol when calling
|
||||
# snap-sync as timer unit
|
||||
# SNAP_SUNC_EXCLUDE="yes"
|
||||
|
||||
Reference in New Issue
Block a user