View Single Post
  #18 (permalink)  
Old 07-15-2009, 11:08 AM
keith@keithnoddle.org keith@keithnoddle.org is offline
Starter Member
 
Posts: 1
Default Restore script

Firstly, I've only been playing with Zimbra since Friday last, so please be gentle...

I read with interest this per user backup thread and all credit to the author - good stuff!

But backups are no good without a matching restore, so I've set to and created one based upon reversing the actions the backup script takes (and un-doing a few things that don't exactly reverse!). Now, caveat emptor is the rule here because I've only undertaken limited testing against a single account on a test server. So far so good, but I'd really appreciate bigger brains that mine taking a look at the script below and telling me what I've missed.

There are a couple of gotchas which I've tried to comment but I'm sure there are others.

Hope this is of some interest.

Keith.

Code:
#!/bin/bash

#===========================================================================================
# Zimbra Restore User - based on reversing the actions of zimbra_backup_user.sh
#
# Basic Testing ONLY undertaken.
#
#    ************************
#    * USE AT YOUR OWN RISK *
#    ************************
#
#===========================================================================================

###
# Requirements : bash, whoami, su, echo, tee, cut, grep, mkdir, chown, rsync, du, zimbra 5.0.x
#
###

# Are we root?
if [ `whoami` != "root" ]; then
        echo "ERROR! ${0} must be run by the root user"
        exit 1
fi

# Output help, needs work.
function display_usage {
	echo "${0} [emailaddress] [backupDirToRestore]"
	echo ""
	echo " [emailaddress] : A valid email account on your Zimbra server."
	echo " [backupDirToRestore] : A valid directory from which to restore"
	exit 1	
}

#===========================================================================================
# New functions added to facilitate restore.
#
# Included here to avoid changes to zimbra_functions.sh - even
# though that is the logical place for them.
#===========================================================================================
# rsync from cache
# $1 [string] : file/directory path
function zimbra_restore_rsync {
	if [ ! -e ${1} ]; then
		log "WARNING! I can't rsync ${1} because it does not exist."
	else
		local DIRS=`ls -1 ${zimbra_cache}/${current_account}${1}`
		for DIR in ${DIRS}
		do
			# Skip the incoming directory
			if [ ${DIR} != "incoming" ]; then
				log "Syncing ${1}/${DIR}/${mailbox_id} from ${zimbra_cache}/${current_account}${1}/${DIR}"
				rsync -aHK ${zimbra_cache}/${current_account}${1}/${DIR} ${1}
				log "Size : `du -s -h ${zimbra_cache}/${current_account}${1}/${DIR} | cut -f1`"
			fi
		done
	fi
}

# Executes mysql restore
# $1 [string] : mysql paramters
function zimbra_sql {
	log "Restoring ${1}"
	zimbra_run "${mysql_directory}/bin/mysql -f -S ${mysql_socket} -u ${zimbra_mysql_user} --password=${zimbra_mysql_password} ${1}"
}
#===========================================================================================



