Version 0.6

This commit is contained in:
Tim Foster 2008-06-29 18:33:28 +01:00
parent 5759424d8e
commit acca7a30a3
6 changed files with 507 additions and 81 deletions

View File

@ -1,14 +1,30 @@
ZFS Automatic Snapshot SMF Service, version 0.5
Introduction NAME
-----------
ZFS Automatic Snapshot SMF Service, version 0.6
DESCRIPTION
This is a *prototype* of a simple SMF service which you can configure to This is a *prototype* of a simple SMF service which you can configure to
take automatic, scheduled snapshots of any given ZFS filesystem. take automatic, scheduled snapshots of any given ZFS filesystem as well
as perform simple incremental or full backups of that filesystem.
To use the service, the user must install the method script, import the default
instance, and then create instances for each ZFS filesystem that should be
managed by the service.
Documentation for the service instance is contained in the manifest file,
zfs-auto-snapshot.xml.
We also bundle a simple GUI application, which will query the user for the
properties required, and will proceed to build an instance manifest. This
GUI is documented as part of the installation instructions below.
Usage Instructions
------------------ INSTALLATION
To install, as root, run the following commands: To install, as root, run the following commands:
@ -19,24 +35,42 @@ Once you have installed these, you need to create an instance of the service
for each set of ZFS snapshots you want to take. The properties we need are: for each set of ZFS snapshots you want to take. The properties we need are:
zfs/fs-name The name of the filesystem zfs/fs-name The name of the filesystem
zfs/interval [ hours | days | months ] zfs/interval [ hours | days | months ]
zfs/keep How many snapshots to retain. "all" keeps all snapshots. zfs/keep How many snapshots to retain. "all" keeps all snapshots.
zfs/period How often you want to take snapshots zfs/period How often you want to take snapshots
(eg. every 10 days) (eg. every 10 days)
zfs/snapshot-children "true" if you would like to recursively take snapshots zfs/snapshot-children "true" if you would like to recursively take snapshots
of all child filesystems of the specified fs-name. of all child filesystems of the specified fs-name.
zfs/backup [ full | incremental | none ]
An example instance manifest is included in this archive, and the default zfs/backup-save-cmd The command string used to save the backup stream.
instance (which should be disabled) is also documented.
zfs/backup-lock You shouldn't need to change this - but it should be
set to "unlocked" by default. We use it to indicate when
a backup is running.
zfs/label A label that can be used to differentiate this set of
backups from others, not required.
An example instance manifest is included in this archive.
The script "zfs-auto-snapshot-admin.sh" is a simple shell wrapper which uses The script "zfs-auto-snapshot-admin.sh" is a simple shell wrapper which uses
zenity, a scriptable GUI framework in GNOME, to write a service manifest zenity, a scriptable GUI framework in GNOME, to write a service manifest
based on user input. based on user input.
# ./zfs-auto-snapshot-admin.sh # ./zfs-auto-snapshot-admin.sh
Usage: zfs-auto-snapshot-admin.sh [zfs filesystem name] Usage: zfs-auto-snapshot-admin.sh [zfs filesystem name]
EXAMPLES
The following shows me running it for the ZFS filesystem The following shows me running it for the ZFS filesystem
"tank/root_filesystem". "tank/root_filesystem".
@ -52,11 +86,17 @@ then issue the command :
You can see what work will be done by checking your crontab. You can see what work will be done by checking your crontab.
SEE ALSO
More background about this service, along with implementation comments can be
found in web log posts at:
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_prototype_1
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_smf_service
http://blogs.sun.com/timf/entry/and_also_for_s10u2_zfs
http://blogs.sun.com/timf/entry/smf_philosophy_more_on_zfs
http://blogs.sun.com/timf/entry/zfs_automatic_snapshots_now_with
The ZFS Automatic Snapshot SMF Service is released under the terms of the CDDL. The ZFS Automatic Snapshot SMF Service is released under the terms of the CDDL.
More background detail about this service can be found in blog posts at:
http://blogs.sun.com/roller/page/timf?entry=zfs_automatic_snapshots_prototype_1
http://blogs.sun.com/roller/page/timf?entry=zfs_automatic_snapshots_smf_service
http://blogs.sun.com/roller/page/timf?entry=and_also_for_s10u2_zfs

View File

