Script: Zimbra Backup Script
I am currently migrating out of Zimbra to a 3rd-party host. Just for archival, here's my Zimbra backup script. Just run this script using a cronjob every day. There will be a short downtime where Zimbra is shutdown to synchronise the last bit of emails, but that should be okay if you have a backup MX server.
This script creates a working live copy of the Zimbra directory, then shutdown Zimbra to sync the directory. The directory is then passed through star, into a small(er) file.
#!/bin/bash
########
## Zimbra backup script
##
## See
##
## Requires star, rsync, bash, gzip
## Does full backups of /opt/zimbra only. Tries to
## minimise zimbra shutdown time with a live rsync,
## then a offline rsync
##
## Changelog
## ---------
## 15 Feb 2009 (Junhao)
## - BUGFIX: deletes leftover archive.tgz tarball before
## creating new tar
## 21 Oct 2008 (Junhao)
## - added disk size to log
## - added tarball -t test
## 19 Oct 2008 (Junhao)
## - Initial commit
##
########
## config
_AWK=`which awk`
_CAT=`which cat`
_CD=cd #bash builtin
_CHECKSUM=`which sha1sum`
_DATE=`which date`
_DF=`which df`
_DU=`which du`
_ECHO=`which echo`
_MV=`which mv`
_MAIL=`which mail`
_RSYNC=`which rsync`
_RM=`which rm`
_SLEEP=`which sleep`
_SU=`which su`
_TAR=`which star`
_TOUCH=`which touch`
DATESTAMP=`date +%Y%b%d-%T`
RELEASE=`${_SU} - zimbra -c"zmcontrol -v"`
RELEASE=`${_ECHO} ${RELEASE} | ${_AWK} '{ print $2"-"$3"-"$4 }'`
TARBALL=zimbra-${RELEASE}-backup-full-${DATESTAMP}.tgz
LOGFILE=zimbra-${RELEASE}-backup-full-${DATESTAMP}.log
EMAIL=user@domain.co.m
MAILSUB="ZCS Backup Report on ${DATESTAMP}"
BKUPRETRIES=5
RESTARTRETRIES=100
ZIMBRADIR=/opt/zimbra
BASEDIR=/opt/zimbra-backups
WORKDIR=${BASEDIR}/working
LOGDIR=${BASEDIR}/logs
SAVEDIR=${BASEDIR}/saved
LOG=${LOGDIR}/${LOGFILE}
CHKSUMLOG=${SAVEDIR}/checksum
TARLOG=${WORKDIR}/tar.log
LOCK=${BASEDIR}/zimbra-backup.lock
TEMPARCHIVE=archive.tgz
function calc_downtime() {
if [ -z "${STARTTIME3}" ]; then
STARTTIME3=`date +%s`
fi
if [ -z "${ENDTIME}" ]; then
ENDTIME=`date +%s`
fi
TOTAL=$((${ENDTIME} - ${STARTTIME1}))
OFFLINE=$((${STARTTIME3} - ${STARTTIME2}))
${_ECHO} "Time taken: $((${TOTAL} / 3600)) hours $((${TOTAL} % 3600 / 60)) minutes $((${TOTAL} % 3600 % 60)) seconds" >> ${LOG}
${_ECHO} "Zimbra Offline: $((${OFFLINE} / 3600)) hours $((${OFFLINE} % 3600 / 60)) minutes $((${OFFLINE} % 3600 % 60)) seconds" >> ${LOG}
return 0
}
function force_restart() {
for (( i=0; i<=${RESTARTRETRIES} ; i=$(($i+1)) )); do
${_SU} - zimbra -c"zmcontrol stop"
${_SLEEP} 10
${_SU} - zimbra -c"zmcontrol start"
${_SLEEP} 10
${_SU} - zimbra -c"zmcontrol status"
if [[ $? == 0 ]]; then
${_ECHO} "Sucessfully restarted zimbra after $((${i}+1)) tries" >> ${LOG}
return 0
else
${_SLEEP} 10
fi
done
${_ECHO} "Could not restart zimbra after ${RESTARTRETRIES} tries" >> ${LOG}
return 254
}
function lock_set() {
${_TOUCH} ${LOCK}
}
function lock_remove() {
${_RM} ${LOCK}
}
function synchronise() {
for (( i=0; i<=${BKUPRETRIES} ; i=$(($i+1)) )); do
${_RSYNC} -avHK --delete --exclude=*.pid ${ZIMBRADIR} ${WORKDIR}
if [[ $? == 0 || $? == 24 ]]; then
return $?
fi
${_SLEEP} 10
done
return 254
}
function send_mail() {
calc_downtime
${_ECHO} "" >> ${LOG}
${_SU} - zimbra -c"zmcontrol status" >> ${LOG}
${_ECHO} "" >> ${LOG}
${_CAT} ${TARLOG} >> ${LOG}
${_RM} ${TARLOG}
${_CAT} ${LOG} | ${_MAIL} -s "${MAILSUB}" ${EMAIL}
}
function send_error() {
MAILSUB="[Failed] ${MAILSUB}"
send_mail
}
function send_success() {
MAILSUB="[Success] ${MAILSUB}"
send_mail
}
${_TOUCH} ${LOG}
lock_set
${_ECHO} "Server: `${_SU} - zimbra -c"zmhostname"`" >>${LOG}
${_ECHO} "Tarball: ${TARBALL}" >> ${LOG}
${_ECHO} "Logfile: ${LOG}" >> ${LOG}
${_ECHO} "Backup started at ${DATESTAMP}" >> ${LOG}
${_ECHO} "" >> ${LOG}
## Outputs time backup started for logging
STARTTIME1=`${_DATE} +%s`
## Online sync to working directory
${_ECHO} "Starting online rsync" >> ${LOG}
synchronise
if [[ $? == 254 ]]; then
${_ECHO} "Error in creating live copy" >> ${LOG}
send_error
lock_remove
exit 1
fi
STARTTIME2=`${_DATE} +%s`
## Shut down zimbra
${_SU} - zimbra -c"zmcontrol stop"
if [[ $? != 0 ]]; then
${_ECHO} "Error stopping zimbra" >> ${LOG}
${_SU} - zimbra -c"zmcontrol status" >>${LOG}
${_ECHO} "Aborting backup, force restarting zimbra" >> ${LOG}
force_restart
if [[ $? == 255 ]]; then
${_ECHO} "Trying again" >> ${LOG}
force_restart
fi
send_error
lock_remove
exit 2
fi
${_SLEEP} 10
## Offline sync to working directory
${_ECHO} "Starting offline rsync" >>${LOG}
synchronise
if [[ $? == 0 ]]; then
${_ECHO} "Offline rsync completed successfully" >> ${LOG}
elif [[ $? == 24 ]]; then
## some files disappeared, meaning some open process running
## wait a while, rerun rsync, and assume okay
${_SLEEP} 60
synchronise
else
${_ECHO} "Error in creating offline copy" >> ${LOG}
${_ECHO} "Aborting backup, force restarting zimbra" >> ${LOG}
force_restart
send_error
lock_remove
exit 3
fi
## Start zimbra
force_restart
if [[ $? == 254 ]]; then
${_ECHO} "Trying again" >> ${LOG}
force_restart
send_error
lock_remove
exit 4
fi
STARTTIME3=`date +%s`
## Synchronization sucessful, create archive
${_CD} ${WORKDIR}
if [ -f ${TARLOG} ]; then
${_RM} ${TARLOG}
else
${_TOUCH} ${TARLOG}
fi
if [ -f ${TEMPARCHIVE} ]; then
${_RM} ${TEMPARCHIVE}
fi
${_TAR} czf ${TEMPARCHIVE} ./ 1>> ${TARLOG} 2>&1
if [[ $? != 0 && $? != 254 ]]; then
${_ECHO} "Error creating tarball. Error Code: $?" >> ${LOG}
${_ECHO} "Aborting backup" >> ${LOG}
${_ECHO} "Tarball location: ${WORKDIR}/${TEMPARCHIVE}" >> ${LOG}
send_error
lock_remove
exit 5
fi
${_TAR} ztf ${TEMPARCHIVE}
if [[ $? == 0 ]]; then
${_MV} ${TEMPARCHIVE} ${SAVEDIR}/${TARBALL}
${_ECHO} "" >> ${LOG}
${_DU} -sh ${SAVEDIR}/${TARBALL} >> ${LOG}
${_DF} -h ${SAVEDIR}/${TARBALL} >> ${LOG}
else
${_ECHO} "Error validating tarball. Error Code: $?" >> ${LOG}
${_ECHO} "Aborting backup" >> ${LOG}
${_ECHO} "Tarball location: ${WORKDIR}/${TEMPARCHIVE}" >> ${LOG}
send_error
lock_remove
exit 6
fi
## Creating checksum
${_CD} ${SAVEDIR}
CHKSUM=`${_CHECKSUM} ${TARBALL}`
${_ECHO} "${CHKSUM}" >> ${CHKSUMLOG}
${_ECHO} "" >> ${LOG}
${_ECHO} "Checksum using ${_CHECKSUM}">> ${LOG}
${_ECHO} "${CHKSUM}" >> ${LOG}
## Backup done!
ENDTIME=`date +%s`
send_success
lock_remove
exit 0