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

Comments

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <pre> <img> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • Textual smileys will be replaced with graphical ones.

More information about formatting options

Smileys

Smileys
SmileyAcronyms
=)=) :) :-) :smile:
;);) ;-) :wink:
=(=( :( :-( :sad:
=D=D :D :-D :lol:
}:)}:) }:-) :evil:
=p=p =P :P :-P :tongue:
O.oO.o :O :-O :shocked:
:?:? :-? :puzzled:
8)8) 8-) :cool:
:jawdrop::jawdrop:
:sick::sick: :barf: