3 # Author: Carl Baldwin & Alan Pippin
4 # Description: This script replicates a given zfs filesystem to a given zfs pool.
5 # This script will keep all snapshots in sync, removing the ones
6 # that have been deleted since the last replicate was performed.
7 # This script will only send the new, or missing, snapshots since
8 # the last replicate was performed.
10 # In test mode (test=1) commands are echoed, not executed
13 [ $test == 0 ] && exec >> /var/log/zfs-replicate.log 2>&1
15 # Usage: zfs-backup [filesystem] [destination_pool]
16 # This script has a limitation with children under a given filesystem.
17 # You must initially backup the parent filesystems first using this script
18 # before backing up any of the children filesystems.
24 srcfs="${srcpool}/$fsname"
27 dstfs="${dstpool}/$srcfs"
32 if [ $test == 1 ]; then
34 echo "fsname: $fsname"
35 echo "srcpool: $srcpool"
37 echo "dstpool: $dstpool"
41 if ! zpool list -H "$srcpool" >/dev/null 2>&1; then
42 echo >&2 "-E- The source pool, '$srcpool' doesn't seem to exist."
46 if ! zpool list -H "$dstpool" >/dev/null 2>&1; then
47 echo >&2 "-E- The destination pool, '$dstpool' doesn't seem to exist."
51 if ! zfs list -rH -t snapshot "$dstfs" 2>&1 | grep "$dstfs@" > /dev/null 2>&1; then
52 echo >&2 "-W- No snapshots detected on the destination drive for this filesystem"
56 if [ $nodstsnaps == 0 ]; then
57 zfs list -rH -t snapshot $srcfs | grep "$srcfs@" | awk '{print $1}' > /tmp/source-list
58 zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | sed "s,$dstpool/,," | awk '{print $1}' > /tmp/destination-list
59 diff -u /tmp/source-list /tmp/destination-list | grep -v '^+++' | awk '/^\+/ {print}' | sed "s,^\+,$dstpool/," > /tmp/obsolete-snapshots
60 rm -f /tmp/source-list /tmp/destination-list
62 echo >&2 "Removing obsolete backups from the destination pool"
63 for snapshot in $(cat /tmp/obsolete-snapshots); do
64 echo >&2 "Removing '$snapshot' from destination."
65 [ $test == 0 ] && zfs destroy "$snapshot"
68 echo >&2 "Rolling back to the most recent snapshot on the destination."
69 [ $test == 0 ] && zfs rollback $(zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | awk '{snap=$1} END {print snap}')
71 echo >&2 "Calculating the most recent common snapshot between the two filesystems."
72 if zfs list -H "$dstfs" > /dev/null 2>&1; then
73 for snap in $(zfs list -rH -t snapshot "$dstfs" | grep "$dstfs@" |
74 sed 's,.*@,,' | awk '{print$1}'); do
75 if zfs list -rH -t snapshot "$fs" | grep "$fs@" | sed 's,.*@,,' | awk '{print$1}' | grep "^${snap}$" >/dev/null 2>&1; then
84 if [ -z "$common" ]; then
88 for snap in $(zfs list -rH -t snapshot "$fs" | grep "$fs@" |
89 sed 's,.*@,,' | awk '{print$1}'); do
90 if [ "$snap" = "$common" ]; then
96 if [ -z "$base" ]; then
97 echo >&2 "Sending '$fs@$snap'"
98 [ $test == 0 ] && zfs set readonly=on "$dstpool"
99 [ $test == 0 ] && zfs set atime=off "$dstpool"
100 [ $test == 0 ] && zfs set sharenfs=off "$dstpool"
101 [ $test == 0 ] && zfs set mountpoint=legacy "$dstpool"
102 [ $test == 0 ] && zfs send "$fs@$snap" | zfs recv -v "$dstfs"
104 echo >&2 "Sending '$fs@$base' -> '$fs@$snap'"
105 [ $test == 0 ] && zfs send -i "$fs@$base" "$fs@$snap" | zfs recv -v "$dstfs"