# Have we got enough parameters
if [ $# -ne 2 ]; then
	display_usage
else
	current_account="${1}"
	current_backup="${2}"
fi

# Setup the zimbra variables and functions
source ./zimbra_functions.sh || exit 1

if [ ! -e ${current_backup} ]; then
	display_usage
else
	zimbra_chown ${current_backup}
fi

# Line breaking please.
IFS=$'\n'

log "Processing ${current_account}"
# Get the zimbraId
current_account_zimbraId=`zimbra_run "${zimbra_home}/bin/zmprov ga ${current_account} zimbraId | grep zimbraId | cut -d' ' -f2"`
restoreZimbraID=`grep zimbraId: ${current_backup}/ga.txt | cut -d' ' -f2`

# Don't try to restore a deleted (or otherwise damaged) account
if [ "${current_account_zimbraId}" != "${restoreZimbraID}" ]; then
	echo "***************************************************************"
	echo "* There seems to be a problem with account ${current_account} *"
	echo "* Either the account doesn't exist or the backup and          *"
	echo "* current zimbraIds don't match                               *"
	echo "*                                                             *"
	echo "*             Restore operation cancelled.                    *"
	echo "***************************************************************"
	exit 1
fi

# Ensure cache exists and cleared for current account. Savage, but it works for me.
rm -rf ${zimbra_cache}/${current_account}
mkdir -p ${zimbra_cache}/${current_account}

# Get the quotaUsed - Could be used to (optionally?) skip accounts using zero quota in the future
current_account_quotaUsed=`zimbra_run "${zimbra_home}/bin/zmprov gmi ${current_account} | grep quotaUsed | cut -d' ' -f2"`
# Get the mailboxId
mailbox_id=`zimbra_run "${zimbra_home}/bin/zmprov gmi ${current_account} | grep mailboxId | cut -d' ' -f2"`

# Get the group and volume ids for the current account.
log "Getting group_id and index_volume_id"
zimbra_create_sql "SELECT group_id, index_volume_id FROM zimbra.mailbox WHERE account_id='"${current_account_zimbraId}"'"
zimbra_execute_sql
mailbox_group_id=`echo ${zimbra_sql_result} | cut -f1`
mailbox_index_volume_id=`echo ${zimbra_sql_result} | cut -f2`
mailbox="mboxgroup${mailbox_group_id}"
log "zimbraId   : ${current_account_zimbraId}"
log "Id         : ${mailbox_id}"
log "Group      : ${mailbox_group_id}"
log "Mailbox    : ${mailbox}"
log "Quota Used : ${current_account_quotaUsed}"

# Get the volume details for the current account.
log "Getting message_volume_id and index_volume_id"
zimbra_create_sql "SELECT message_volume_id, index_volume_id FROM zimbra.current_volumes WHERE index_volume_id='"${mailbox_index_volume_id}"'"
zimbra_execute_sql
message_volume_id=`echo ${zimbra_sql_result} | cut -f1`
index_volume_id=`echo ${zimbra_sql_result} | cut -f2`

# Get the index volume name and path
log "Getting index volume path"
zimbra_create_sql "SELECT name, path FROM zimbra.volume WHERE id='"${index_volume_id}"'"
zimbra_execute_sql
index_volume_name=`echo ${zimbra_sql_result} | cut -f1`
index_volume_path=`echo ${zimbra_sql_result} | cut -f2`

# Get the message volume name and path
log "Getting message volume path"
zimbra_create_sql "SELECT name, path FROM zimbra.volume WHERE id='"${message_volume_id}"'"
zimbra_execute_sql
message_volume_name=`echo ${zimbra_sql_result} | cut -f1`
message_volume_path=`echo ${zimbra_sql_result} | cut -f2`
log "${index_volume_name} path   : ${index_volume_path}"
log "${message_volume_name} path : ${message_volume_path}"

#===========================================================================================
# No hot restore - not sure what to do with ga.txt and gsig.txt...
#===========================================================================================

# Set the account to maintenance mode (Read-only, new mail will be queued at the MTA)
zimbra_set_account_status maintenance

### Warm restore of the current account database, current account rows, index and store directories.
#
log "Restoring index_n_store tarball to ${zimbra_cache}/${current_account}"
tar zxvf ${current_backup}/index_n_store.tar.gz ${zimbra_cache}/${current_account}/ 2&> /dev/null

zimbra_restore_rsync ${index_volume_path}
zimbra_restore_rsync ${message_volume_path}

#===========================================================================================
# Note that the way the mboxgroupN is dumped by zimbra_backup_user.sh means restoring it
# will cause anything held in mboxgroupN that was created after the backup was taken to
# be lost. This could maybe do with some work to merge rather than replace the entries?
#===========================================================================================
zimbra_sql "${mailbox} < ${current_backup}/${mailbox}.sql"
zimbra_sql "zimbra < ${current_backup}/mailbox.sql"
zimbra_sql "zimbra < ${current_backup}/mailbox_metadata.sql"
zimbra_sql "zimbra < ${current_backup}/out_of_office.sql"
zimbra_sql "zimbra < ${current_backup}/scheduled_task.sql"
zimbra_sql "zimbra < ${current_backup}/table_maintenance.sql"

# Tidy up
rm -rf ${zimbra_cache}/${current_account}

#
### Warm restore ends

# Set the account to back to active mode
zimbra_set_account_status active

# Purely cosmetic
echo ""
Reply With Quote