173 lines
5.1 KiB
Bash
173 lines
5.1 KiB
Bash
|
#!/bin/sh
|
||
|
#
|
||
|
# 2014/11/14 - Initial script creation
|
||
|
#
|
||
|
# Based on snapxfer script by pds from zpool.org
|
||
|
# https://zpool.org/zfs-snapshots-and-remote-replication/
|
||
|
#
|
||
|
# Also main use zfSnap (https://github.com/zfsnap/zfsnap/wiki/zfSnap)
|
||
|
# Must take the 1.x version (tree legacy). Too many changes in the 2.0.0 beta.
|
||
|
|
||
|
# Local ZFS filesystems that will have a snapshot
|
||
|
DATASETS="datastore/vm"
|
||
|
|
||
|
# Destination host for snapshot replication
|
||
|
# The user must have enought rights to use `zfs command`
|
||
|
DHOST="root@192.168.42.52"
|
||
|
|
||
|
# Output logfile
|
||
|
LOGFILE="/var/log/snapsend/snapsend.log"
|
||
|
|
||
|
# Tools that help implement snapshot logic (and transfer soon according the pull-requests)
|
||
|
ZSNAP="/usr/local/bin/zfSnap.sh"
|
||
|
|
||
|
# Administrator's email
|
||
|
MAILADMIN="admin1@localhost,admin2@localhost"
|
||
|
|
||
|
# List of all ZFS snapshot
|
||
|
SNAP_LIST=''
|
||
|
REMOTE_SNAP_LIST=''
|
||
|
|
||
|
# The prefix to add to the previous snapshot's name which is now useless
|
||
|
PREFIXSNAP='done_'
|
||
|
|
||
|
######################################################################
|
||
|
# Functions #
|
||
|
######################################################################
|
||
|
|
||
|
# Returns 0 if snapshot exists
|
||
|
SnapExists() {
|
||
|
SNAP_LIST="${SNAP_LIST:-`zfs list -H -o name -t snapshot`}"
|
||
|
local i
|
||
|
for i in $SNAP_LIST; do
|
||
|
[ "$1" = "$i" ] && return 0
|
||
|
done
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
# Returns 0 if snapshot exists on the remote host
|
||
|
RemoteSnapExist() {
|
||
|
REMOTE_SNAP_LIST="${REMOTE_SNAP_LIST:-`ssh ${1} zfs list -H -o name -t snapshot`}"
|
||
|
local i
|
||
|
for i in $REMOTE_SNAP_LIST; do
|
||
|
[ "$2" = "$i" ] && return 0
|
||
|
done
|
||
|
return 1
|
||
|
}
|
||
|
|
||
|
######################################################################
|
||
|
# Main logic #
|
||
|
######################################################################
|
||
|
|
||
|
interval=$1
|
||
|
|
||
|
usage()
|
||
|
{
|
||
|
if [ "X${interval}" = "X" ]; then
|
||
|
printf 'Usage: %s (async|hourly|daily|weekly|purge)\n' $(basename $0)
|
||
|
printf '\n'
|
||
|
printf '* Asynchronous DR snapshots are kept for 1 hour\n'
|
||
|
printf '* Hourly snapshots are kept for 1 day\n'
|
||
|
printf '* Daily snapshots are kept for one week\n'
|
||
|
printf '* Weekly snapshots are kept for one month\n'
|
||
|
printf '\n'
|
||
|
|
||
|
exit 1
|
||
|
fi
|
||
|
}
|
||
|
|
||
|
if [ ! -f "${LOGFILE}" ]; then
|
||
|
mkdir -p $(dirname ${LOGFILE})
|
||
|
touch "${LOGFILE}"
|
||
|
fi
|
||
|
|
||
|
case "${interval}" in
|
||
|
'async' )
|
||
|
for dset in $DATASETS
|
||
|
do
|
||
|
# Take snapshots for asynchronouss DR purposes
|
||
|
$ZSNAP -v -s -S -a 1h $dset >> $LOGFILE
|
||
|
|
||
|
# Get the last snapshot name on localhost
|
||
|
LOCALSNAP=$(zfs list -H -o name -t snapshot -r ${dset} | tail -n1 | cut -d@ -f2)
|
||
|
#printf 'Local snapshot: %s\n' ${LOCALSNAP}
|
||
|
|
||
|
# Get the last snapshot name on remote host $DHOST
|
||
|
DHOSTSNAP=$(ssh ${DHOST} zfs list -H -o name -t snapshot -r ${dset} | tail -n1 | cut -d@ -f2)
|
||
|
#printf 'Remote snapshot: %s\n' ${DHOSTSNAP}
|
||
|
|
||
|
# Test if $DHOSTSNAP exist on the local system
|
||
|
# Recompose the full snapshot name with $dset@$DHOSTSNAP
|
||
|
if SnapExists "${dset}@${DHOSTSNAP}"; then
|
||
|
#printf '%s exist on the local system\n' ${DHOSTSNAP}
|
||
|
printf '%s@%s will be send to %s to replace the old snapshot: %s' ${dset} ${LOCALSNAP} ${DHOST} ${DHOSTSNAP} >> $LOGFILE
|
||
|
|
||
|
# Send the snapshot the remote host
|
||
|
# zfs send -I datastore/vm@2014-11-17_11.26.22--1h datastore/vm@2014-11-18_14.19.10--1h | ssh nec02.ipr.univ-rennes1.fr zfs recv datastore/vm
|
||
|
zfs send -I "${DHOSTSNAP}" "${dset}@${LOCALSNAP}" | ssh "${DHOST}" zfs recv "${dset}"
|
||
|
printf ' ... done\n' >> $LOGFILE
|
||
|
else
|
||
|
# Mail admin
|
||
|
printf 'ERROR snapshot %s does not exist on the local system\n' ${DHOSTSNAP} | mail ${MAILADMIN} -s 'snapsend_error'
|
||
|
printf 'ERROR snapshot %s for %s does not exist on the local system\n' ${DHOSTSNAP} ${dset} >> $LOGFILE
|
||
|
fi
|
||
|
|
||
|
# Test if the snapshot was successfully receive on the remote host
|
||
|
if RemoteSnapExist "${DHOST}" "${dset}@${LOCALSNAP}"; then
|
||
|
printf 'SUCCESS: the snapshot %s for %s exist on the remote host (%s)\n' ${LOCALSNAP} ${dset} ${DHOST} >> $LOGFILE
|
||
|
|
||
|
# Rename the useless snapshot for the next purge
|
||
|
zfs rename ${dset}@${DHOSTSNAP} ${dset}@${PREFIXSNAP}${DHOSTSNAP}
|
||
|
else
|
||
|
# Mail admin
|
||
|
printf 'ERROR snapshot %s for %s does not exist on the remote host (%s)\n' ${LOCALSNAP} ${dset} ${DHOST} | mail ${MAILADMIN} -s 'snapsend_error'
|
||
|
printf 'ERROR snapshot %s for %s does not exist on the remote host (%s)\n' ${LOCALSNAP} ${dset} ${DHOST} >> $LOGFILE
|
||
|
fi
|
||
|
done
|
||
|
|
||
|
printf '\n' >> $LOGFILE
|
||
|
;;
|
||
|
|
||
|
'hourly' )
|
||
|
# take snapshots, keep for one day
|
||
|
for dset in $DATASETS
|
||
|
do
|
||
|
$ZSNAP -v -s -S -a 1d $dset >> $LOGFILE
|
||
|
done
|
||
|
printf '\n' >> $LOGFILE
|
||
|
;;
|
||
|
|
||
|
'daily' )
|
||
|
# take snapshots, keep for one week
|
||
|
for dset in $DATASETS
|
||
|
do
|
||
|
$ZSNAP -v -s -S -a 1w $dset >> $LOGFILE
|
||
|
done
|
||
|
|
||
|
# Purge snapshots according to TTL
|
||
|
$ZSNAP -v -s -S -d -p ${PREFIXSNAP} >> $LOGFILE
|
||
|
printf '\n' >> $LOGFILE
|
||
|
;;
|
||
|
|
||
|
'weekly' )
|
||
|
# take snapshots, keep for one month
|
||
|
for dset in $DATASETS
|
||
|
do
|
||
|
$ZSNAP -v -s -S -a 1m $dset >> $LOGFILE
|
||
|
done
|
||
|
printf '\n' >> $LOGFILE
|
||
|
;;
|
||
|
|
||
|
'purge' )
|
||
|
# purge snapshots according to TTL
|
||
|
$ZSNAP -v -s -S -d
|
||
|
;;
|
||
|
|
||
|
* )
|
||
|
usage
|
||
|
;;
|
||
|
|
||
|
esac
|
||
|
|
||
|
exit 0
|