Removed import command
[zfs-ubuntu/.git] / zfs-replicate
1 #!/bin/bash
2
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.
9
10 # In test mode (test=1) commands are echoed, not executed
11 test=0
12
13 [ $test == 0 ] && exec >> /var/log/zfs/zfs-replicate.log 2>&1
14
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.
19
20 fs=$1
21 fs=${fs%/}
22 fsname=${1#*/}
23 srcpool=${1%%/*}
24 srcfs="${srcpool}/$fsname"
25 srcfs=${srcfs%/}
26 dstpool=$2
27 dstfs="${dstpool}/$srcfs"
28 dstfs=${dstfs%/}
29 nodstsnaps=0
30 common=""
31
32 if [ $test == 1 ]; then
33   echo "fs: $fs"
34   echo "fsname: $fsname"
35   echo "srcpool: $srcpool"
36   echo "srcfs: $srcfs"
37   echo "dstpool: $dstpool"
38   echo "dstfs: $dstfs"
39 fi
40
41 if ! zpool list -H "$srcpool" >/dev/null 2>&1; then
42   echo >&2 "-E- The source pool, '$srcpool' doesn't seem to exist."
43   exit 1
44 fi
45
46 if ! zpool list -H "$dstpool" >/dev/null 2>&1; then
47   echo >&2 "-E- The destination pool, '$dstpool' doesn't seem to exist."
48   exit 1
49 fi
50
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: $dstfs"
53   if zfs list -t filesystem | grep "$dstfs"; then
54    echo >&2 "-I- Found zfs filesystem $dstfs on the destination pool $dstpool without any snapshots"
55    echo >&2 "-I- Removing the zfs filesystem: $dstfs"
56    zfs destroy "$dstfs" 
57   fi
58   nodstsnaps=1
59 fi
60
61 if [ $nodstsnaps == 0 ]; then
62   zfs list -rH -t snapshot $srcfs | grep "$srcfs@" | awk '{print $1}' > /tmp/source-list
63   zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | sed "s,$dstpool/,," | awk '{print $1}' > /tmp/destination-list
64   diff -u /tmp/source-list /tmp/destination-list | grep -v '^+++' | awk '/^\+/ {print}' | sed "s,^\+,$dstpool/," > /tmp/obsolete-snapshots
65   rm -f /tmp/source-list /tmp/destination-list
66
67   echo >&2 "Removing obsolete backups from the destination pool" 
68   for snapshot in $(cat /tmp/obsolete-snapshots); do
69     echo >&2 "Removing '$snapshot' from destination."
70     [ $test == 0 ] && zfs destroy "$snapshot"
71   done
72
73   echo >&2 "Rolling back to the most recent snapshot on the destination." 
74   [ $test == 0 ] && zfs rollback $(zfs list -rH -t snapshot $dstfs | grep "$dstfs@" | awk '{snap=$1} END {print snap}')
75
76   echo >&2 "Calculating the most recent common snapshot between the two filesystems." 
77   if zfs list -H "$dstfs" > /dev/null 2>&1; then
78     for snap in $(zfs list -rH -t snapshot "$dstfs" | grep "$dstfs@" |
79                     sed 's,.*@,,' | awk '{print$1}'); do
80       if zfs list -rH -t snapshot "$fs" | grep "$fs@" | sed 's,.*@,,' | awk '{print$1}' | grep "^${snap}$" >/dev/null 2>&1; then
81         common=$snap
82       fi  
83     done
84   fi
85 fi
86
87 base=$common
88 foundcommon=false
89 if [ -z "$common" ]; then
90   foundcommon=true
91 fi
92
93 for snap in $(zfs list -rH -t snapshot "$fs" | grep "$fs@" |
94                 sed 's,.*@,,' | awk '{print$1}'); do
95   if [ "$snap" = "$common" ]; then
96     foundcommon=true
97     continue
98   fi
99
100   if $foundcommon; then
101     if [ -z "$base" ]; then
102       echo >&2 "Sending '$fs@$snap'"
103       [ $test == 0 ] && zfs set readonly=on "$dstpool"
104       [ $test == 0 ] && zfs set atime=off "$dstpool"
105       [ $test == 0 ] && zfs set sharenfs=off "$dstpool"
106       [ $test == 0 ] && zfs set mountpoint=legacy "$dstpool"
107       [ $test == 0 ] && zfs send "$fs@$snap" | zfs recv -v "$dstfs"
108     else
109       echo >&2 "Sending '$fs@$base' -> '$fs@$snap'"
110       [ $test == 0 ] && zfs send -i "$fs@$base" "$fs@$snap" | zfs recv -v "$dstfs"
111     fi
112     base=$snap
113   fi
114 done
115
116 true