Skip to content

Commit 3b0e383

Browse files
committed
Implement snapshot replication.
1 parent d5cb31a commit 3b0e383

File tree

2 files changed

+135
-6
lines changed

2 files changed

+135
-6
lines changed

src/zfs-auto-snapshot.8

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,17 @@ PRE is 'zfs\-auto\-snap' by default.
4646
\fB\-q\fR, \fB\-\-quiet\fR
4747
Suppress warnings and notices at the console.
4848
.TP
49-
\fB\-\-send\-full\fR=\fIF\fR
50-
Send zfs full backup. Unimplemented.
49+
\fB\-\-send\-full\fR=[\fIremote host\fR]:[\fIremote pool\fR]
50+
Send zfs full backup to remote hostname (or IP address) and put it in remote pool.
5151
.TP
52-
\fB\-\-send\-incr\fR=\fIF\fR
53-
Send zfs incremental backup. Unimplemented.
52+
\fB\-\-send\-incr\fR=[\fIremote host\fR]:[\fIremote pool\fR]
53+
Send zfs incremental backup to remote hostname (or IP address) and put it in remote pool.
54+
.TP
55+
\fB\-\-send\-opts\fR=\fIOPTS\fR
56+
Option(s) passed to 'zfs send'.
57+
.TP
58+
\fB\-\-recv\-opts\fR=\fIOPTS\fR
59+
Option(s) passed to 'zfs receive'.
5460
.TP
5561
\fB\-\-sep\fR=\fICHAR\fR
5662
Use CHAR to separate date stamps in snapshot names.

src/zfs-auto-snapshot.sh

Lines changed: 125 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ opt_keep=''
3434
opt_label=''
3535
opt_prefix='zfs-auto-snap'
3636
opt_recursive=''
37+
opt_send_type=''
38+
opt_send_host=''
39+
opt_recv_pool=''
40+
opt_send_opts=''
41+
opt_recv_opts=''
3742
opt_sep='_'
3843
opt_setauto=''
3944
opt_syslog=''
@@ -50,6 +55,7 @@ WARNING_COUNT='0'
5055

5156
# Other global variables.
5257
SNAPSHOTS_OLD=''
58+
SNAPS_DONE=''
5359

5460

5561
print_usage ()
@@ -66,8 +72,10 @@ print_usage ()
6672
-l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly'.
6773
-p, --prefix=PRE PRE is 'zfs-auto-snap' by default.
6874
-q, --quiet Suppress warnings and notices at the console.
69-
--send-full=F Send zfs full backup. Unimplemented.
70-
--send-incr=F Send zfs incremental backup. Unimplemented.
75+
--send-full=F Send zfs full backup.
76+
--send-incr=F Send zfs incremental backup.
77+
--send-opts=F Option(s) passed to 'zfs send'.
78+
--recv-opts=F Option(s) passed to 'zfs receive'.
7179
--sep=CHAR Use CHAR to separate date stamps in snapshot names.
7280
-g, --syslog Write messages into the system log.
7381
-r, --recursive Snapshot named filesystem and all descendants.
@@ -144,6 +152,64 @@ do_run () # [argv]
144152
}
145153

146154

155+
find_last_snap () # dset, GLOB
156+
{
157+
local snap="$1"
158+
local GLOB="$2"
159+
160+
local dset="${snap%@*}"
161+
local last_snap
162+
local jj
163+
164+
# STEP 1: Go through ALL snapshots that exist, look for exact
165+
# match on dataset/volume (with snapshot matching 'GLOB').
166+
for jj in $SNAPSHOTS_OLD
167+
do
168+
# Check whether this is an old snapshot of the filesystem.
169+
if [ -z "${jj#$dset@$GLOB}" ]; then
170+
# We want the FIRST one (which is the last in time
171+
# before the one we just created in do_snapshot()).
172+
# Also, here we just need the snapshot name.
173+
last_snap="${jj#*@}"
174+
break
175+
fi
176+
done
177+
178+
# NOTE: If we don't have any previous snapshots (for example, we've
179+
# just created the first one) we can end up with last_snap=''
180+
# here.
181+
# If we're called with '--send-incr' we have to options:
182+
# 1: We change from incremental to full.
183+
# 2: We accept that the user have said INCR, and stick with
184+
# it.
185+
if [ "$opt_send_type" = "incr" -a -z "$last_snap" ]; then
186+
if [ -n "$opt_verbose" ]; then
187+
echo > /dev/stderr "WARNING: No previous snapshots exist but we where called"
188+
echo > /dev/stderr " with --send-incr. Can not continue."
189+
echo > /dev/stderr " Please rerun with --send-full."
190+
fi
191+
return 1
192+
fi
193+
194+
if [ -n "$opt_recursive" -a -n "$last_snap" ]; then
195+
# STEP 2: Again, go through ALL snapshots that exists, but this
196+
# time only look for the snapshots that 'starts with'
197+
# the dataset/volume in question AND 'ends with'
198+
# the exact snapshot name/date in step 2.
199+
for jj in $SNAPSHOTS_OLD
200+
do
201+
if echo "$jj" | grep -qE "^$dset.*@$last_snap"; then
202+
echo $jj
203+
fi
204+
done
205+
else
206+
echo "$snap"
207+
fi
208+
209+
return 0
210+
}
211+
212+
147213
do_snapshots () # properties, flags, snapname, oldglob, [targets...]
148214
{
149215
local PROPS="$1"
@@ -170,6 +236,8 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
170236
if [ $RUNSNAP -eq 1 ] && do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
171237
then
172238
[ "$opt_post_snapshot" != "" ] && do_run "$opt_post_snapshot $ii $NAME"
239+
[ -n "$opt_send_host" ] && SNAPS_DONE="$SNAPS_DONE
240+
$ii@$NAME"
173241
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
174242
else
175243
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
@@ -203,6 +271,34 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...]
203271
done
204272
}
205273

274+
do_send () # snapname, oldglob
275+
{
276+
local NAME="$1"
277+
local GLOB="$2"
278+
local remote="ssh $opt_send_host zfs receive $opt_recv_opts $opt_recv_pool"
279+
local ii
280+
local jj
281+
282+
# STEP 1: Go throug all snapshots we've created
283+
for ii in $SNAPS_DONE
284+
do
285+
opts=''
286+
SNAPS_SEND=''
287+
288+
# STEP 2: Find the last snapshot
289+
SNAPS_SEND=$(find_last_snap "$ii" "$GLOB")
290+
291+
# STEP 3: Go through all snapshots that is to be transfered and send them.
292+
for jj in $SNAPS_SEND
293+
do
294+
if [ "$opt_send_type" = "incr" ]; then
295+
do_run "zfs send $opt_send_opts -i $jj $ii | $remote"
296+
else
297+
do_run "zfs send $opt_send_opts -R $jj | $remote"
298+
fi
299+
done
300+
done
301+
}
206302

207303
# main ()
208304
# {
@@ -212,6 +308,7 @@ GETOPT=$(getopt \
212308
--longoptions=event:,keep:,label:,prefix:,sep: \
213309
--longoptions=debug,help,quiet,syslog,verbose \
214310
--longoptions=pre-snapshot:,post-snapshot:,destroy-only \
311+
--longoptions=send-full:,send-incr:,send-opts:,recv-opts: \
215312
--options=dnshe:l:k:p:rs:qgv \
216313
-- "$@" ) \
217314
|| exit 128
@@ -296,6 +393,30 @@ do
296393
opt_recursive='1'
297394
shift 1
298395
;;
396+
(--send-full)
397+
opt_send_type='full'
398+
399+
opt_send_host=$(echo "$2" | sed 's,:.*,,')
400+
opt_recv_pool=$(echo "$2" | sed 's,.*:,,')
401+
402+
opt_send_opts="$opt_send_opts -R"
403+
shift 2
404+
;;
405+
(--send-incr)
406+
opt_send_type='incr'
407+
408+
opt_send_host=$(echo "$2" | sed 's,:.*,,')
409+
opt_recv_pool=$(echo "$2" | sed 's,.*:,,')
410+
shift 2
411+
;;
412+
(--send-opts)
413+
opt_send_opts="$2"
414+
shift 2
415+
;;
416+
(--recv-opts)
417+
opt_recv_opts="$2"
418+
shift 2
419+
;;
299420
(--sep)
300421
case "$2" in
301422
([[:alnum:]_.:\ -])
@@ -567,6 +688,8 @@ test -n "$opt_dry_run" \
567688
do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR"
568689
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE"
569690

691+
do_send "$SNAPNAME" "$SNAPGLOB"
692+
570693
print_log notice "@$SNAPNAME," \
571694
"$SNAPSHOT_COUNT created," \
572695
"$DESTRUCTION_COUNT destroyed," \

0 commit comments

Comments
 (0)