#/bin/bash
+# Author: Carl Baldwin & Alan Pippin
+# Description: This script replicates a remote zfs filesystem to a local zfs pool.
+# This script will keep all snapshots in sync, removing the ones
+# that have been deleted since the last replicate was performed.
+# This script will only send the new, or missing, snapshots since
+# the last replicate was performed.
# Usage: replicate <hostname> <zfs filesystem>
+
+# source our configuration
+config="${0%/*}/zfs-scripts.conf"
+[ -e "${config}.dist" ] && . ${config}.dist
+[ -e "${config}" ] && . ${config}
+
+# command line arg parsing
remote=$1
remote_fs=$2
remote_pool=${2%%/*}
-remote_lockdir="/tmp/zfs-admin-lock"
hostname=`hostname`
-# Set the name of the local pool used to store the backup of the remote
-local_pool=backups
-
-# Set the email address to send notification to
-mailto=root@pippins.net
-mailx=/usr/bin/mailx
-
-# When this variable is set, local filesystems will be destroyed
-# before receiving a full streams into them from the remote source.
-destroy_local_filesystem_on_full_replicate=0
-
-# The ssh connection doesn't find zfs without this.
-zfs=/usr/sbin/zfs
-
# Setup our cleanup and exit trap
cleanup() {
if [[ -e "$local_list" ]]; then
rm -f $remote_list
fi
if [[ -n "$remote" ]]; then
- ssh $remote ls -d "$remote_lockdir" > /dev/null 2>&1
+ ssh $remote ls -d "$lockdir" > /dev/null 2>&1
if [[ $? == 0 ]]; then
- ssh $remote rm -rf "$remote_lockdir"
+ ssh $remote rm -rf "$lockdir"
fi
fi
}
fatal_and_exit() {
echo -e 2>&1 "$1"
+ # Destroy the backup markers on the local filesystem if they exist
+ if [[ -n "$current_backup_marker" ]]; then
+ zfs list -t snapshot ${local_pool}/${current_backup_marker} > /dev/null 2>&1
+ if [ $? == 0 ]; then
+ $zfs destroy ${local_pool}/${current_backup_marker}
+ fi
+ fi
+ if [[ -n "$previous_backup_marker" ]]; then
+ zfs list -t snapshot ${local_pool}/${previous_backup_marker} > /dev/null 2>&1
+ if [ $? == 0 ]; then
+ $zfs destroy ${local_pool}/${previous_backup_marker}
+ fi
+ fi
+ # send email notification
if [[ -n "$2" ]]; then
echo -e "$1" | $mailx -s "zfs replicate on $hostname failed" "$2"
fi
+ # exit
exit 1
}
trap fatal_and_exit INT
# Create the remote lockdir before continuing with the replicate
# Spinlock on creating the lock
maxsleeptime=60
-maxattempts=100
+maxattempts=400
attempts=0
while true; do
- ssh $remote mkdir "$remote_lockdir" >/dev/null 2>&1
+ ssh $remote mkdir "$lockdir" >/dev/null 2>&1
if [ $? != 0 ]; then
# Another zfs admin tool is running.
# Wait a random amount of time and try again
if [[ $attempts -gt $maxattempts ]]; then
# We've exceeded our maximum while loop count
echo "-E- The zfs filesystem has been locked down. Skipping replicate operation."
- fail_msg=`ssh $remote ls -ld $remote_lockdir 2>&1`
+ fail_msg=`ssh $remote ls -ld $lockdir 2>&1`
fatal_and_exit "zfs-replicate-all unable to obtain zfs admin lock:\n$fail_msg" $mailto
fi
done
fatal_and_exit "-E- remote incremental $zfs rollback command failed" $mailto
fi
# Now it should be safe to send the snaps
- ssh $remote $zfs send -R -I${previous_backup_marker} ${current_backup_marker} |
- $zfs receive -vF -d ${local_pool}/${remote_pool}
+ if [[ $throttle_enable == 1 && -e $throttle ]]; then
+ ssh $remote $zfs send -R -I${previous_backup_marker} ${current_backup_marker} |
+ $throttle $throttle_opt | $zfs receive -vF -d ${local_pool}/${remote_pool}
+ else
+ ssh $remote $zfs send -R -I${previous_backup_marker} ${current_backup_marker} |
+ $zfs receive -vF -d ${local_pool}/${remote_pool}
+ fi
if [ $? != 0 ]; then
fatal_and_exit "-E- remote incremental $zfs send command failed" $mailto
fi
if [[ -n "$common" ]]; then
# We found a common snapshot, incrementally send the new snaps
- ssh $remote $zfs send -R -I${common/*@/@} ${current_backup_marker} |
- $zfs receive -vF -d ${local_pool}/${remote_pool}
+ if [[ $throttle_enable == 1 && -e $throttle ]]; then
+ ssh $remote $zfs send -R -I${common/*@/@} ${current_backup_marker} |
+ $throttle $throttle_opt | $zfs receive -vF -d ${local_pool}/${remote_pool}
+ else
+ ssh $remote $zfs send -R -I${common/*@/@} ${current_backup_marker} |
+ $zfs receive -vF -d ${local_pool}/${remote_pool}
+ fi
if [ $? != 0 ]; then
fatal_and_exit "-E- remote incremental $zfs send command failed" $mailto
fi
fi
fi
# Send the full filesystem
- ssh $remote $zfs send -R ${current_backup_marker} |
- $zfs receive -vF -d ${local_pool}/${remote_pool}
+ if [[ $throttle_enable == 1 && -e $throttle ]]; then
+ ssh $remote $zfs send -R ${current_backup_marker} |
+ $throttle $throttle_opt | $zfs receive -vF -d ${local_pool}/${remote_pool}
+ else
+ ssh $remote $zfs send -R ${current_backup_marker} |
+ $zfs receive -vF -d ${local_pool}/${remote_pool}
+ fi
if [ $? != 0 ]; then
fatal_and_exit "-E- remote full $zfs send command failed" $mailto
fi