1 Commits

Author SHA1 Message Date
b2bf3f2c9d Initial debian packaging. 2011-11-21 20:02:30 -06:00
16 changed files with 181 additions and 399 deletions

View File

@ -2,16 +2,6 @@ all:
install: install:
install -d $(DESTDIR)$(PREFIX)/etc/cron.d install -d $(DESTDIR)$(PREFIX)/etc/cron.d
install -d $(DESTDIR)$(PREFIX)/etc/cron.daily install etc/zfs-auto-snapshot.cron $(DESTDIR)$(PREFIX)/etc/cron.d/zfs-auto-snapshot
install -d $(DESTDIR)$(PREFIX)/etc/cron.hourly
install -d $(DESTDIR)$(PREFIX)/etc/cron.weekly
install -d $(DESTDIR)$(PREFIX)/etc/cron.monthly
install etc/zfs-auto-snapshot.cron.frequent $(DESTDIR)$(PREFIX)/etc/cron.d/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.hourly $(DESTDIR)$(PREFIX)/etc/cron.hourly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.daily $(DESTDIR)$(PREFIX)/etc/cron.daily/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.weekly $(DESTDIR)$(PREFIX)/etc/cron.weekly/zfs-auto-snapshot
install etc/zfs-auto-snapshot.cron.monthly $(DESTDIR)$(PREFIX)/etc/cron.monthly/zfs-auto-snapshot
install -d $(DESTDIR)$(PREFIX)/share/man/man8
install src/zfs-auto-snapshot.8 $(DESTDIR)$(PREFIX)/share/man/man8/zfs-auto-snapshot.8
install -d $(DESTDIR)$(PREFIX)/sbin install -d $(DESTDIR)$(PREFIX)/sbin
install src/zfs-auto-snapshot.sh $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot install src/zfs-auto-snapshot.sh $(DESTDIR)$(PREFIX)/sbin/zfs-auto-snapshot

10
README
View File

@ -1,12 +1,4 @@
zfs-auto-snapshot: An alternative implementation of the zfs-auto-snapshot service for Linux.
An alternative implementation of the zfs-auto-snapshot service for Linux
that is compatible with zfs-linux and zfs-fuse.
Automatically create, rotate, and destroy periodic ZFS snapshots. This is
the utility that creates the @zfs-auto-snap_frequent, @zfs-auto-snap_hourly,
@zfs-auto-snap_daily, @zfs-auto-snap_weekly, and @zfs-auto-snap_monthly
snapshots if it is installed.
This program is a posixly correct bourne shell script. It depends only on This program is a posixly correct bourne shell script. It depends only on
the zfs utilities and cron, and can run in the dash shell. the zfs utilities and cron, and can run in the dash shell.

6
debian/changelog vendored Normal file
View File

@ -0,0 +1,6 @@
zfs-auto-snapshot (1.0.0) oneiric; urgency=low
* Initial release.
* Initial debian packaging.
-- Darik Horn <dajhorn@vanadac.com> Mon, 21 Nov 2011 20:01:19 -0600

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
7

16
debian/control vendored Normal file
View File

@ -0,0 +1,16 @@
Source: zfs-auto-snapshot
Section: admin
Priority: extra
Maintainer: Darik Horn <dajhorn@vanadac.com>
Build-Depends: debhelper (>= 7.4)
Standards-Version: 3.8.4
Homepage: http://www.zfsonlinux.org/
Vcs-Git: git://github.com/dajhorn/zfs-auto-snapshot.git
Vcs-Browser: https://github.com/dajhorn/zfs-auto-snapshot/
Package: zfs-auto-snapshot
Architecture: all
Depends: ${misc:Depends}, cron, zfsutils
Description: ZFS Automatic Snapshot Service
Automatically create and destroy ZFS snapshots on an hourly, daily, weekly
and monthly schedule.

1
debian/copyright vendored Normal file
View File

@ -0,0 +1 @@
Copyright 2011 Darik Horn <dajhorn@vanadac.com>

4
debian/rules vendored Executable file
View File

@ -0,0 +1,4 @@
#!/usr/bin/make -f
%:
dh $@

