Skip to content

Instantly share code, notes, and snippets.

@mshroyer
Last active December 14, 2015 07:39
Show Gist options
  • Select an option

  • Save mshroyer/5052559 to your computer and use it in GitHub Desktop.

Select an option

Save mshroyer/5052559 to your computer and use it in GitHub Desktop.
Utility script for maintaining rolling snapshots of ZFS datasets on FreeBSD 9.1.
#!/bin/sh
# snapzfs.sh - Maintain rotating ZFS dataset snapshots
#
# Creates, rotates, and prunes rolling snapshots of ZFS datasets. When
# this script is run, the sets defined in the configuration section below
# are snapshot and rotated as necessary depending on the age and number of
# existing snapshots. Written for and tested on FreeBSD 9.1.
#
# Mark Shroyer
# Wed Feb 27 10:39:33 EST 2013
### BEGIN CONFIGURATION ###################################################
## Snapshot set definitions
snapshot_sets='swteam'
# Software team document share
swteam_pool='data'
swteam_dataset='swteam'
swteam_mount='/data/swteam'
swteam_keep_days=7
swteam_keep_weeks=4
swteam_keep_months=6
### END CONFIGURATION #####################################################
alias zfs=/sbin/zfs
get_mtime()
{
stat -f %Sm -t $1 $2
}
snapshot_name()
{
local class="$1"
local number="$2"
printf "%s.%03d" "$class" "$number"
}
create_snapshot()
{
local pool="$1"
local dataset="$2"
local mount="$3"
local class="$4"
local retention="$5"
echo "=> Deleting expired ${class} snapshots..."
local index_max=$(( $retention - 1))
local index_cur=$index_max
local snapshot_cur=`snapshot_name ${class} ${index_cur}`
while [ -e "${mount}/.zfs/snapshot/${snapshot_cur}" ]; do
echo "${mount}/${snapshot_cur}"
zfs destroy "${pool}/${dataset}@${snapshot_cur}"
index_cur=$(( $index_cur + 1 ))
snapshot_cur=`snapshot_name ${class} ${index_cur}`
done
echo "=> Rotating ${class} snapshots..."
local index_new=$index_max
local index_old=
while [ $index_new -gt 0 ]; do
index_old=$(( $index_new - 1))
local name_old=`snapshot_name ${class} ${index_old}`
local name_new=`snapshot_name ${class} ${index_new}`
if [ -e "${mount}/.zfs/snapshot/${name_old}" ]; then
zfs rename "${pool}/${dataset}@${name_old}" \
"${pool}/${dataset}@${name_new}"
fi
index_new=$index_old
done
echo "=> Creating new ${class} snapshot..."
local snapshot_first=`snapshot_name ${class} 0`
zfs snapshot "${pool}/${dataset}@${snapshot_first}"
}
check_snapshot()
{
local pool="$1"
local dataset="$2"
local mount="$3"
local class="$4"
local retention="$5"
local format="$6"
if [ -z "$retention" ] || [ "$retention" -eq 0 ]; then
# Don't need a snapshot
return 1
fi
local snapshot_path="${mount}/.zfs/snapshot/`snapshot_name ${class} 0`"
if [ ! -e "${snapshot_path}" ]; then
# Time for a new snapshot
return 0
fi
local date_current=`date +${format}`
local date_snapshot=`get_mtime ${format} ${snapshot_path}`
if [ $date_current -gt $date_snapshot ]; then
# Time for a new snapshot
return 0
else
# No new snapshot in this class required
return 1
fi
}
for set_name in `echo $snapshot_sets`; do
set_pool=`eval "echo \\\$\${set_name}_pool"`
set_dataset=`eval "echo \\\$\${set_name}_dataset"`
set_mount=`eval "echo \\\$\${set_name}_mount"`
set_keep_days=`eval "echo \\\$\${set_name}_keep_days"`
set_keep_weeks=`eval "echo \\\$\${set_name}_keep_weeks"`
set_keep_months=`eval "echo \\\$\${set_name}_keep_months"`
# So that future date comparisons against the snapshot's mtime will
# work as expected...
touch "${set_mount}"
check_snapshot "$set_pool" "$set_dataset" "$set_mount" \
daily "$set_keep_days" %Y%j
if [ $? -eq 0 ]; then
create_snapshot "$set_pool" "$set_dataset" "$set_mount" \
daily "$set_keep_days"
fi
check_snapshot "$set_pool" "$set_dataset" "$set_mount" \
weekly "$set_keep_weeks" %Y%U
if [ $? -eq 0 ]; then
create_snapshot "$set_pool" "$set_dataset" "$set_mount" \
weekly "$set_keep_weeks"
fi
check_snapshot "$set_pool" "$set_dataset" "$set_mount" \
monthly "$set_keep_months" %Y%m
if [ $? -eq 0 ]; then
create_snapshot "$set_pool" "$set_dataset" "$set_mount" \
monthly "$set_keep_months"
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment