From df8667bff265d0848569a816c6c6365d45108b84 Mon Sep 17 00:00:00 2001 From: Turbo Fredriksson Date: Thu, 2 Apr 2015 21:50:53 +0200 Subject: [PATCH] Implement snapshot replication. --- src/zfs-auto-snapshot.8 | 14 +++-- src/zfs-auto-snapshot.sh | 110 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 4 deletions(-) diff --git a/src/zfs-auto-snapshot.8 b/src/zfs-auto-snapshot.8 index 51fdc7f..1ca11b0 100644 --- a/src/zfs-auto-snapshot.8 +++ b/src/zfs-auto-snapshot.8 @@ -46,11 +46,17 @@ PRE is 'zfs\-auto\-snap' by default. \fB\-q\fR, \fB\-\-quiet\fR Suppress warnings and notices at the console. .TP -\fB\-\-send\-full\fR=\fIF\fR -Send zfs full backup. Unimplemented. +\fB\-\-send\-full\fR=\fIhost|ip address\fR +Send zfs full backup to \fIhost\fR (or \fIip address\fR). .TP -\fB\-\-send\-incr\fR=\fIF\fR -Send zfs incremental backup. Unimplemented. +\fB\-\-send\-incr\fR=\fIhost\fR +Send zfs incremental backup to \fIhost\fR (or \fIip address\fR). +.TP +\fB\-\-send\-opts\fR=\fIOPTS\fR +Option(s) passed to 'zfs send'. +.TP +\fB\-\-recv\-opts\fR=\fIOPTS\fR +Option(s) passed to 'zfs receive'. .TP \fB\-\-sep\fR=\fICHAR\fR Use CHAR to separate date stamps in snapshot names. diff --git a/src/zfs-auto-snapshot.sh b/src/zfs-auto-snapshot.sh index 2a82c9a..6db169f 100755 --- a/src/zfs-auto-snapshot.sh +++ b/src/zfs-auto-snapshot.sh @@ -34,6 +34,11 @@ opt_keep='' opt_label='' opt_prefix='zfs-auto-snap' opt_recursive='' +opt_send_type='' +opt_send_host='' +opt_recv_pool='' +opt_send_opts='' +opt_recv_opts='' opt_sep='_' opt_setauto='' opt_syslog='' @@ -68,6 +73,8 @@ print_usage () -q, --quiet Suppress warnings and notices at the console. --send-full=F Send zfs full backup. Unimplemented. --send-incr=F Send zfs incremental backup. Unimplemented. + --send-opts=F Option(s) passed to 'zfs send'. + --recv-opts=F Option(s) passed to 'zfs receive'. --sep=CHAR Use CHAR to separate date stamps in snapshot names. -g, --syslog Write messages into the system log. -r, --recursive Snapshot named filesystem and all descendants. @@ -159,6 +166,8 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...] # global WARNING_COUNT # global SNAPSHOTS_OLD + SNAPS_DONE='' + for ii in $TARGETS do if [ -n "$opt_do_snapshots" ] @@ -170,6 +179,8 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...] if [ $RUNSNAP -eq 1 ] && do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'" then [ "$opt_post_snapshot" != "" ] && do_run "$opt_post_snapshot $ii $NAME" + [ -n "$opt_send_host" ] && SNAPS_DONE="$SNAPS_DONE +$ii@$NAME" SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 )) else WARNING_COUNT=$(( $WARNING_COUNT + 1 )) @@ -203,6 +214,78 @@ do_snapshots () # properties, flags, snapname, oldglob, [targets...] done } +do_send () # snapname, oldglob +{ + local NAME="$1" + local GLOB="$2" + local remote="ssh $opt_send_host zfs receive $opt_recv_opts $opt_recv_pool" + local ii + + # STEP 1: Go throug all snapshots we've created + for ii in $SNAPS_DONE + do + opts='' + SNAPS_SEND='' + + dset=${ii%@*} + + # STEP 2: Go through ALL snapshots that exist, look for exact + # match on dataset/volume (with snapshot matching 'GLOB'). + for jj in $SNAPSHOTS_OLD + do + # Check whether this is an old snapshot of the filesystem. + if [ -z "${jj#$dset@$GLOB}" ]; then + # We want the FIRST one (which is the last in time + # before the one we just created in do_snapshot()). + # Also, here we just need the snapshot name. + last_snap="${jj#*@}" + break + fi + done + + # NOTE: If we don't have any previous snapshots (for example, we've + # just created the first one) we can end up with last_snap='' + # here. + # If we're called with '--send-incr' we have to options: + # 1: We change from incremental to full. + # 2: We accept that the user have said INCR, and stick with + # it. + if [ "$opt_send_type" = "incr" -a -z "$last_snap" ]; then + if [ -n "$opt_verbose" ]; then + echo "WARNING: No previous snapshots exist but we where called" + echo " with --send-incr. Can not continue." + echo " Please rerun with --send-full." + fi + return 1 + fi + + if [ -n "$opt_recursive" -a -n "$last_snap" ]; then + # STEP 3: Again, go through ALL snapshots that exists, but this + # time only look for the snapshots that 'starts with' + # the dataset/volume in question AND 'ends with' + # the exact snapshot name/date in step 2. + for jj in $SNAPSHOTS_OLD + do + if echo "$jj" | grep -qE "^$dset.*@$last_snap"; then + SNAPS_SEND="$SNAPS_SEND +$jj" + fi + done + else + SNAPS_SEND="$ii" + fi + + # STEP 4: Go through all snapshots that is to be transfered and send them. + for jj in $SNAPS_SEND + do + if [ "$opt_send_type" = "incr" ]; then + do_run "zfs send $opt_send_opts -i $jj $ii | $remote" + else + do_run "zfs send $opt_send_opts -R $jj | $remote" + fi + done + done +} # main () # { @@ -212,6 +295,7 @@ GETOPT=$(getopt \ --longoptions=event:,keep:,label:,prefix:,sep: \ --longoptions=debug,help,quiet,syslog,verbose \ --longoptions=pre-snapshot:,post-snapshot:,destroy-only \ + --longoptions=send-full:,send-incr:,send-opts:,recv-opts: \ --options=dnshe:l:k:p:rs:qgv \ -- "$@" ) \ || exit 128 @@ -296,6 +380,30 @@ do opt_recursive='1' shift 1 ;; + (--send-full) + opt_send_type='full' + + opt_send_host=$(echo "$2" | sed 's,:.*,,') + opt_recv_pool=$(echo "$2" | sed 's,.*:,,') + + opt_send_opts="$opt_send_opts -R" + shift 2 + ;; + (--send-incr) + opt_send_type='incr' + + opt_send_host=$(echo "$2" | sed 's,:.*,,') + opt_recv_pool=$(echo "$2" | sed 's,.*:,,') + shift 2 + ;; + (--send-opts) + opt_send_opts="$2" + shift 2 + ;; + (--recv-opts) + opt_recv_opts="$2" + shift 2 + ;; (--sep) case "$2" in ([[:alnum:]_.:\ -]) @@ -567,6 +675,8 @@ test -n "$opt_dry_run" \ do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR" do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE" +do_send "$SNAPNAME" "$SNAPGLOB" + print_log notice "@$SNAPNAME," \ "$SNAPSHOT_COUNT created," \ "$DESTRUCTION_COUNT destroyed," \