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:
113
bin/snap-sync
113
bin/snap-sync
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user