@ -0,0 +1,60 @@
<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
<service_bundle type='manifest' name='space-timf,10mins'>
<service
name='system/filesystem/zfs/auto-snapshot'
type='service'
version='0.6'>
<create_default_instance enabled='false' />
<instance name='space-timf,10mins' enabled='false' >
<exec_method
type='method'
name='start'
exec='/lib/svc/method/zfs-auto-snapshot start'
timeout_seconds='10' />
<exec_method
type='method'
name='stop'
exec='/lib/svc/method/zfs-auto-snapshot stop'
timeout_seconds='10' />
<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />
</property_group>
<!-- properties for zfs automatic snapshots -->
<property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="space/timf"
override="true"/>
<propval name="interval" type="astring" value="minutes"
override="true"/>
<propval name="period" type="astring" value="10"
override="true"/>
<propval name="offset" type="astring" value="0"
override="true"/>
<propval name="keep" type="astring" value="12"
override="true"/>
<propval name="snapshot-children" type="boolean" value="true"
override="true"/>
<propval name="backup" type="astring" value="none"
override="true"/>
<propval name="backup-save-cmd" type="astring" value=""
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="10mins"
override="true"/>
</property_group>
</instance>
<stability value='Unstable' />
</service>
</service_bundle>

View File

@ -32,9 +32,10 @@
# all child datasets. More documentation available at # all child datasets. More documentation available at
# http://blogs.sun.com/timf # http://blogs.sun.com/timf
# #
# The service will move itself into maintenance if it's unable to take a snapshot, # The service will move itself into maintenance if it's unable to take a
# destroy a snapshot as per the snapshot retention policy, or is unable to # snapshot, destroy a snapshot as per the snapshot retention policy, unable to
# create or update the cron job. # zfs send a dataset (if configured) or is unable to create or update the cron
# job.
# #
@ -61,15 +62,28 @@ result=$SMF_EXIT_OK
# $1 is assumed to be a valid FMRI # $1 is assumed to be a valid FMRI
function schedule_snapshots { function schedule_snapshots {
FMRI=$1 typeset FMRI=$1
# FIXME need work in here to actually validate the FMRI props # FIXME need work in here to actually validate the FMRI props
FILESYS=$(svcprop -p zfs/fs-name $FMRI) typeset FILESYS=$(svcprop -p zfs/fs-name $FMRI)
INTERVAL=$(svcprop -p zfs/interval $FMRI) typeset INTERVAL=$(svcprop -p zfs/interval $FMRI)
PERIOD=$(svcprop -p zfs/period $FMRI) typeset PERIOD=$(svcprop -p zfs/period $FMRI)
OFFSET=$(svcprop -p zfs/offset $FMRI) typeset OFFSET=$(svcprop -p zfs/offset $FMRI)
typeset STATE=0
typeset BACKUP=$(svcprop -p zfs/backup $FMRI)
typeset BACKUP_SAVE_CMD=$(svcprop -p zfs/backup $FMRI)
case $BACKUP in
'full' | 'incremental' )
if [ -z "${BACKUP_SAVE_CMD}" ]
then
check_failure 1 "Backup requested, but no backup command specified."
fi
;;
esac
# for now, we're forcing the offset to be 0 seconds. # for now, we're forcing the offset to be 0 seconds.
OFFSET=0 typeset OFFSET=0
# validate the filesystem # validate the filesystem
zfs list $FILESYS 2>&1 1> /dev/null zfs list $FILESYS 2>&1 1> /dev/null
@ -105,7 +119,12 @@ function schedule_snapshots {
# just live with this. # just live with this.
# #
function add_cron_job { # $INTERVAL $PERIOD $OFFSET $FMRI function add_cron_job { # $INTERVAL $PERIOD $OFFSET $FMRI
typeset INTERVAL=$1
typeset PERIOD=$2
typeset OFFSET=$3
typeset FMRI=$4
case $INTERVAL in case $INTERVAL in
'minutes') 'minutes')
TIMES=$(get_divisor 0 59 $PERIOD) TIMES=$(get_divisor 0 59 $PERIOD)
@ -145,7 +164,8 @@ function add_cron_job { # $INTERVAL $PERIOD $OFFSET $FMRI
# $1 is assumed to be a valid FMRI # $1 is assumed to be a valid FMRI
function unschedule_snapshots { function unschedule_snapshots {
FMRI=$1 typeset FMRI=$1
crontab -l | grep -v "/lib/svc/method/zfs-auto-snapshot $FMRI$" > /tmp/saved-crontab.$$ crontab -l | grep -v "/lib/svc/method/zfs-auto-snapshot $FMRI$" > /tmp/saved-crontab.$$
crontab /tmp/saved-crontab.$$ crontab /tmp/saved-crontab.$$
check_failure $? "Unable to unschedule snapshots for $FMRI" check_failure $? "Unable to unschedule snapshots for $FMRI"
@ -167,30 +187,74 @@ function unschedule_snapshots {
# $1 is assumed to be a valid FMRI # $1 is assumed to be a valid FMRI
function take_snapshot { function take_snapshot {
FMRI=$1 typeset FMRI=$1
DATE=$(date +%F-%H:%M:%S) typeset DATE=$(date +%F-%H:%M:%S)
SNAPNAME="zfs-auto-snap-${DATE}" typeset FILESYS=$(svcprop -p zfs/fs-name $FMRI)
FILESYS=$(svcprop -p zfs/fs-name $FMRI) typeset KEEP=$(svcprop -p zfs/keep $FMRI)
KEEP=$(svcprop -p zfs/keep $FMRI) typeset SNAP_CHILDREN=$(svcprop -p zfs/snapshot-children $FMRI)
SNAP_CHILDREN=$(svcprop -p zfs/snapshot-children $FMRI)
typeset BACKUP=$(svcprop -p zfs/backup $FMRI)
typeset STATE=0
# an identifier allows us to setup multiple snapshot schedules
# per filesystem - so we append a :<id> token if the user has
# requested one, which then gets used in the SNAPNAME. SMF
# returns the value '""' for the empty string to differentiate
# between an unset property, and a set-but-empty property.
# Shocking, I know.
typeset LABEL="$(svcprop -p zfs/label $FMRI)"
if [ "$LABEL" != "\"\"" ]
then
LABEL=":${LABEL}"
else
LABEL=""
fi
typeset SNAPNAME="zfs-auto-snap${LABEL}-${DATE}"
# Ok, now say cheese! It'd be nice if the child snapshotting was
# atomic, but we don't yet have that in zfs. # Ok, now say cheese! If we're taking recursive snapshots,
# walk through the children, destroying old ones if required.
if [ "${SNAP_CHILDREN}" == "true" ] if [ "${SNAP_CHILDREN}" == "true" ]
then then
for child in $(zfs list -r -H -o name -t filesystem $FILESYS)
OS=$(uname -r)
for child in $(zfs list -r -H -o name -t filesystem,volume $FILESYS)
do do
destroy_older_snapshots $child $KEEP destroy_older_snapshots $child $KEEP $LABEL
zfs snapshot $child@$SNAPNAME if [ "${OS}" != "5.11" ]
check_failure $? "Unable to take snapshot $child@$SNAPNAME." then
# Solaris 10 doesn't have recursive snapshots, but we do
# them outside the loop otherwise.
zfs snapshot $child@$SNAPNAME
check_failure $? "Unable to take snapshot $child@$SNAPNAME."
fi
done done
# take the recursive snapshots if we're on Solaris Nevada.
if [ "${OS}" == "5.11" ]
then
zfs snapshot -r $FILESYS@$SNAPNAME
check_failure $? "Unable to take recursive snapshots $FILESYS@$SNAPNAME."
fi
else else
destroy_older_snapshots $FILESYS $KEEP destroy_older_snapshots $FILESYS $KEEP
zfs snapshot $FILESYS@$SNAPNAME zfs snapshot $FILESYS@$SNAPNAME
check_failure $? "Unable to take snapshot $FILESYS@$SNAPNAME." check_failure $? "Unable to take snapshot $FILESYS@$SNAPNAME."
fi fi
# If the user has asked for backups, go ahead and do this.
if [ "${BACKUP}" != "none" ]
then
take_backup $FILESYS $BACKUP "$LABEL" $FMRI
check_failure $? "Unable to backup filesystem $FILESYS using \
$BACKUP backup strategy."
fi
# finally, check our status before we return # finally, check our status before we return
STATE=$(svcprop -p restarter/state $FMRI) STATE=$(svcprop -p restarter/state $FMRI)
if [ "${STATE}" == "maintenance" ] if [ "${STATE}" == "maintenance" ]
@ -202,16 +266,18 @@ function take_snapshot {
return $STATE return $STATE
} }
# Given a filesystem name, and a limit of the number of snapshots we want # Given a filesystem name, and a limit of the number of snapshots we want,
# along with the identifier for this set of snapshots,
# we destroy all older snapshots of this filesystem whose names begin # we destroy all older snapshots of this filesystem whose names begin
# with the text "zfs-auto-snap". Note that here we destroy one more snapshot # with the text "zfs-auto-snap<id>". Note that here we destroy one more snapshot
# than the "keep" threshold - this is because in the context of calling this # than the "keep" threshold - this is because in the context of calling this
# function, we're already creating one new auto-snapshot. # function, we're already creating one new auto-snapshot.
# #
function destroy_older_snapshots { function destroy_older_snapshots {
FILESYS=$1 typeset FILESYS=$1
COUNTER=$2 typeset COUNTER=$2
typeset LABEL=$3
if [ "${COUNTER}" == "all" ] if [ "${COUNTER}" == "all" ]
then then
@ -222,7 +288,7 @@ function destroy_older_snapshots {
# walk through the snapshots, newest first, destroying older ones # walk through the snapshots, newest first, destroying older ones
for snapshot in $(zfs list -r -t snapshot -H -o name $FILESYS \ for snapshot in $(zfs list -r -t snapshot -H -o name $FILESYS \
| grep $FILESYS@zfs-auto-snap | sort -r) | grep "$FILESYS@zfs-auto-snap${LABEL}" | sort -r)
do do
if [ $COUNTER -le 0 ] if [ $COUNTER -le 0 ]
then then
@ -237,14 +303,15 @@ function destroy_older_snapshots {
} }
# Given the exit status of a command, an integer, 0 if the command completed # Given the exit status of a command, an integer, 0 if the command completed
# without errors, if the command exited with errors, then we degrade the # without errors. If the command exited with errors, we degrade the
# state of this service into maintenance mode. We also log an error message # state of this service into maintenance mode. We also log an error message
# as passed into this function. # as passed into this function.
# #
function check_failure { # integer exit status, error message to display function check_failure { # integer exit status, error message to display
RESULT=$1 typeset RESULT=$1
ERR_MSG=$2 typeset ERR_MSG=$2
if [ $RESULT -ne 0 ] if [ $RESULT -ne 0 ]
then then
echo "Error: $ERR_MSG" echo "Error: $ERR_MSG"
@ -261,11 +328,11 @@ function check_failure { # integer exit status, error message to display
# #
function get_divisor { # start period, end period, width of period function get_divisor { # start period, end period, width of period
START=$1 typeset START=$1
END=$2 typeset END=$2
WIDTH=$3 typeset WIDTH=$3
RANGE=$START typeset RANGE=$START
JUMP=$(( $RANGE + $WIDTH )) typeset JUMP=$(( $RANGE + $WIDTH ))
while [ $JUMP -lt $END ] while [ $JUMP -lt $END ]
do do
@ -277,6 +344,114 @@ function get_divisor { # start period, end period, width of period
} }
# Given a filesytem name, and a backup type (currently "complete" or
# "incremental") along with an FMRI, we backup the filesystem - either
# from the latest snapshot that was taken, or by an incremental backup.
# Properties in the FMRI tell us what to do with the backup stream
#
function take_backup { # filesystem backup-type label fmri
typeset FILESYS=$1
typeset BACKUP=$2
typeset LABEL=$3
typeset FMRI=$4
# obtain lock from fmri
typeset LOCK=$(svcprop -p zfs/backup-lock $FMRI)
if [ "$LOCK" != "unlocked" ]
then
# Unable to perform this backup due to an existing backup being
# executed for this dataset. This would result in moving the
# service to maintenance mode if we're doing incrementals, but
# it's not so serious for full backups.
echo "Unable to backup $FILESYS: $LOCK."
if [ "$BACKUP" == "incremental" ]
then
echo "A lock prevented us from performing an incremental backup."
return 1
else
echo "Full backup not completed for $FMRI on $(date)."
return 0
fi
else
# set our lock. (this isn't atomic, unfortunately :-( )
svccfg -s $FMRI setprop zfs/backup-lock = astring: \
"\"$BACKUP backup in progress by PID $$\""
svcadm refresh $FMRI
fi
typeset BACKUP_SAVE_CMD=$(svcprop -p zfs/backup-save-cmd $FMRI \
| sed -e 's/\\//g')
typeset SNAP_CHILDREN=$(svcprop -p zfs/snapshot-children $FMRI)
typeset BACKUP_DATASETS=""
# Determine how many datasets we have to backup
if [ "$SNAP_CHILDREN" == "true" ]
then
BACKUP_DATASETS=$(zfs list -r -H -o name -t filesystem,volume $FILESYS)
else
# only one dataset to worry about here.
BACKUP_DATASETS=$FILESYS
fi
# loop through the datasets, backing up each one.
for dataset in $BACKUP_DATASETS
do
# An initial check of the input parameters, to see how we should proceed
case $BACKUP in
"incremental")
# get the last two snapshots
LAST_SNAP=$(zfs list -H -o name -r -t snapshot $dataset \
| grep "$dataset@zfs-auto-snap${LABEL}" | tail -1)
PREV_SNAP=$(zfs list -H -o name -r -t snapshot $dataset \
| grep "$dataset@zfs-auto-snap${LABEL}" \
| tail -2 | head -1)
if [ "$PREV_SNAP" == "$LAST_SNAP" ]
then
echo "Previous snap not found of $dataset, taking full backup"
BACKUP="full"
fi
;;
"full")
LAST_SNAP=$(zfs list -H -o name -r -t snapshot $dataset \
| grep "$dataset@zfs-auto-snap${LABEL}" | tail -1)
;;
*)
check_failure 1 "Unknown backup type $BACKUP"
svccfg -s $FMRI setprop zfs/backup-lock = astring: "unlocked"
svcadm refresh $FMRI
return 1
;;
esac
# Now perform the backup. Note that on errors, we'll immediately mark
# the service as being in maintenance mode, however, backups will still
# be attempted for other datasets in our list.
case $BACKUP in
"incremental")
zfs send -i $PREV_SNAP $LAST_SNAP | $BACKUP_SAVE_CMD
check_failure $? "Error performing incremental backup of $dataset."
;;
"full")
zfs send $LAST_SNAP | $BACKUP_SAVE_CMD
check_failure $? "Error performing full backup of $dataset."
;;
esac
done
# Now we can release our lock
svccfg -s $FMRI setprop zfs/backup-lock = astring: "unlocked"
svcadm refresh $FMRI
}
# Here's the beginning of the main script. As we're a method script for SMF, # Here's the beginning of the main script. As we're a method script for SMF,
# we take start and stop arguments, and assume that the $SMF_FMRI value is being # we take start and stop arguments, and assume that the $SMF_FMRI value is being
@ -313,7 +488,7 @@ case "$1" in
;; ;;
# the default case, we actually call from the cron job itself that's # the default case, we actually call from the cron job itself that's
# executing this script. # executing this script, and do the job of taking snapshots.
*) *)
SMF_FMRI=$1 SMF_FMRI=$1
# are we being called with the correct argument (an FMRI) ? # are we being called with the correct argument (an FMRI) ?

View File

@ -22,14 +22,14 @@
CDDL HEADER END CDDL HEADER END
--> -->
<service_bundle type='manifest' name='space-timf'> <service_bundle type='manifest' name='space-timf,mybackup'>
<service <service
name='system/filesystem/zfs/auto-snapshot' name='system/filesystem/zfs/auto-snapshot'
type='service' type='service'
version='0.4'> version='0.6'>
<create_default_instance enabled='false' /> <create_default_instance enabled='false' />
<instance name='space-timf' enabled='false' > <instance name='space-timf,mybackup' enabled='false' >
<exec_method <exec_method
type='method' type='method'
@ -60,6 +60,19 @@
override="true"/> override="true"/>
<propval name="snapshot-children" type="boolean" value="false" <propval name="snapshot-children" type="boolean" value="false"
override="true"/> override="true"/>
<propval name="backup" type="astring" value="full"
override="true"/>
<propval name="backup-save-cmd" type="astring"
value="/usr/bin/ssh timf@usuki /usr/bin/pfexec /usr/sbin/zfs receive -d tank/backup"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="mybackup"
override="true"/>
</property_group> </property_group>
</instance> </instance>

View File

@ -33,13 +33,14 @@
# the version string, and call the appropriate "_26" versions of functions # the version string, and call the appropriate "_26" versions of functions
# if we need to. (zenity that ships in s10u2 is based on GNOME 2.6 and doesn't # if we need to. (zenity that ships in s10u2 is based on GNOME 2.6 and doesn't
# have the same functionality as the 2.14-based zenity) # have the same functionality as the 2.14-based zenity)
#
MAIN_TITLE="Take regular ZFS snapshots" MAIN_TITLE="Take regular ZFS snapshots"
function get_interval_26 { function get_interval_26 {
# Get an interval for taking snapshots # Get an interval for taking snapshots
# zenity 2.6 doesn't support the --text option to --list # zenity 2.6 doesn't support the --text option to --list
TITLE="${MAIN_TITLE}: Choose a time period for taking snapshots " TITLE="${MAIN_TITLE}: Choose a time period for taking snapshots."
INTERVAL=$(zenity --list --title="${TITLE}" \ INTERVAL=$(zenity --list --title="${TITLE}" \
--radiolist --column="select" \ --radiolist --column="select" \
--column="interval" x "minutes" x "hours" x "days" x "months") --column="interval" x "minutes" x "hours" x "days" x "months")
@ -167,6 +168,63 @@ function get_snap_children {
fi fi
} }
function get_backup {
# decide if we want to do backup of this filesystem
TITLE="${MAIN_TITLE}: Remote backups"
TEXT="Choose a type of backup to perform for this filesystem:"
BACKUP=$(zenity --list --title="${TITLE}" --text="${TEXT}" \
--radiolist --column="select" \
--column="type" x "full" x "incremental" x "none")
if [ $? -eq 1 ]
then
exit 1;
fi
case $BACKUP in
'incremental' | 'full')
get_backup_command
;;
*)
BACKUP="none"
;;
esac
}
function get_backup_command {
# ask the user which backup command they want to use.
TITLE="${MAIN_TITLE}: Backup command"
TEXT="Enter a command you wish to run on the backup stream.\
eg. eval cat > /net/hostname/backup.\$\$"
BACKUP_COMMAND=$(zenity --entry --title="${TITLE}" --text="${TEXT}" \
--entry-text="ssh timf@hostname \
/usr/bin/pfexec /usr/sbin/zfs receive tank/backup")
if [ $? -eq 1 ]
then
exit 1;
fi
}
function get_label {
# ask the user if they want to attach a label to this instance
TITLE="${MAIN_TITLE}: Label"
TEXT="Choose a label you may use to distinguish this snapshot schedule\
from others (Alphanumeric chars only. Cancel to leave blank.)"
LABEL=$(zenity --entry --title="${TITLE}" --text="${TEXT}" \
--entry-text="")
if [ $? -eq 1 ]
then
LABEL=""
fi
}
function show_summary { function show_summary {
# let's give the user a summary of what we've done: # let's give the user a summary of what we've done:
@ -176,7 +234,11 @@ function show_summary {
Interval = ${INTERVAL}\n\ Interval = ${INTERVAL}\n\
Period = ${PERIOD}\n\ Period = ${PERIOD}\n\
Keep snapshots = ${KEEP_SNAP}\n\ Keep snapshots = ${KEEP_SNAP}\n\
Snapshot Children = ${SNAP_CHILDREN}\n\n\ Snapshot Children = ${SNAP_CHILDREN}\n\
Backup = ${BACKUP}\n\
Backup command = ${BACKUP_COMMAND}\n\
Label = ${LABEL}\n\
\n\
Do you want to write this auto-snapshot manifest now ?" Do you want to write this auto-snapshot manifest now ?"
zenity --question --title="${TITLE}" --text="${TEXT}" zenity --question --title="${TITLE}" --text="${TEXT}"
@ -213,6 +275,8 @@ then
get_period_26 get_period_26
get_maxsnap_26 get_maxsnap_26
get_snap_children get_snap_children
get_backup
get_label
show_summary show_summary
else else
@ -221,6 +285,8 @@ else
get_period get_period
get_maxsnap get_maxsnap
get_snap_children get_snap_children
get_backup
get_label
show_summary show_summary
fi fi
@ -231,7 +297,10 @@ fi
# svc:/system/filesystem/zfs/auto-snapshot:tank-tims--fs # svc:/system/filesystem/zfs/auto-snapshot:tank-tims--fs
ESCAPED_NAME=$(echo $1 | sed -e 's#-#--#g' | sed -e 's#/#-#g' \ ESCAPED_NAME=$(echo $1 | sed -e 's#-#--#g' | sed -e 's#/#-#g' \
| sed -e 's#\.#-#g') | sed -e 's#\.#-#g')
if [ ! -z "${LABEL}" ]
then
ESCAPED_NAME="${ESCAPED_NAME},${LABEL}"
fi
# Now we can build an SMF manifest to perform these actions... # Now we can build an SMF manifest to perform these actions...
cat > auto-snapshot-instance.xml <<EOF cat > auto-snapshot-instance.xml <<EOF
@ -241,10 +310,10 @@ cat > auto-snapshot-instance.xml <<EOF
<service <service
name='system/filesystem/zfs/auto-snapshot' name='system/filesystem/zfs/auto-snapshot'
type='service' type='service'
version='0.4'> version='0.6'>
<create_default_instance enabled='false' /> <create_default_instance enabled='false' />
<instance name='$ESCAPED_NAME' enabled='false' > <instance name='${ESCAPED_NAME}' enabled='false' >
<exec_method <exec_method
type='method' type='method'
@ -262,7 +331,9 @@ cat > auto-snapshot-instance.xml <<EOF
<propval name='duration' type='astring' value='transient' /> <propval name='duration' type='astring' value='transient' />
</property_group> </property_group>
<!-- properties for zfs automatic snapshots -->
<property_group name="zfs" type="application"> <property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="$FILESYS" <propval name="fs-name" type="astring" value="$FILESYS"
override="true"/> override="true"/>
<propval name="interval" type="astring" value="$INTERVAL" <propval name="interval" type="astring" value="$INTERVAL"
@ -275,6 +346,17 @@ cat > auto-snapshot-instance.xml <<EOF
override="true"/> override="true"/>
<propval name="snapshot-children" type="boolean" value="$SNAP_CHILDREN" <propval name="snapshot-children" type="boolean" value="$SNAP_CHILDREN"
override="true"/> override="true"/>
<propval name="backup" type="astring" value="$BACKUP"
override="true"/>
<propval name="backup-save-cmd" type="astring" value="$BACKUP_COMMAND"
override="true"/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="${LABEL}"
override="true"/>
</property_group> </property_group>
</instance> </instance>
@ -290,6 +372,7 @@ echo ""
echo " # svccfg import auto-snapshot-instance.xml" echo " # svccfg import auto-snapshot-instance.xml"
echo "" echo ""
echo "then issue the command :" echo "then issue the command :"
echo " # svcadm enable svc:/system/filesystem/zfs/auto-snapshot:$ESCAPED_NAME" echo " # svcadm enable \
svc:/system/filesystem/zfs/auto-snapshot:$ESCAPED_NAME"
echo "" echo ""
echo "You can see what work will be done by checking your crontab." echo "You can see what work will be done by checking your crontab."

View File

@ -32,7 +32,7 @@
<service <service
name='system/filesystem/zfs/auto-snapshot' name='system/filesystem/zfs/auto-snapshot'
type='service' type='service'
version='0.3'> version='0.6'>
<!-- no point in being able to take snapshots if we don't have a fs --> <!-- no point in being able to take snapshots if we don't have a fs -->
<dependency <dependency
@ -59,15 +59,42 @@
<!-- the properties we expect that any instance will define <!-- the properties we expect that any instance will define
they being : they being :
fs-name : the name of the filesystem we want to snapshot fs-name : The name of the filesystem we want to snapshot
interval : minutes | hours | days | weeks
period : how many (m,d,h,w) do we wait between snapshots interval : minutes | hours | days | months
offset : the offset into the time period we want
keep : how many snapshots we should keep, otherwise, we period : How many (m,h,d,m) do we wait between snapshots
offset : The offset into the time period we want
keep : How many snapshots we should keep, otherwise, we
delete the oldest when we hit this threshold delete the oldest when we hit this threshold
snapshot-children : whether we should recursively snapshot
snapshot-children : Whether we should recursively snapshot
all filesystems contained within. all filesystems contained within.
backup : If we want to perform a "zfs send" for our backup
we set this - either to "full" or "incremental".
If set to "none", we don't perform backups.
backup-save-cmd : A command string to save the backup - if unset,
we return an error and move the service to
maintenance.
backup-lock : A string we set when a backup operation is in
progress, to prevent two backups from the same
service instance running into each other. Not
completely flawless, but okay. Due to 6338294,
we use the keyword "unlocked" to indicate that
the lock is not held.
label : A string that allows us to differentiate this set
of snapshot schedules from others configured for the
same filesystem. This is not usually needed and can
be left unset, but it can be useful in some
situations (particularly for backups).
--> -->
<property_group name="zfs" type="application"> <property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="Not set" override="true"/> <propval name="fs-name" type="astring" value="Not set" override="true"/>
@ -75,6 +102,13 @@
<propval name="offset" type="astring" value="Not set" override="true"/> <propval name="offset" type="astring" value="Not set" override="true"/>
<propval name="snapshot-children" type="boolean" value="false" override="true"/> <propval name="snapshot-children" type="boolean" value="false" override="true"/>
<propval name="keep" type="astring" value="all" override="true"/> <propval name="keep" type="astring" value="all" override="true"/>
<propval name="backup" type="astring" value="none" override="true"/>
<propval name="backup-save-cmd" type="astring" value="" override="true"/>
<propval name="backup-lock" type="astring" value="unlocked" override="true"/>
<propval name="label" type="astring" value="" override="true"/>
</property_group> </property_group>
<!-- we're *not* defining an instance here : the idea is that instances <!-- we're *not* defining an instance here : the idea is that instances
@ -101,11 +135,21 @@
</property_group> </property_group>
<property_group name="zfs" type="application"> <property_group name="zfs" type="application">
<propval name="fs-name" type="astring" value="tank/timf"/> <propval name="fs-name" type="astring" value="tank/timf"/>
<propval name="interval" type="astring" value="days"/> <propval name="interval" type="astring" value="days"/>
<propval name="period" type="astring" value="7"/> <propval name="period" type="astring" value="7"/>
<propval name="offset" type="astring" value="0"/> <propval name="offset" type="astring" value="0"/>
<propval name="snapshot-children" type="boolean" value="false"/> <propval name="snapshot-children" type="boolean" value="false"/>
<propval name="backup" type="astring" value="full" override="true"/>
<propval name="backup-save-cmd" type="astring" override="true"
value="ssh haiiro.ireland zfs receive -d tank/data "/>
<propval name="backup-lock" type="astring" value="unlocked"
override="true"/>
<propval name="label" type="astring" value="all" override="true"/>
</property_group> </property_group>
</instance> </instance>
@ -122,20 +166,31 @@
</common_name> </common_name>
<description> <description>
<loctext xml:lang='C'> <loctext xml:lang='C'>
This service provides system support for taking This service provides system support for taking automatic snapshots of ZFS
automatic snapshots of ZFS filesystems. filesystems.
In order to use this service, you must create
instances per set of automatic snapshots you
want to create.
The on starting a service instance, a cron job In order to use this service, you must create a service instance per set of
corresponding to the properties set in the automatic snapshots you want to take.
instance is created on the host. This cron job
will regularly take snapshots of the specified The on starting a service instance, a cron job corresponding to the properties
ZFS filesystem. set in the instance is created on the host. This cron job will regularly take
snapshots of the specified ZFS filesystem.
On stopping the service, that cron job is removed.
We also have the ability to perform backups, done using the "zfs send" command.
A property set in the service called "backup-save-cmd" can be configured as the
command used to save the backup stream. See the zfs(1M) man page for an example.
The backups can be either "full" backups, or "incremental" backups - for each
incremental backup, a full backup must be configured first. If for some reason
an incremental backup fails, a full backup is performed instead.
Care should be taken when configuring backups to ensure that the time
granularity of the cron job is sufficient to allow the backup to complete
between invocations of each backup. We perform locking to ensure that two
backups of the same filesystem cannot run simultaneously, but will move the
service into "maintenance" state should this occur.
On stopping the service, that cron job is
removed.
</loctext> </loctext>
</description> </description>
</template> </template>