From: Alan J. Pippin Date: Tue, 13 Jan 2009 07:56:18 +0000 (-0700) Subject: Initial working draft of the new replicate algorithm using X-Git-Url: http://git.pippins.net/embedvideo/.git/static/images/%7Bupdate_month%7D?a=commitdiff_plain;h=044c7d0a0fb4de5c6ae6828e3e31d92a13c851db;p=zfs-ubuntu%2F.git Initial working draft of the new replicate algorithm using backup marker snapshots instead of pure snapshot diffs. --- diff --git a/zfs-replicate b/zfs-replicate index 7c6e650..128d051 100755 --- a/zfs-replicate +++ b/zfs-replicate @@ -1,14 +1,48 @@ #/bin/bash -set -x - # Usage: replicate remote=$1 remote_fs=$2 +remote_pool=${2%%/*} -# change to match the name of the local backup pool +# Set this variable to '1' to use the legacy, non-marker, snapshot diff, replicate script logic +use_legacy_replicate=0 + +# Set the name of the local pool used to store the backup of the remote local_pool=backups +# Make sure we have valid arguments +if [[ -z "$remote" ]] || [[ -z "$remote_fs" ]]; then + echo "Usage: $0 " + exit 1 +fi + +# Make sure the local pool and local receiving filesystem exist, or print some errors +if ! zpool list -H "$local_pool" >/dev/null 2>&1; then + echo >&2 "-E- The local pool, '$local_pool' doesn't seem to exist." + exit 1 +fi +if ! zfs list "$local_pool/$remote_pool" >/dev/null 2>&1; then + echo >&2 "-E- The local filesystem for the remote pool, '$local_pool/$remote_pool' doesn't seem to exist." + echo >&2 " You will need to create this filesystem before this script can replicate your data." + echo >&2 " You can create this filsystem by executing this command: 'zfs create $local_pool/$remote_pool'" + exit 1 +fi + +# Obtain the zpool guid for the local pool +local_pool_guid=`zpool get guid $local_pool 2>&1 | grep $local_pool | awk '{ print $3 }'` +if ! zpool get guid $local_pool > /dev/null 2>&1; then + echo >&2 "-E- Unable to extract the guid for the local pool: $local_pool" + exit 1 +fi + +# Turn on shell verbosity +set -x + +# Setup our backup marker names +current_backup_marker=${remote_fs}@current-backup-${local_pool_guid} +previous_backup_marker=${remote_fs}@previous-backup-${local_pool_guid} + # The ssh connection doesn't find zfs without this. zfs=/usr/sbin/zfs @@ -18,29 +52,101 @@ ssh $remote \ $zfs list -H -t snapshot | grep ^${remote_fs}@ | awk '{print$1}' > $remote_list +if [[ $? != 0 ]]; then + echo "-E- remote $zfs list command failed" + exit 1 +fi # List the snapshots on the local machine. local_list=$(mktemp /tmp/replicate.XXXXXX) $zfs list -H -t snapshot | grep ^${local_pool}/${remote_fs}@ | awk '{gsub(/^${local_pool}./,"",$1); print$1}' > $local_list +if [[ $? != 0 ]]; then + echo "-E- local $zfs list command failed" + exit 1 +fi + +if [ $use_legacy_replicate == 0 ]; then + # Destroy the current backup marker snapshot on the remote system if it exists + grep -q ${current_backup_marker} $remote_list + if [ $? == 0 ]; then + ssh $remote $zfs destroy ${current_backup_marker} + if [[ $? != 0 ]]; then + echo "-E- remote $zfs destroy command failed" + exit 1 + fi + fi + # Create the current backup marker snapshot on the remote system + ssh $remote $zfs snapshot ${current_backup_marker} + if [[ $? != 0 ]]; then + echo "-E- remote $zfs snapshot command failed" + exit 1 + fi -# See what the most recent snapshot on the remote end is. -latest=$(tail -n 1 $remote_list) + # Check to see if the previous backup marker exists in the remote snapshot list. + # Check to see if the previous backup marker exists in the local snapshot list. + # If the previous backup markers exists, perform an incremental replicate. + # Otherwise, perform a full replicate. + grep -q ${previous_backup_marker} $remote_list + full=$? + grep -q ${previous_backup_marker} $local_list + full=$(($full || $?)) -# I did this to make sure that diff would always display the most recent common -echo bogus.remote >> $remote_list -echo bogus.local >> $local_list -common=$(diff -u $remote_list $local_list | grep '^ ' | tail -n 1) + if [[ $full == 0 ]]; then + ssh $remote $zfs send -R -I${previous_backup_marker} ${current_backup_marker} | + $zfs receive -vF -d ${local_pool}/${remote_fs%/*} + if [[ $? != 0 ]]; then + echo "-E- remote incremental $zfs send command failed" + exit 1 + fi + else + ssh $remote $zfs send -R ${current_backup_marker} | + $zfs receive -vF -d ${local_pool}/${remote_fs%/*} + if [[ $? != 0 ]]; then + echo "-E- remote full $zfs send command failed" + exit 1 + fi + fi + + # destroy the previous backup markers now that we've replicated past them + $zfs destroy ${local_pool}/${previous_backup_marker} > /dev/null 2>&1 + ssh $remote $zfs destroy ${previous_backup_marker} > /dev/null 2>&1 -if [ -n "$common" ]; then - # We found a common snapshot - ssh $remote $zfs send -R -I${common/*@/@} $latest | - $zfs receive -vF -d ${local_pool}/${remote_fs%/*} + # Rename the current backup marker to be the previous backup marker + $zfs rename ${local_pool}/${current_backup_marker} ${local_pool}/${previous_backup_marker} + if [[ $? != 0 ]]; then + echo "-E- local $zfs rename command failed" + exit 1 + fi + ssh $remote $zfs rename ${current_backup_marker} ${previous_backup_marker} + if [[ $? != 0 ]]; then + echo "-E- remote $zfs rename command failed" + exit 1 + fi + else - # We did not find a common snapshot, so send the entire filesystem - ssh $remote $zfs send -R $latest | - $zfs receive -vF -d ${local_pool}/${remote_fs%/*} -endif + # See what the most recent snapshot on the remote end is. + latest=$(tail -n 1 $remote_list) + + # I did this to make sure that diff would always display the most recent common + # Since we're keying off the context of the diff, we need to ensure we will get context + # by injecting a known difference in case no others exist in the lists. + echo bogus.remote >> $remote_list + echo bogus.local >> $local_list + common=$(diff -u $remote_list $local_list | grep '^ ' | tail -n 1) + + if [ -n "$common" ]; then + # We found a common snapshot + ssh $remote $zfs send -R -I${common/*@/@} $latest | + $zfs receive -vF -d ${local_pool}/${remote_fs%/*} + else + # We did not find a common snapshot, so send the entire filesystem + ssh $remote $zfs send -R $latest | + $zfs receive -vF -d ${local_pool}/${remote_fs%/*} + fi +fi + +# Remove tmp files +#rm -f $local_list $remote_list -rm -f $local_list $remote_list