2
debian/watch vendored Normal file
View File

@ -0,0 +1,2 @@
version=3
http://githubredir.debian.net/github/dajhorn/zfs-auto-snapshot/([\d\.].*).tar.gz

View File

@ -0,0 +1,8 @@
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
OPTIONS="--quiet --syslog"
* */4 * * * root zfs-auto-snapshot $OPTIONS --label=frequent --keep=4 //
@hourly root zfs-auto-snapshot $OPTIONS --label=hourly --keep=24 //
@daily root zfs-auto-snapshot $OPTIONS --label=daily --keep=31 //
@weekly root zfs-auto-snapshot $OPTIONS --label=weekly --keep=4 //
@monthly root zfs-auto-snapshot $OPTIONS --label=monthly --keep=12 //

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=daily --keep=31 //

View File

@ -1,3 +0,0 @@
PATH="/usr/bin:/bin:/usr/sbin:/sbin"
*/15 * * * * root zfs-auto-snapshot -q -g --label=frequent --keep=4 //

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=hourly --keep=24 //

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=monthly --keep=12 //

View File

@ -1,2 +0,0 @@
#!/bin/sh
exec zfs-auto-snapshot --quiet --syslog --label=weekly --keep=8 //

View File

@ -1,70 +0,0 @@
.TH ZFS-AUTO-SNAPSHOT "8" "June 16, 2013" "zfs-auto-snapshot.sh" "System Administration Commands"
.SH NAME
zfs-auto-snapshot \- take regular ZFS snapshots
.SH SYNOPSIS
.B zfs-auto-snapshot
[\fIoptions\fR] [\fI-l label\fR] \fI<'//' | name \fR[\fIname\fR...]\fI>\fR
.SH DESCRIPTION
.B zfs-auto-snapshot
automatically creates, rotates, and destroys snapshots for all your
ZFS datasets, and is compatible with both zfsonlinux and zfs-fuse.
.SH OPTIONS
.TP
\fB\-\-default\-exclude\fR
By default \fBzfs-auto-snapshot\fR will snapshot all datasets except
for those in which the user-property \fBcom.sun:auto-snapshot\fR is
set to \fBfalse\fR. This option reverses the behavior and requires
\fBcom.sun:auto-snapshot\fR to be set to \fBtrue\fR.
.TP
\fB\-d\fR, \fB\-\-debug\fR
Print debugging messages.
.TP
\fB\-e\fR, \fB\-\-event\fR=\fIEVENT\fR
Set the com.sun:auto\-snapshot\-desc property to EVENT.
.TP
\fB\-\-fast\fR
Use a faster zfs list invocation.
.TP
\fB\-n\fR, \fB\-\-dry\-run\fR
Print actions without actually doing anything.
.TP
\fB\-s\fR, \fB\-\-skip\-scrub\fR
Do not snapshot filesystems in scrubbing pools.
.TP
\fB\-h\fR, \fB\-\-help\fR
Print the usage message.
.TP
\fB\-k\fR, \fB\-\-keep\fR=\fINUM\fR
Keep NUM recent snapshots and destroy older snapshots.
.TP
\fB\-l\fR, \fB\-\-label\fR=\fILAB\fR
LAB is usually 'hourly', 'daily', or 'monthly'.
.TP
\fB\-p\fR, \fB\-\-prefix\fR=\fIPRE\fR
PRE is 'zfs\-auto\-snap' by default.
.TP
\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.
.TP
\fB\-\-send\-incr\fR=\fIF\fR
Send zfs incremental backup. Unimplemented.
.TP
\fB\-\-sep\fR=\fICHAR\fR
Use CHAR to separate date stamps in snapshot names.
.TP
\fB\-g\fR, \fB\-\-syslog\fR
Write messages into the system log.
.TP
\fB\-r\fR, \fB\-\-recursive\fR
Snapshot named filesystem and all descendants.
.TP
\fB\-v\fR, \fB\-\-verbose\fR
Print info messages.
.TP
name
Filesystem and volume names, or '//' for all ZFS datasets.
.SH SEE ALSO
.BR zfs (8)

View File

