From 9002e502c487b7f95a81c565d9b13eb8d6fa2fbd Mon Sep 17 00:00:00 2001 From: Ralf Zerres Date: Tue, 21 Nov 2017 15:22:36 +0100 Subject: [PATCH] snap-sync: apply snapper description variables - destinguish three states (running, synced, finished) - adapt handling where needed --- bin/snap-sync | 141 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/bin/snap-sync b/bin/snap-sync index 5d187e8..c18dd33 100755 --- a/bin/snap-sync +++ b/bin/snap-sync @@ -86,18 +86,23 @@ check_prerequisites () { } check_snapper_failed_ids () { + local noconfirm=${1:-$false} + # active, non finished snapshot backups are marked with following string # "$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}') - 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}') + snapper_failed_ids=$(eval snapper --config $selected_config list --type single | awk '/'"$snap_description_running"'/ {cnt++} END {print cnt}') + if [ ${#snapper_failed_ids} -gt 0 ]; then + #if [ -n "$snapper_failed_ids" ]; then + if [ "$noconfirm" ]; then + answer="yes" else - printf "\nNOTE: Previous failed %s backup snapshots found for '%s'.\n" "$progname" "$selected_config" | tee $PIPE + printf "\nNOTE: Found %s previous failed sync runs for '%s'.\n" "${snapper_failed_ids}" "$selected_config" | tee $PIPE get_answer_yes_no "Delete failed backup snapshots [y/N]? " "no" - if [ "$answer" = "yes" ]; then - snapper -c $selected_config delete $(snapper -c $selected_config list | awk '/'"$progname"' backup in progress/ {print $3}') - fi + fi + if [ "$answer" = "yes" ]; then + cmd2="snapper --config \"$selected_config\" list | awk '/'\"$snap_description_running\"'/ {print \$3}'" + cmd="snapper --config \"$selected_config\" delete " + $(eval $cmd $(eval $cmd2)) fi fi } @@ -227,9 +232,7 @@ notify_error() { } parse_params () { - ### # Evaluate given call parameters - ### while [ $# -gt 0 ]; do key="$1" case $key in @@ -243,13 +246,21 @@ parse_params () { ;; -c|--config) if [ ${#selected_config} -gt 0 ]; then - selected_config="${selected_config} ${2}" + selected_configs="${selected_configs} ${2}" else - selected_config="$2" + selected_configs="$2" fi shift 2 ;; - -d|--description) + -d|--description|--description-finished) + description="$2" + shift 2 + ;; + --description-running) + description="$2" + shift 2 + ;; + --description-synced) description="$2" shift 2 ;; @@ -329,7 +340,10 @@ parse_params () { . $SNAPPER_CONFIG selected_configs=${selected_configs:-$SNAPPER_CONFIGS} - description=${description:-"latest incremental backup"} + snap_description_synced=${description_lastsync:-"snap-sync last incremental"} + snap_description_finished=${description_finished:-"snap-sync backup"} + snap_description_running=${description_running:-"snap-sync in progress"} + uuid_cmdline=${uuid_cmdline:-"none"} target_cmdline=${target_cmdline:-"none"} subvolid_cmdline=${subvolid_cmdline:-"none"} @@ -350,6 +364,11 @@ parse_params () { printf "Snap Config: '%s'\n" "$selected_config" printf "Snap Remote: '%s'\n" "$ssh" + printf "Snapper Descriptions" + printf " backup finished: '%s'\n" "$snap_description_finished" + printf " backup synced: '%s'\n" "$snap_description_synced" + printf " backup running: '%s'\n" "$snap_description_running" + if [ "$verbose" ]; then snap_sync_options="verbose=true"; fi if [ "$dryrun" ]; then snap_sync_options="${snap_sync_options} dry-run=true"; fi if [ "$noconfirm" ]; then snap_sync_options="${snap_sync_options} noconfirm=true"; fi @@ -359,29 +378,28 @@ parse_params () { run_config () { - printf "\nVerify configuration...\n" | tee $PIPE - - # commandline selection takes precedence - if [ -n "$selected_config" ]; then - selected_configs=$selected_config - fi + printf "\nVerify configuration...\n" + # loop though selected snapper configurations # Pseudo Arrays $i -> store associated elements of selected_config i=0 for selected_config in $selected_configs; do - count=$(eval snapper -c $selected_config list -t single | awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {cnt++} END {print cnt}') - #count=$(eval snapper -c $selected_config list -t single | grep -c -e "subvolid=$selected_subvol" -e 'uuid=$selected_uuid') + count=$(eval snapper --config $selected_config list --type single | \ + awk '/'"$snap_description_synced"'/' | \ + awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {cnt++} END {print cnt}') if [ -n "$count" ] && [ "$count" -gt 1 ]; then - error "More than one snapper entry found with UUID $selected_uuid and SUBVOLID $selected_subvol for configuration '$selected_config'. Skipping configuration '$selected_config'." + printf "%s entries are marked as '%s' for snapper config '%s'\n" \ + "$count" "$snap_description_synced" "$selected_config" | tee PIPE + printf "Pointing to target with UUID '%s' and SUBVOLID '%s'. Skipping configuration '%s'.\n" \ + "$selected_uuid" "$selected_subvol" "$selected_config" + printf "Please cleanup for further processing.\n" | tee PIPE + error "Skipping configuration $selected_config." selected_configs=$(echo $selected_configs | sed -e "s/\($selected_config*\)//") - if [ "$verbose" ]; then - printf "Counter=%s" "$count" - fi continue fi # cleanup failed former runs - check_snapper_failed_ids cleanup + check_snapper_failed_ids $noconfirm SNAP_SYNC_EXCLUDE=no if [ -f "/etc/snapper/configs/$selected_config" ]; then @@ -399,9 +417,11 @@ run_config () { printf "\n" - # processed snapshot backup is marked with userdata key/value pairs - # backupdir, uuid - snapper_sync_id=$(eval snapper --config "$selected_config" list --type single | awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {print $1}') + # get latest successfully finished snapshot + # (tagged with userdata key/value pairs) + snapper_sync_id=$(eval snapper --config "$selected_config" list --type single | \ + awk '/'"$snap_description_synced"'/' | \ + awk '/subvolid='"$selected_subvol"'/, /uuid='"$selected_uuid"'/ {print $1}') if [ ${#snapper_sync_id} -gt 0 ]; then snapper_sync_snapshot=$SUBVOLUME/.snapshots/$snapper_sync_id/snapshot fi @@ -688,29 +708,66 @@ run_finalize () { src_uuid=$(eval findmnt --noheadings --output UUID $SUBVOLUME) src_subvolid=$(eval findmnt --noheadings --output OPTIONS $SUBVOLUME | sed -e 's/.*subvolid=\([0-9]*\).*/\1/') userdata="backupdir=$backup_dir, subvolid=$selected_subvol, uuid=$selected_uuid, host=$remote" - target_description="snap-sync backup" target_userdata="subvolid=$src_subvolid, uuid=$src_uuid, host=$src_host" # Tag new snapshots key/value parameter - printf "Tagging snapper metadata for configuration '%s' ...\n" "$selected_config" | tee $PIPE if [ ! "$dryrun" ]; then - $(eval snapper --verbose --config "$selected_config" modify --description \"$description\" --userdata \"$userdata\" "$snapper_new_id") + # source snapshot + if [ "$verbose" ]; then + printf "Tagging snapper metadata on source for configuration '%s' ...\n" "$selected_config" | tee $PIPE + fi + cmd="snapper --verbose --config \"$selected_config\" modify --description \"$snap_description_synced\" --userdata \"$userdata\" \"$snapper_new_id\"" + ret=$(eval $cmd) + #printf "return: '%s'\n" "$ret" sync - #TODO: wait for btrfs-receive to be completed! - $ssh "snapper --verbose --config $snapper_target_config modify --description \"$target_description\" --userdata \"$target_userdata\" $snapper_new_id" - $ssh sync + + # target snapshot + if [ "$verbose" ]; then + printf "Tagging snapper metadata on target for configuration '%s' ...\n" "$selected_config" | tee $PIPE + fi + i=1 + cmd="snapper --verbose --config \"$snapper_target_config\" list --type single | awk '/'\"$snap_description_running\"'/' | awk -F '|' '\$1 == "$snapper_new_id" {print \$1}'" + # !!! ugly hack !!!: wait for snapper to list target snapshot in database. how to trigger database resync? + # it is not deterministic, when the entry in the listing will show up .... for now, wait max 10 min ... + while [ "$i" -le 20 ]; do + if [ -n "$ssh" ]; then + ret=$($ssh $cmd) + else + ret=$(eval $cmd) + fi + #printf "return: '%s'\n" "$ret" + if [ -n "$ret" ]; then + if [ "$ret" -eq "$snapper_new_id" ]; then + cmd="snapper --verbose --config \"$snapper_target_config\" modify --description \"$snap_description_finished\" --userdata \"$target_userdata\" \"$snapper_new_id\"" + if [ -n "$ssh" ]; then + ret=$($ssh "$cmd") + else + ret=$(eval $cmd) + fi + #printf "return: '%s'\n" "$ret" + break + fi + fi + if [ "$verbose" ]; then + printf "Waiting for snappers database update on target ...\n" + fi + sleep 30 + i=$(($i + 1)) + done else - cmd="snapper --verbose --config $selected_config modify --description $snap_description_lastsync --userdata $userdata $snapper_new_id" + cmd="snapper --verbose --config $selected_config modify --description $snap_description_synced --userdata $userdata $snapper_new_id" printf "dryrun: %s\n" "$cmd" cmd="$ssh snapper --verbose --config $snapper_target_config modify -description $snap_description_finished --userdata $target_userdata $snapper_new_id" printf "dryrun: %s\n" "$cmd" fi - # Cleanup old source snapshots - source_description="snap-sync backup" - $(eval snapper --verbose --config "$selected_config" modify --description \"$source_description\" --cleanup timeline "$snapper_sync_id") - #snapper -c "$selected_config" delete "$snapper_sync_id" - sync + # Cleanup synced source snapshots + if [ -n "$snapper_sync_id" ]; then + cmd="snapper --verbose --config \"$snapper_config\" modify --description \"$snap_description_finished\" --cleanup timeline \"$snapper_sync_id\"" + ret=$(eval $cmd) + #printf "return: '%s'\n" "$ret" + sync + fi printf "Backup complete for snapper configuration '%s'.\n" "$selected_config" > $PIPE