forked from extern/zfs-auto-snapshot
Version 0.6
This commit is contained in:
parent
5759424d8e
commit
acca7a30a3
@ -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
|
|
||||||
|
|
||||||
|
60
auto-snapshot-instance.xml
Normal file
60
auto-snapshot-instance.xml
Normal 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>
|
@ -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
|
||||||
@ -106,6 +120,11 @@ function schedule_snapshots {
|
|||||||
#
|
#
|
||||||
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)
|
|
||||||
|
|
||||||
# Ok, now say cheese! It'd be nice if the child snapshotting was
|
typeset BACKUP=$(svcprop -p zfs/backup $FMRI)
|
||||||
# atomic, but we don't yet have that in zfs.
|
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! 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) ?
|
||||||
|
@ -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>
|
||||||
|
@ -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."
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user