@ -28,8 +28,6 @@ opt_backup_full=''
opt_backup_incremental='' opt_backup_incremental=''
opt_default_exclude='' opt_default_exclude=''
opt_dry_run='' opt_dry_run=''
opt_event='-'
opt_fast_zfs_list=''
opt_keep='' opt_keep=''
opt_label='' opt_label=''
opt_prefix='zfs-auto-snap' opt_prefix='zfs-auto-snap'
@ -40,29 +38,15 @@ opt_syslog=''
opt_skip_scrub='' opt_skip_scrub=''
opt_verbose='' opt_verbose=''
# Global summary statistics.
DESTRUCTION_COUNT='0'
SNAPSHOT_COUNT='0'
WARNING_COUNT='0'
# Other global variables.
SNAPSHOTS_OLD=''
print_usage () print_usage ()
{ {
echo "Usage: $0 [options] [-l label] <'//' | name [name...]> echo "Usage: $0 [options] [-l label] <'//' | name [name...]>
--default-exclude Exclude datasets if com.sun:auto-snapshot is unset. --default-exclude Exclude objects if com.sun:auto-snapshot is unset.
-d, --debug Print debugging messages. -d, --debug Print debugging messages.
-e, --event=EVENT Set the com.sun:auto-snapshot-desc property to EVENT.
--fast Use a faster zfs list invocation.
-n, --dry-run Print actions without actually doing anything. -n, --dry-run Print actions without actually doing anything.
-s, --skip-scrub Do not snapshot filesystems in scrubbing pools. -s, --skip-scrub Do not snapshot filesystems in scrubbing pools.
-h, --help Print this usage message. -h, --help Print this usage message.
-H, --hanoi=INT Use the hanoi rotation scheme.
INT how frequently the hanoi rotation is being run.
It is a number followed by one of w, d, h, m, s for
weeks, days, hours, minutes or seconds.
-k, --keep=NUM Keep NUM recent snapshots and destroy older snapshots. -k, --keep=NUM Keep NUM recent snapshots and destroy older snapshots.
-l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly'. -l, --label=LAB LAB is usually 'hourly', 'daily', or 'monthly'.
-p, --prefix=PRE PRE is 'zfs-auto-snap' by default. -p, --prefix=PRE PRE is 'zfs-auto-snap' by default.
@ -73,7 +57,7 @@ print_usage ()
-g, --syslog Write messages into the system log. -g, --syslog Write messages into the system log.
-r, --recursive Snapshot named filesystem and all descendants. -r, --recursive Snapshot named filesystem and all descendants.
-v, --verbose Print info messages. -v, --verbose Print info messages.
name Filesystem and volume names, or '//' for all ZFS datasets. name Filesystem and volume names, or '//' for all ZFS objects.
" "
} }
@ -124,7 +108,7 @@ print_log () # level, message, ...
} }
do_run () # [argv] do_run ()
{ {
if [ -n "$opt_dry_run" ] if [ -n "$opt_dry_run" ]
then then
@ -133,7 +117,7 @@ do_run () # [argv]
else else
eval $* eval $*
RC="$?" RC="$?"
if [ "$RC" -eq '0' ] if [ "$RC" -eq 0 ]
then then
print_log debug "$*" print_log debug "$*"
else else
@ -143,237 +127,51 @@ do_run () # [argv]
return "$RC" return "$RC"
} }
do_rotate () # flags, oldglob, target
{
local FLAGS="$1"
local GLOB="$2"
local TARGET="$3"
local KEEP=''
# global DESTRUCTION_COUNT
# global WARNING_COUNT
# global SNAPSHOTS_OLD
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$TARGET@$GLOB}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le '0' ]
then
if do_run "zfs destroy $FLAGS '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
}
compute_hanoi_level() # date
{
local DATE="$1"
local EPOCH_TIME=''
local HANOI_NUM=''
local HANOI_LEVEL='1'
# The h* is because on Solaris %H%M will have generated 12h34
EPOCH_TIME=$(date +%s --date="$(echo $DATE | sed 's/-\(..\)h*\(..\)$/ \1:\2/')")
HANOI_NUM=$(($EPOCH_TIME / $opt_hanoi));
while test "$HANOI_NUM" -ne "0"
do
case "${HANOI_NUM}" in
(*[13579])
break
;;
esac
HANOI_LEVEL=$(($HANOI_LEVEL + 1))
HANOI_NUM=$(($HANOI_NUM / 2))
done
echo $HANOI_LEVEL
}
do_hanoi () # flags, oldglob, target
{
local FLAGS="$1"
local GLOB="$2"
local TARGET="$3"
local KEEP=''
local HANOI_LEVEL='0'
local SNAP_DATE=''
local SNAP_LEVEL=''
local POSSIBLY_DESTROY=''
# global DESTRUCTION_COUNT
# global WARNING_COUNT
# global SNAPSHOTS_OLD
HANOI_LEVEL=$(compute_hanoi_level "$DATE")
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$TARGET@$GLOB}" ]
then
# If younger snapshot was stored for possible
# deletion, delete it.
if [ -n "$POSSIBLY_DESTROY" ]
then
if do_run "zfs destroy $FLAGS '$POSSIBLY_DESTROY'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
POSSIBLY_DESTROY=''
fi
SNAP_DATE=$(echo $jj | sed 's/.*-\(....-..-..-..h*..\)$/\1/')
SNAP_LEVEL=$(compute_hanoi_level "$SNAP_DATE")
if test "$HANOI_LEVEL" -eq "$SNAP_LEVEL"
then
# By default the hanoi rotation scheme acts as
# though there are an infinite number of
# disks. So instead of immediately destroying
# this snapshot, remember it as possibly
# needing to be destroyed and only do so if an
# older snapshot is found for this set.
POSSIBLY_DESTROY="$jj"
fi
fi
done
}
do_snapshots () # properties, flags, snapname, oldglob, [targets...]
{
local PROPS="$1"
local FLAGS="$2"
local NAME="$3"
local GLOB="$4"
local TARGETS="$5"
# global SNAPSHOT_COUNT
# global WARNING_COUNT
for ii in $TARGETS
do
if do_run "zfs snapshot $PROPS $FLAGS '$ii@$NAME'"
then
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT + 1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
continue
fi
if test -z "$opt_hanoi"
then
test -z "$opt_keep" && continue
do_rotate "$FLAGS" "$GLOB" "$ii"
else
do_hanoi "$FLAGS" "$GLOB" "$ii"
fi
done
}
# main () # main ()
# { # {
DATE=$(date +%F-%H%M)
GETOPT=$(getopt \ GETOPT=$(getopt \
--longoptions=default-exclude,dry-run,fast,skip-scrub,recursive \ --longoptions=default-exclude,dry-run,skip-scrub,recursive \
--longoptions=event:,hanoi:,keep:,label:,prefix:,sep: \ --longoptions=keep:,label:,prefix:,sep: \
--longoptions=debug,help,quiet,syslog,verbose \ --longoptions=debug,help,quiet,syslog,verbose \
--options=dnshH:e:l:k:p:rs:qgv \ --options=dnshl:k:rs:gv \
-- "$@" ) \ -- "$@" ) \
|| exit 128 || exit 1
eval set -- "$GETOPT" eval set -- "$GETOPT"
while [ "$#" -gt '0' ] while [ "$#" -gt 0 ]
do do
case "$1" in case "$1" in
(-d|--debug) (-d|--debug)
opt_debug='1' opt_debug=1
opt_quiet='' opt_quiet=''
opt_verbose='1' opt_verbose=1
shift 1 shift 1
;; ;;
(--default-exclude) (--default-exclude)
opt_default_exclude='1' opt_default_exclude='1'
shift 1 shift 1
;; ;;
(-e|--event)
if [ "${#2}" -gt '1024' ]
then
print_log error "The $1 parameter must be less than 1025 characters."
exit 139
elif [ "${#2}" -gt '0' ]
then
opt_event="$2"
fi
shift 2
;;
(--fast)
opt_fast_zfs_list='1'
shift 1
;;
(-n|--dry-run) (-n|--dry-run)
opt_dry_run='1' opt_dry_run='1'
shift 1 shift 1
;; ;;
(-s|--skip-scrub) (-s|--skip-scrub)
opt_skip_scrub='1' opt_skip_scrub=1
shift 1 shift 1
;; ;;
(-h|--help) (-h|--help)
print_usage print_usage
exit 0 exit 0
;; ;;
(-H|--hanoi)
HANOI_OPT="$2"
MULT="";
INT="";
case "$2" in
(*[0-9]s) MULT=1 ;;
(*[0-9]m) MULT=60 ;;
(*[0-9]h) MULT=3600 ;;
(*[0-9]d) MULT=86400 ;;
(*[0-9]w) MULT=604800 ;;
(*)
print_log error "Unrecognized interval $2 for the $1 parameter."
exit 139
;;
esac
INT=$(echo $2 | sed 's/.$//')
if ! test "$INT" -gt '0' 2>/dev/null
then
print_log error "The $2 parameter must be a positive integer."
fi
opt_hanoi=$(($INT * $MULT))
shift 2
;;
(-k|--keep) (-k|--keep)
if ! test "$2" -gt '0' 2>/dev/null if ! test "$2" -gt 0 2>/dev/null
then then
print_log error "The $1 parameter must be a positive integer." print_log error "The $1 parameter must be a positive integer."
exit 129 exit 2
fi fi
opt_keep="$2" opt_keep="$2"
shift 2 shift 2
@ -383,19 +181,8 @@ do
shift 2 shift 2
;; ;;
(-p|--prefix) (-p|--prefix)
# @TODO: Parameter validation. See --sep below for the regex.
opt_prefix="$2" opt_prefix="$2"
while test "${#opt_prefix}" -gt '0'
do
case $opt_prefix in
([![:alnum:]_.:\ -]*)
print_log error "The $1 parameter must be alphanumeric."
exit 130
;;
esac
opt_prefix="${opt_prefix#?}"
done
opt_prefix="$2"
shift 2
;; ;;
(-q|--quiet) (-q|--quiet)
opt_debug='' opt_debug=''
@ -404,33 +191,33 @@ do
shift 1 shift 1
;; ;;
(-r|--recursive) (-r|--recursive)
opt_recursive='1' opt_recursive=1
shift 1 shift 1
;; ;;
(--sep) (--sep)
case "$2" in case "$2" in
([[:alnum:]_.:\ -]) ([[:alnum:]_-.:\ ])
: :
;; ;;
('') ('')
print_log error "The $1 parameter must be non-empty." print_log error "The $1 parameter must be non-empty."
exit 131 exit 3
;; ;;
(*) (*)
print_log error "The $1 parameter must be one alphanumeric character." print_log error "The $1 parameter must be one alphanumeric character."
exit 132 exit 4
;; ;;
esac esac
opt_sep="$2" opt_sep="$2"
shift 2 shift 2
;; ;;
(-g|--syslog) (-g|--syslog)
opt_syslog='1' opt_syslog=1
shift 1 shift 1
;; ;;
(-v|--verbose) (-v|--verbose)
opt_quiet='' opt_quiet=''
opt_verbose='1' opt_verbose=1
shift 1 shift 1
;; ;;
(--) (--)
@ -440,10 +227,10 @@ do
esac esac
done done
if [ "$#" -eq '0' ] if [ "$#" -eq 0 ]
then then
print_log error "The filesystem argument list is empty." print_log error "The filesystem argument list is empty."
exit 133 exit 5
fi fi
# Count the number of times '//' appears on the command line. # Count the number of times '//' appears on the command line.
@ -453,15 +240,10 @@ do
test "$ii" = '//' && SLASHIES=$(( $SLASHIES + 1 )) test "$ii" = '//' && SLASHIES=$(( $SLASHIES + 1 ))
done done
if [ "$#" -gt '1' -a "$SLASHIES" -gt '0' ] if [ "$#" -gt 1 -a "$SLASHIES" -gt 0 ]
then then
print_log error "The // must be the only argument if it is given." print_log error "The // must be the only argument if it is given."
exit 134 exit 6
fi
if test -n "$opt_hanoi" -a -z "$opt_label"
then
opt_label=hanoi
fi fi
# These are the only times that `zpool status` or `zfs list` are invoked, so # These are the only times that `zpool status` or `zfs list` are invoked, so
@ -469,20 +251,15 @@ fi
# Solaris implementation. # Solaris implementation.
ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \ ZPOOL_STATUS=$(env LC_ALL=C zpool status 2>&1 ) \
|| { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 135; } || { print_log error "zpool status $?: $ZPOOL_STATUS"; exit 7; }
ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \ ZFS_LIST=$(env LC_ALL=C zfs list -H -t filesystem,volume -s name \
-o name,com.sun:auto-snapshot,com.sun:auto-snapshot:"$opt_label") \ -o name,com.sun:auto-snapshot,com.sun:auto-snapshot:"$opt_label") \
|| { print_log error "zfs list $?: $ZFS_LIST"; exit 136; } || { print_log error "zfs list $?: $ZFS_LIST"; exit 8; }
if [ -n "$opt_fast_zfs_list" ]
then
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -o name -s name|grep $opt_prefix |awk '{ print substr( $0, length($0) - 14, length($0) ) " " $0}' |sort -r -k1,1 -k2,2|awk '{ print substr( $0, 17, length($0) )}') \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; }
else
SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \ SNAPSHOTS_OLD=$(env LC_ALL=C zfs list -H -t snapshot -S creation -o name) \
|| { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 137; } || { print_log error "zfs list $?: $SNAPSHOTS_OLD"; exit 9; }
fi
# Verify that each argument is a filesystem or volume. # Verify that each argument is a filesystem or volume.
for ii in "$@" for ii in "$@"
@ -495,30 +272,30 @@ do
$ZFS_LIST $ZFS_LIST
HERE HERE
print_log error "$ii is not a ZFS filesystem or volume." print_log error "$ii is not a ZFS filesystem or volume."
exit 138 exit 10
done done
# Get a list of pools that are being scrubbed. # Get a list of pools that are being scrubbed.
ZPOOLS_SCRUBBING=$(echo "$ZPOOL_STATUS" | awk -F ': ' \ ZPOOLS_SCRUBBING=$(echo "$ZFS_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \ '$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *scan$/ && $2 ~ /scrub in progress/ { print pool }' \ $1 ~ /^ *scan$/ && $2 ~ /scrub in progress/ { print pool }' \
| sort ) | sort )
# Get a list of pools that cannot do a snapshot. # Get a list of pools that cannot do a snapshot.
ZPOOLS_NOTREADY=$(echo "$ZPOOL_STATUS" | awk -F ': ' \ ZPOOLS_NOTREADY=$(echo "$ZFS_STATUS" | awk -F ': ' \
'$1 ~ /^ *pool$/ { pool = $2 } ; \ '$1 ~ /^ *pool$/ { pool = $2 } ; \
$1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \ $1 ~ /^ *state$/ && $2 !~ /ONLINE|DEGRADED/ { print pool } ' \
| sort) | sort)
# Get a list of datasets for which snapshots are explicitly disabled. # Get a list of objects for which snapshots are explicitly disabled.
NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \ NOAUTO=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /false/ || tolower($3) ~ /false/ {print $1}') 'tolower($2) ~ /false/ || tolower($3) ~ /false/ {print $1}')
# If the --default-exclude flag is set, then exclude all datasets that lack # If the --default-exclude flag is set, then exclude all objects that lack
# an explicit com.sun:auto-snapshot* property. Otherwise, include them. # an explicit com.sun:auto-snapshot* property. Otherwise, include them.
if [ -n "$opt_default_exclude" ] if [ -n "$opt_default_exclude" ]
then then
# Get a list of datasets for which snapshots are explicitly enabled. # Get a list of objects for which snapshots are explicitly enabled.
CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \ CANDIDATES=$(echo "$ZFS_LIST" | awk -F '\t' \
'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}') 'tolower($2) ~ /true/ || tolower($3) ~ /true/ {print $1}')
else else
@ -527,20 +304,20 @@ else
'tolower($2) !~ /false/ && tolower($3) !~ /false/ {print $1}') 'tolower($2) !~ /false/ && tolower($3) !~ /false/ {print $1}')
fi fi
# Initialize the list of datasets that will get a recursive snapshot. # Initialize the list of objects that will get a recursive snapshot.
TARGETS_RECURSIVE='' TARGETS_RECURSIVE=''
# Initialize the list of datasets that will get a non-recursive snapshot. # Initialize the list of objects that will get a non-recursive snapshot.
TARGETS_REGULAR='' TARGETS_REGULAR=''
for ii in $CANDIDATES for ii in $CANDIDATES
do do
# Qualify dataset names so variable globbing works properly. # Qualify object names so variable globbing works properly.
# Suppose ii=tanker/foo and jj=tank sometime during the loop. # Suppose ii=tanker/foo and jj=tank sometime during the loop.
# Just testing "$ii" != ${ii#$jj} would incorrectly match. # Just testing "$ii" != ${ii#$jj} would incorrectly match.
iii="$ii/" iii="$ii/"
# Exclude datasets that are not named on the command line. # Exclude objects that are not named on the command line.
IN_ARGS='0' IN_ARGS='0'
for jj in "$@" for jj in "$@"
do do
@ -554,13 +331,13 @@ do
continue continue
fi fi
# Exclude datasets in pools that cannot do a snapshot. # Exclude objects in pools that cannot do a snapshot.
for jj in $ZPOOLS_NOTREADY for jj in $ZPOOLS_NOTREADY
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether the pool name is a prefix of the dataset name. # Check whether the pool name is a prefix of the object name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log info "Excluding $ii because pool $jj is not ready." print_log info "Excluding $ii because pool $jj is not ready."
@ -568,13 +345,13 @@ do
fi fi
done done
# Exclude datasets in scrubbing pools if the --skip-scrub flag is set. # Exclude objects in scrubbing pools if the --skip-scrub flag is set.
test -n "$opt_skip_scrub" && for jj in $ZPOOLS_SCRUBBING test -z "$opt_skip_scrub" && for jj in $ZPOOLS_SCRUBBING
do do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether the pool name is a prefix of the dataset name. # Check whether the pool name is a prefix of the object name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log info "Excluding $ii because pool $jj is scrubbing." print_log info "Excluding $ii because pool $jj is scrubbing."
@ -587,17 +364,17 @@ do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# The --recursive switch only matters for non-wild arguments. # The --recusive switch only matters for non-wild arguments.
if [ -z "$opt_recursive" -a "$1" != '//' ] if [ -z "$opt_recursive" -a "$1" != '//' ]
then then
# Snapshot this dataset non-recursively. # Snapshot this object non-recursively.
print_log debug "Including $ii for regular snapshot." print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2 continue 2
# Check whether the candidate name is a prefix of any excluded dataset name. # Check whether the candidate name is a prefix of any excluded object name.
elif [ "$jjj" != "${jjj#$iii}" ] elif [ "$jjj" != "${jjj#$iii}" ]
then then
# Snapshot this dataset non-recursively. # Snapshot this object non-recursively.
print_log debug "Including $ii for regular snapshot." print_log debug "Including $ii for regular snapshot."
TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t TARGETS_REGULAR="${TARGETS_REGULAR:+$TARGETS_REGULAR }$ii" # nb: \t
continue 2 continue 2
@ -609,7 +386,7 @@ do
# Ibid regarding iii. # Ibid regarding iii.
jjj="$jj/" jjj="$jj/"
# Check whether any included dataset is a prefix of the candidate name. # Check whether any included object is a prefix of the candidate name.
if [ "$iii" != "${iii#$jjj}" ] if [ "$iii" != "${iii#$jjj}" ]
then then
print_log debug "Excluding $ii because $jj includes it recursively." print_log debug "Excluding $ii because $jj includes it recursively."
@ -628,24 +405,20 @@ do
TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t TARGETS_RECURSIVE="${TARGETS_RECURSIVE:+$TARGETS_RECURSIVE }$ii" # nb: \t
done done
# ISO style date; fifteen characters: YYYY-MM-DD-HHMM # Summary statistics.
# On Solaris %H%M expands to 12h34. DESTRUCTION_COUNT='0'
DATE=$(date --utc +%F-%H%M) SNAPSHOT_COUNT='0'
WARNING_COUNT='0'
if test -n "$opt_hanoi" -a "$opt_event" = "-" # Linux lacks SMF and the notion of an FMRI event.
then FMRI_EVENT='-'
opt_event=hanoi-$HANOI_OPT-level-$(compute_hanoi_level $DATE)
fi
# Linux lacks SMF and the notion of an FMRI event, but always set this property # Create the snapshot using these arguments.
# because the SUNW program does. The dash character is the default. SNAPSHOT_PROPERTIES="-o com.sun:auto-snapshot-desc='$FMRI_EVENT'"
SNAPPROP="-o com.sun:auto-snapshot-desc='$opt_event'" SNAPSHOT_NAME="$opt_prefix${opt_label:+$opt_sep$opt_label-$DATE}"
# The snapshot name after the @ symbol. # The expression for old snapshots. -YYYY-MM-DD-HHMM
SNAPNAME="$opt_prefix${opt_label:+$opt_sep$opt_label}-$DATE" SNAPSHOT_MATCH="$opt_prefix${opt_label:+?$opt_label}????????????????"
# The expression for matching old snapshots. -YYYY-MM-DD-HHMM
SNAPGLOB="$opt_prefix${opt_label:+?$opt_label}????????????????"
test -n "$TARGETS_REGULAR" \ test -n "$TARGETS_REGULAR" \
&& print_log info "Doing regular snapshots of $TARGETS_REGULAR" && print_log info "Doing regular snapshots of $TARGETS_REGULAR"
@ -656,13 +429,83 @@ test -n "$TARGETS_RECURSIVE" \
test -n "$opt_dry_run" \ test -n "$opt_dry_run" \
&& print_log info "Doing a dry run. Not running these commands..." && print_log info "Doing a dry run. Not running these commands..."
do_snapshots "$SNAPPROP" "" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_REGULAR"
do_snapshots "$SNAPPROP" "-r" "$SNAPNAME" "$SNAPGLOB" "$TARGETS_RECURSIVE"
print_log notice "@$SNAPNAME," \ for ii in $TARGETS_REGULAR
"$SNAPSHOT_COUNT created," \ do
"$DESTRUCTION_COUNT destroyed," \ if do_run "zfs snapshot $SNAPSHOT_PROPERTIES '$ii@$SNAPSHOT_NAME'"
"$WARNING_COUNT warnings." then
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT +1 ))
continue
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
if [ -z "$opt_keep" ]
then
continue
fi
KEEP="$opt_keep"
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$SNAPSHOT_MATCH}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le 0 ]
then
if do_run "zfs destroy '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
for ii in $TARGETS_RECURSIVE
do
if do_run "zfs snapshot $SNAPSHOT_PROPERTIES -r '$ii@$SNAPSHOT_NAME'"
then
SNAPSHOT_COUNT=$(( $SNAPSHOT_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT +1 ))
continue
fi
# Retain at most $opt_keep number of old snapshots of this filesystem,
# including the one that was just recently created.
if [ -z "$opt_keep" ]
then
continue
fi
KEEP="$opt_keep"
# ASSERT: The old snapshot list is sorted by increasing age.
for jj in $SNAPSHOTS_OLD
do
# Check whether this is an old snapshot of the filesystem.
if [ -z "${jj#$ii@$SNAPSHOT_MATCH}" ]
then
KEEP=$(( $KEEP - 1 ))
if [ "$KEEP" -le 0 ]
then
if do_run "zfs destroy -r '$jj'"
then
DESTRUCTION_COUNT=$(( $DESTRUCTION_COUNT +1 ))
else
WARNING_COUNT=$(( $WARNING_COUNT + 1 ))
fi
fi
fi
done
done
print_log notice "@$SNAPSHOT_NAME, \
$SNAPSHOT_COUNT created, $DESTRUCTION_COUNT destroyed, $WARNING_COUNT warnings."
exit 0
# } # }