Skip to content

Commit 98f7e5a

Browse files
committed
add -r option and sample configurations
Implement the '-r N' option to use the Nth most recent local snapshot instead of the newest. This is useful for avoiding short-lived (e.g. hourly) snapshots during a long-running backup, e.g. if the backup hasn't run for a while. Also add some sample configuration files, for normal and catchup use.
1 parent 45e0a58 commit 98f7e5a

File tree

4 files changed

+84
-11
lines changed

4 files changed

+84
-11
lines changed

README

+28-9
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,22 @@ snapshots continue to be taken even if the backup fails. It does not
88
necessarily require that package -- anything that regularly generates
99
snapshots that follow a given pattern will suffice.
1010

11+
Command-line options:
12+
-n debug/dry-run mode
13+
-v verbose mode
14+
-f file specify a configuration file
15+
-r N use the Nth most recent local snapshot rather than the newest
16+
-h, -? display help message
17+
1118

1219
Basic installation: After following the prerequisites, run manually to verify
1320
operation, and then add a line like the following to zfssnap's crontab:
14-
30 * * * * /path/to/zfs-backup.sh
21+
30 * * * * /path/to/zfs-backup.sh [ options ]
1522
(This for an hourly sync -- adjust accordingly if you only want to back up
16-
daily, etc.)
23+
daily, etc. zfs-backup now supports commandline options and configuration
24+
files, so you can schedule different cron jobs with different config files,
25+
e.g. to back up to two different targets. If you schedule multiple cron
26+
jobs, you should use different lockfiles in each configuration.)
1727

1828
This aims to be much more robust than the backup functionality of
1929
zfs-auto-snapshot, namely:
@@ -27,21 +37,23 @@ zfs-auto-snapshot, namely:
2737

2838
PREREQUISITES:
2939
1. zfs-auto-snapshot or equivalent package installed locally and regular
30-
snapshots enabled (hourly, daily, etc.)
31-
2. home directory set for zfssnap role
40+
snapshots enabled (hourly, daily, etc.)
41+
2. home directory set for zfssnap role (the user taking snapshots and doing
42+
the sending)
3243
3. ssh keys set up between zfssnap@localhost and remuser@remhost
3344
4. zfs allow done for remuser on remhost
3445
(see http://mail.opensolaris.org/pipermail/zfs-auto-snapshot/2009-November/000198.html
3546
for guidance on #2-4; you may need to also allow further permissions such as
3647
sharenfs, userprop, hold, etc.)
3748
5. an initial (full) zfs send/receive done so that remhost has the fs we
38-
are backing up, and the associated snapshots -- something like:
49+
are backing up, and the associated snapshots -- something like:
3950
zfs send -R $POOL/$FS@zfs-auto-snap_daily-(latest) | ssh $REMUSER@$REMHOST zfs recv -dvF $REMPOOL
51+
Note: 'zfs send -R' will send *all* snapshots associated with a dataset, so
52+
if you wish to purge old snapshots, do that first.
4053
6. zfs allow any additional permissions needed, to fix any errors produced in step 5
4154
7. configure the tag/prop/remuser/remhost/rempool variables in this script or in a config file
42-
(and update the CFG=... line accordingly)
4355
8. zfs set $PROP={ fullpath | basename } pool/fs
44-
for each FS or volume you wish to back up.
56+
for each FS or volume you wish to back up.
4557

4658
PROPERTY VALUES:
4759
Given the hierarchy pool/a/b,
@@ -59,8 +71,15 @@ It's probably best to do a dry-run first (zfs recv -ndvF).
5971

6072
Note: I use daily snapshots in these manual send/recv examples because
6173
it is less likely that the snapshot you are using will be rotated out
62-
in the middle of a send. Also, ZFS will send all snapshots for a given
63-
filesystem before sending any for its children.
74+
in the middle of a send. Also, note that ZFS will send all snapshots for a
75+
given filesystem before sending any for its children, rather than going in
76+
global date order.
77+
78+
Alternatively, use a different tag (e.g. weekly) that still has common
79+
snapshots, possibly in combination with the -r option (Nth most recent) to
80+
avoid short-lived snapshots (e.g. hourly) being rotated out in the middle
81+
of your sync. This is a good use case for an alternate configuration file.
82+
6483

6584
PROCEDURE:
6685
* find newest local hourly snapshot

