snap-sync: cleanup target snapper structure handling

- verify_snapper_structure: better variable parsing
- handle situation of deleted snapper target structure
- lineup variable names
- ensure proper sync after snapper calls
- snapper metadata updates
- support multiple config option parameter
- update dry-run processing
- refuse to backup a snapshot to the target,
  if new source snap-id already exists on target subvolume
- cleanup permission settings for backupdir
This commit is contained in:
2017-11-22 17:52:18 +01:00
parent 50680410bb
commit fea12084f9

View File

@@ -81,12 +81,16 @@ check_snapper_failed_ids () {
# "$progname backup in progress" (snapper description field) # "$progname backup in progress" (snapper description field)
snapper_failed_ids=$(eval snapper -c $selected_config list -t single | awk '/'"$progname"' backup in progress/ {cnt++} END {print cnt}') snapper_failed_ids=$(eval snapper -c $selected_config list -t single | awk '/'"$progname"' backup in progress/ {cnt++} END {print cnt}')
if [ -n "$snapper_failed_ids" ]; then if [ -n "$snapper_failed_ids" ]; then
if [ noconfirm ]; then
snapper -c $selected_config delete $(snapper -c $selected_config list | awk '/'"$progname"' backup in progress/ {print $3}')
else
printf "\nNOTE: Previous failed %s backup snapshots found for '%s'.\n" "$progname" "$selected_config" | tee $PIPE printf "\nNOTE: Previous failed %s backup snapshots found for '%s'.\n" "$progname" "$selected_config" | tee $PIPE
get_answer_yes_no "Delete failed backup snapshots [y/N]? " "no" get_answer_yes_no "Delete failed backup snapshots [y/N]? " "no"
if [ "$answer" = "yes" ]; then if [ "$answer" = "yes" ]; then
snapper -c $selected_config delete $(snapper -c $selected_config list | awk '/'"$progname"' backup in progress/ {print $3}') snapper -c $selected_config delete $(snapper -c $selected_config list | awk '/'"$progname"' backup in progress/ {print $3}')
fi fi
fi fi
fi
} }
die () { die () {
@@ -118,7 +122,7 @@ get_disk_infos () {
fi fi
# we need at least one target disk # we need at least one target disk
if [ ${#disk_targets} -eq 0 ]; then die "no suitable target disk found \n"die if [ ${#disk_targets} -eq 0 ]; then die "no suitable target disk found"
fi fi
# Posix Shells do not support Array. Therefore using ... # Posix Shells do not support Array. Therefore using ...
@@ -229,7 +233,11 @@ parse_params () {
shift 2 shift 2
;; ;;
-c|--config) -c|--config)
if [ ${#selected_config} -gt 0 ]; then
selected_config="${selected_config} ${2}"
else
selected_config="$2" selected_config="$2"
fi
shift 2 shift 2
;; ;;
-d|--description) -d|--description)
@@ -262,7 +270,11 @@ parse_params () {
shift shift
;; ;;
--config=*) --config=*)
selected_config=${1#*=} if [ ${#selected_config} -gt 0 ]; then
selected_config="${selected_config} ${1#*=}"
else
selected_config="${1#*=}"
fi
shift shift
;; ;;
--dry-run) --dry-run)
@@ -292,11 +304,11 @@ parse_params () {
;; ;;
-*) -*)
printf "WARN: Unknown option (ignored): $1" >&2 printf "WARN: Unknown option (ignored): $1" >&2
#shift die "Unknown option"
exit 1
;; ;;
*) *)
die "Unknown option: $key\nRun '$progname -h' for valid options.\n" printf "Unknown option: %s\nRun '%s -h' for valid options.\n" $key $progname
die "Unknown option"
;; ;;
esac esac
done done
@@ -397,6 +409,11 @@ run_config () {
else else
backup_root="$selected_target/$backupdir" backup_root="$selected_target/$backupdir"
fi fi
else
# use sane default
if [ -z "$backup_root" ]; then
backup_root="$selected_target"
fi
fi fi
fi fi
else else
@@ -510,12 +527,7 @@ run_backup () {
snapper_target_config=$(eval echo \$snapper_target_config_$i) snapper_target_config=$(eval echo \$snapper_target_config_$i)
snapper_target_snapshot=$(eval echo \$snapper_target_snapshot_$i) snapper_target_snapshot=$(eval echo \$snapper_target_snapshot_$i)
if [ ! "$dryrun" ]; then verify_snapper_structure "backup_root=$backup_root" "snapper_target_config=$snapper_target_config" "snapper_new_id=$snapper_new_id"
verify_snapper_structure $backup_root $snapper_config $snapper_target_config $snapper_new_id
else
cmd="verify_snapper_structure $backup_root $snapper_config $snapper_target_config $snapper_new_id"
printf "dryrun: %s\n" "$cmd"
fi
if [ -z "$snapper_sync_id" ]; then 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_snapshot"
@@ -526,6 +538,7 @@ run_backup () {
fi fi
if [ ! "$dryrun" ]; then 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_snapshot" &>/dev/null
sync
else 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_snapshot"
printf "dryrun: %s\n" "$cmd" printf "dryrun: %s\n" "$cmd"
@@ -543,6 +556,7 @@ run_backup () {
printf "Deleting sync snapshot for %s ...\n" "$selected_config" | tee $PIPE printf "Deleting sync snapshot for %s ...\n" "$selected_config" | tee $PIPE
fi fi
snapper -c "$selected_config" delete "$snapper_sync_id" snapper -c "$selected_config" delete "$snapper_sync_id"
sync
else else
printf "dryrun: btrfs send %s -c %s %s | %s btrfs receive %s %s\n" \ printf "dryrun: btrfs send %s -c %s %s | %s btrfs receive %s %s\n" \
"$verbose_flag" "$snapper_sync_snapshot" "$snapper_new_snapshot" \ "$verbose_flag" "$snapper_sync_snapshot" "$snapper_new_snapshot" \
@@ -581,8 +595,10 @@ run_backup () {
# Tag new snapshot as the latest # Tag new snapshot as the latest
printf "Tagging snapper metadata for configuration '%s' ...\n" "$selected_config" | tee $PIPE printf "Tagging snapper metadata for configuration '%s' ...\n" "$selected_config" | tee $PIPE
if [ ! "$dryrun" ]; then if [ ! "$dryrun" ]; then
snapper -v -c "$selected_config" modify -d \"$description\" -u \"$userdata\" "$snapper_new_id" eval snapper --verbose --config "$selected_config" modify --description \"$description\" --userdata \"$userdata\" "$snapper_new_id"
$ssh snapper -v -c "$snapper_target_config" modify -d \"$target_description\" -u \"$target_userdata\" "$snapper_new_id" sync
$ssh snapper --verbose --config "$snapper_target_config" modify --description \"$target_description\" --userdata \"$target_userdata\" "$snapper_new_id"
$ssh sync
else else
cmd="snapper -v -c $selected_config modify -d $description -u $userdata $snapper_new_id" cmd="snapper -v -c $selected_config modify -d $description -u $userdata $snapper_new_id"
printf "dryrun: %s\n" "$cmd" printf "dryrun: %s\n" "$cmd"
@@ -737,46 +753,85 @@ EOF
} }
verify_snapper_structure () { verify_snapper_structure () {
local backup_root=$1 local backup_root=${1##backup_root=}
local snapper_config=$2 local snapper_config=${2##snapper_target_config=}
local snapper_subvol=$3 local snapper_id=${3##snapper_new_id=}
local snapper_id=$4
local snapper_snapshots=".snapshots" local snapper_snapshots=".snapshots"
if [ "$verbose" ]; then if [ "$verbose" ]; then
echo "Verify snapper filesystem structure on target ..." printf "Verify snapper filesystem structure on target ...\n"
fi fi
# if not accessible, create backup-path # if not accessible, create backup-path
if $ssh [ ! -d $backup_root ]; then if $ssh [ ! -d $backup_root ]; then
if [ ! "$dryrun" ]; then
if [ "$verbose" ]; then if [ "$verbose" ]; then
echo "Create backup-path $backup_root ..." printf "Create backup-path %s ...\n" "$backup_root"
fi fi
$ssh mkdir --mode=0700 --parents $backup_root $ssh mkdir --mode=0700 --parents $backup_root
else
printf "dryrun: Would create backup-path $backup_root %s ...\n" "$backup_root"
fi fi
if $ssh [ ! -d $backup_root/$snapper_subvol ]; then fi
# verify that we have a snapper compatible structure for selected config on target
if $ssh [ ! -d $backup_root/$snapper_config ]; then
if $ssh [ ! -f $SNAPPER_TEMPLATES/snap-sync ]; 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" printf "A snapper template %s to configure the snapper subvolume %s is missing in %s.\n" "snap-sync" "$snapper_config" "$SNAPPER_TEMPLATES"
printf "Did you miss to install the snap-sync's default snapper template?\n"
die "snapper template %s to configure the snapper subvolume %s is missing in %s.\n" "snap-sync" "$snapper_config" "$SNAPPER_TEMPLATES"
fi fi
if [ ! "$dryrun" ]; then
if [ "$verbose" ]; then if [ "$verbose" ]; then
printf "Create new snapper capable subvolume in '%s' ...\n" "$backup_root/$snapper_subvol" printf "Create new snapper capable subvolume in '%s' ...\n" "$backup_root/$snapper_config"
fi
# TODO: test if there is any old snapper config
create_config="btrfs subvolume create $backup_root/$snapper_config"
$ssh $create_config || die "Snapper structure for config %s to hold target snapshots could not be created in directory on %s.\n" "$snapper_config" "$backup_root"
# snapper-logic will create $backup_root/$snapper_config/.snapshots
$ssh snapper --config $snapper_config create-config --template snap-sync $backup_root/$snapper_config
$ssh chmod 0700 $backup_root/$snapper_config
sync
else
printf "dryrun: Would create new snapper structure in '%s' ...\n" "$backup_root/$snapper_config"
fi fi
create_subvol="btrfs subvolume create $backup_root/$snapper_subvol" 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 $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 snapper --config $snapper_subvol create-config --template snap-sync $backup_root/$snapper_subvol
$ssh chmod --mode=0700 $backup_root/$snapper_config $ssh chmod 0700 $backup_root/$snapper_subvol
else else
if $ssh [ `stat --format=%i $backup_root/$snapper_subvol` -ne 256 ]; then cmd="$ssh stat --format=%i $backup_root/$snapper_config"
die "%s needs to be a BTRFS subvolume. But given %s is just a directory.\n" "$snapper_subvol" "$backup_root/$snapper_subvol" if [ $(eval $cmd) -ne 256 ]; then
die "%s needs to be a BTRFS subvolume. But given %s is just a directory.\n" "$snapper_config" "$backup_root/$snapper_config"
fi
# test if there is any restover/old snapper config
if $ssh [ ! -d $backup_root/$snapper_config/$snapper_snapshots ]; then
cmd="btrfs subvolume create $backup_root/$snapper_config/$snapper_snapshots"
$ssh $cmd || die "Can't create subvolume %s in %s to hold target snapshots.\n" "$snapper_snapshots" "$backup_root/$snapper_config"
fi fi
fi fi
# verify that target snapshot id can take the new snapshot data
if $ssh [ ! -d $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id ]; then if [ ! "$dryrun" ]; then
if $ssh [ ! -d $backup_root/$snapper_config/$snapper_snapshots/$snapper_id ]; then
if [ "$verbose" ]; then if [ "$verbose" ]; then
echo "Create backup-path $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id" printf "Create path %s to store target snapshot.\n" "$backup_root/$snapper_config/$snapper_snapshots/$snapper_id"
fi fi
$ssh mkdir --mode=0700 $backup_root/$snapper_subvol/$snapper_snapshots/$snapper_id $ssh mkdir --mode=0700 $backup_root/$snapper_config/$snapper_snapshots/$snapper_id
else
if [ "$verbose" ]; then
printf "Snapshot %s already in use on target %s.\n" "$snapper_id" "$backup_root/$snapper_config/$snapper_snapshots"
fi
printf "Cancel Snapshot creation: Former snapshot with id '%s' already exist in '%s'\n" "\
$snapper_id" "$backup_root/$snapper_config/$snapper_snapshots"
# cleanup generated snapper entry
check_snapper_failed_ids
die "Can't create new snapshot with given snapshot-id!"
return=1
fi
else
printf "dryrun: Would check/create path %s to store target snapshot ...\n" \
"$backup_root/$snapper_config/$snapper_snapshots/$snapper_id"
fi fi
} }