Initial commit
This commit is contained in:
commit
f0e745a5f5
|
@ -0,0 +1,88 @@
|
|||
ZFS Prune Snapshots
|
||||
===================
|
||||
|
||||
Remove snapshots from one or more zpools that match given criteria
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Remove snapshots older than a week across all zpools
|
||||
|
||||
zfs-prune-snapshots 1w
|
||||
|
||||
Same as above, but with increased verbosity and without
|
||||
actually deleting any snapshots (dry-run)
|
||||
|
||||
zfs-prune-snapshots -vn 1w
|
||||
|
||||
Remove snapshots older than 3 weeks on tank1 and tank2/backup.
|
||||
Note that this script will recurse through *all* of tank1 and
|
||||
*all* datasets below tank2/backup
|
||||
|
||||
zfs-prune-snapshots 3w tank1 tank2/backup
|
||||
|
||||
Remove snapshots older than a month on the zones pool that start
|
||||
with the string "autosnap_"
|
||||
|
||||
zfs-prune-snapshots 1M -p 'autosnap_' zones
|
||||
|
||||
Timespec
|
||||
--------
|
||||
|
||||
The first argument denotes how old a snapshot must be for it to
|
||||
be considered for deletion - possible specifiers are
|
||||
|
||||
- `s` seconds
|
||||
- `m` minutes
|
||||
- `h` hours
|
||||
- `d` days
|
||||
- `w` weeks
|
||||
- `M` months
|
||||
- `y` years
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
usage: zfs-prune-snapshots [-hnv] [-p <prefix] <time> [[dataset1] ...]
|
||||
|
||||
remove snapshots from one or more zpools that match given criteria
|
||||
|
||||
examples
|
||||
# zfs-prune-snapshots 1w
|
||||
remove snapshots older than a week across all zpools
|
||||
|
||||
# zfs-prune-snapshots -vn 1w
|
||||
same as above, but with increased verbosity and without
|
||||
actually deleting any snapshots (dry-run)
|
||||
|
||||
# zfs-prune-snapshots 3w tank1 tank2/backup
|
||||
remove snapshots older than 3 weeks on tank1 and tank2/backup.
|
||||
note that this script will recurse through *all* of tank1 and
|
||||
*all* datasets below tank2/backup
|
||||
|
||||
# zfs-prune-snapshots 1M -p 'autosnap_' zones
|
||||
remove snapshots older than a month on the zones pool that start
|
||||
with the string "autosnap_"
|
||||
|
||||
timespec
|
||||
the first argument denotes how old a snapshot must be for it to
|
||||
be considered for deletion - possible specifiers are
|
||||
|
||||
s seconds
|
||||
m minutes
|
||||
h hours
|
||||
d days
|
||||
w weeks
|
||||
M months
|
||||
y years
|
||||
|
||||
options
|
||||
-h print this message and exit
|
||||
-n dry-run, don't actually delete snapshots
|
||||
-p <prefix> snapshot prefix string to match
|
||||
-v increase verbosity
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
MIT License
|
|
@ -0,0 +1,162 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# script to prune zfs snapshots over a given age
|
||||
#
|
||||
# Author: Dave Eddy <dave@daveeddy.com>
|
||||
# Date: November 20, 2015
|
||||
# License: MIT
|
||||
|
||||
usage() {
|
||||
local prog=${0##*/}
|
||||
cat <<-EOF
|
||||
usage: zfs-prune-snapshots [-hnv] [-p <prefix] <time> [[dataset1] ...]
|
||||
|
||||
remove snapshots from one or more zpools that match given criteria
|
||||
|
||||
examples
|
||||
# zfs-prune-snapshots 1w
|
||||
remove snapshots older than a week across all zpools
|
||||
|
||||
# zfs-prune-snapshots -vn 1w
|
||||
same as above, but with increased verbosity and without
|
||||
actually deleting any snapshots (dry-run)
|
||||
|
||||
# zfs-prune-snapshots 3w tank1 tank2/backup
|
||||
remove snapshots older than 3 weeks on tank1 and tank2/backup.
|
||||
note that this script will recurse through *all* of tank1 and
|
||||
*all* datasets below tank2/backup
|
||||
|
||||
# zfs-prune-snapshots 1M -p 'autosnap_' zones
|
||||
remove snapshots older than a month on the zones pool that start
|
||||
with the string "autosnap_"
|
||||
|
||||
timespec
|
||||
the first argument denotes how old a snapshot must be for it to
|
||||
be considered for deletion - possible specifiers are
|
||||
|
||||
s seconds
|
||||
m minutes
|
||||
h hours
|
||||
d days
|
||||
w weeks
|
||||
M months
|
||||
y years
|
||||
|
||||
options
|
||||
-h print this message and exit
|
||||
-n dry-run, don't actually delete snapshots
|
||||
-p <prefix> snapshot prefix string to match
|
||||
-v increase verbosity
|
||||
EOF
|
||||
}
|
||||
|
||||
debug() {
|
||||
((verbosity >= 1)) && echo "$@"
|
||||
return 0
|
||||
}
|
||||
|
||||
# given a time in seconds, return the "human readable" string
|
||||
human() {
|
||||
local seconds=$1
|
||||
if ((seconds < 0)); then
|
||||
((seconds *= -1))
|
||||
fi
|
||||
|
||||
local times=(
|
||||
$((seconds / 60 / 60 / 24 / 365)) # years
|
||||
$((seconds / 60 / 60 / 24 / 30)) # months
|
||||
$((seconds / 60 / 60 / 24 / 7)) # weeks
|
||||
$((seconds / 60 / 60 / 24)) # days
|
||||
$((seconds / 60 / 60)) # hours
|
||||
$((seconds / 60)) # minutes
|
||||
$((seconds)) # seconds
|
||||
)
|
||||
local names=(year month week day hour minute second)
|
||||
|
||||
local i
|
||||
for ((i = 0; i < ${#names[@]}; i++)); do
|
||||
if ((${times[$i]} > 1)); then
|
||||
echo "${times[$i]} ${names[$i]}s"
|
||||
return
|
||||
elif ((${times[$i]} == 1)); then
|
||||
echo "${times[$i]} ${names[$i]}"
|
||||
return
|
||||
fi
|
||||
done
|
||||
echo '0 seconds'
|
||||
}
|
||||
|
||||
dryrun=false
|
||||
verbosity=0
|
||||
prefix=
|
||||
while getopts 'hnp:v' option; do
|
||||
case "$option" in
|
||||
h) usage; exit 0;;
|
||||
n) dryrun=true;;
|
||||
p) prefix=$OPTARG;;
|
||||
v) ((verbosity++));;
|
||||
*) usage; exit 1;;
|
||||
esac
|
||||
done
|
||||
shift "$((OPTIND - 1))"
|
||||
|
||||
# extract the first argument - the timespec - and
|
||||
# convert it to seconds
|
||||
t=$1
|
||||
time_re='^([0-9]+)([smhdwMy])$'
|
||||
seconds=
|
||||
if [[ $t =~ $time_re ]]; then
|
||||
# ex: "21d" becomes num=21 spec=d
|
||||
num=${BASH_REMATCH[1]}
|
||||
spec=${BASH_REMATCH[2]}
|
||||
|
||||
case "$spec" in
|
||||
s) seconds=$((num));;
|
||||
m) seconds=$((num * 60));;
|
||||
h) seconds=$((num * 60 * 60));;
|
||||
d) seconds=$((num * 60 * 60 * 24));;
|
||||
w) seconds=$((num * 60 * 60 * 24 * 7));;
|
||||
M) seconds=$((num * 60 * 60 * 24 * 30));;
|
||||
y) seconds=$((num * 60 * 60 * 24 * 365));;
|
||||
*) echo "error: unknown spec '$spec'" >&2; exit 1;;
|
||||
esac
|
||||
elif [[ -z $t ]]; then
|
||||
echo 'error: timespec must be specified as the first argument' >&2
|
||||
exit 1
|
||||
else
|
||||
echo "error: failed to parse timespec '$t'" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shift
|
||||
pools=("$@")
|
||||
|
||||
now=$(date +%s)
|
||||
cutoff=$((now - seconds))
|
||||
code=0
|
||||
while read -r snapshot creation; do
|
||||
# ensure optional prefix matches
|
||||
snapname=${snapshot#*@}
|
||||
if [[ -n $prefix && $prefix != ${snapname:0:${#prefix}} ]]; then
|
||||
debug "skipping $snapshot: doesn't match prefix $prefix"
|
||||
continue
|
||||
fi
|
||||
|
||||
# ensure snapshot is older than the cutoff time
|
||||
delta=$((now - creation))
|
||||
human=$(human "$delta")
|
||||
if ((delta <= seconds)); then
|
||||
debug "skipping $snapshot: $human old"
|
||||
continue
|
||||
fi
|
||||
|
||||
# remove the snapshot
|
||||
echo -n "removing $snapshot: $human old"
|
||||
if $dryrun; then
|
||||
echo ' <dry-run: no action taken>'
|
||||
else
|
||||
echo
|
||||
zfs destroy "$snapshot" || code=1
|
||||
fi
|
||||
done < <(zfs list -Hpo name,creation -t snapshot -r "${pools[@]}")
|
||||
exit "$code"
|
Loading…
Reference in New Issue