example-catchup.cfg

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Use the standard backup settings, but use daily snapshots to catch up
2+
# in case of missed backups.
3+
4+
# $0 the is backup script that sources this, but $1 is in fact this very file.
5+
. `dirname $1`/example.cfg
6+
7+
TAG="zfs-auto-snap_daily"
8+
VERBOSE="-v"
9+
10+
# Use the 3rd most recent daily snapshot, to avoid the hourly snapshots
11+
# entirely. To override this and get the most recent snapshot (like -N in the
12+
# old zfs-bak-sync-daily script), use '-r 1' on the cmdline. I recommend you
13+
# first run it as-is, then run with -r 1 afterwards. After that it should be
14+
# ready to re-enable the hourly backups.
15+
RECENT=3

example.cfg

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# sample configuration for hourly backups
2+
3+
# Don't forget to schedule a cron job such as:
4+
# 30 * * * * /path/to/zfs-backup.sh /path/to/this.cfg
5+
6+
TAG="zfs-auto-snap_hourly"
7+
LOCK="/var/tmp/zfsbackup.lock"
8+
# remote settings (on destination host)
9+
REMUSER="zfsbak"
10+
REMHOST="backupserver.local"
11+
REMPOOL="backuppool"

zfs-backup.sh

+30-2
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,20 @@ usage() {
7474
echo " -n\t\tdebug (dry-run) mode"
7575
echo " -v\t\tverbose mode"
7676
echo " -f\t\tspecify a configuration file"
77+
echo " -r N\t\tuse the Nth most recent snapshot instead of the newest"
7778
echo "If the configuration file is last option specified, the -f flag is optional."
7879
exit 1
7980
}
81+
# simple ordinal function, does not validate input
82+
ord() {
83+
case $1 in
84+
1|*[0,2-9]1) echo "$1st";;
85+
2|*[0,2-9]2) echo "$1nd";;
86+
3|*[0,2-9]3) echo "$1rd";;
87+
*1[123]|*[0,4-9]) echo "$1th";;
88+
*) echo $1;;
89+
esac
90+
}
8091

8192
# Option parsing
8293
set -- $(getopt h?nvf:r: $*)
@@ -89,6 +100,7 @@ for opt; do
89100
-n) dbg_flag=Y; shift;;
90101
-v) verb_flag=Y; shift;;
91102
-f) CFG=$2; shift 2;;
103+
-r) recent_flag=$2; shift 2;;
92104
--) shift; break;;
93105
esac
94106
done
@@ -108,6 +120,7 @@ fi
108120
# Set options now, so cmdline opts override the cfg file
109121
[ "$dbg_flag" ] && DEBUG=1
110122
[ "$verb_flag" ] && VERBOSE="-v"
123+
[ "$recent_flag" ] && RECENT=$recent_flag
111124
# set default value so integer tests work
112125
if [ -z "$RECENT" ]; then RECENT=0; fi
113126

@@ -134,9 +147,15 @@ do_backup() {
134147
return 2
135148
fi
136149

137-
newest_local="$(/usr/sbin/zfs list -t snapshot -H -S creation -o name -d 1 $DATASET | grep $TAG | head -1)"
150+
if [ $RECENT -gt 1 ]; then
151+
newest_local="$(/usr/sbin/zfs list -t snapshot -H -S creation -o name -d 1 $DATASET | grep $TAG | awk NR==$RECENT)"
152+
msg="using local snapshot ($(ord $RECENT) most recent):"
153+
else
154+
newest_local="$(/usr/sbin/zfs list -t snapshot -H -S creation -o name -d 1 $DATASET | grep $TAG | head -1)"
155+
msg="newest local snapshot:"
156+
fi
138157
snap2=${newest_local#*@}
139-
[ "$DEBUG" -o "$VERBOSE" ] && echo "newest local snapshot: $snap2"
158+
[ "$DEBUG" -o "$VERBOSE" ] && echo "$msg $snap2"
140159

141160
# needs public key auth configured beforehand
142161
newest_remote="$(ssh -n $REMUSER@$REMHOST /usr/sbin/zfs list -t snapshot -H -S creation -o name -d 1 $TARGET | grep $TAG | head -1)"
@@ -172,6 +191,15 @@ do_backup() {
172191
return 0
173192
fi
174193

194+
# sanity checking of snapshot times -- avoid going too far back with -r
195+
snap1time=$(zfs get -Hp -o value creation $DATASET@$snap1)
196+
snap2time=$(zfs get -Hp -o value creation $DATASET@$snap2)
197+
if [ $snap2time -lt $snap1time ]; then
198+
echo "Error: target snapshot $snap2 is older than $snap1!"
199+
echo "Did you go too far back with '-r'?"
200+
return 1
201+
fi
202+
175203
if [ $DEBUG ]; then
176204
echo "would run: /usr/sbin/zfs send -R -I $snap1 $DATASET@$snap2 |"
177205
echo " ssh $REMUSER@$REMHOST /usr/sbin/zfs recv $RECV_OPT -vF $REMPOOL"

0 commit comments

Comments
 (0)