#!/bin/bash # # xenBackup - Backup Xen Domains # # Version: 1.0: Created: John D Quinn, http://www.johnandcailin.com/john # Version: 1.01: Modified: Adam Wilbraham 20080829 - Added basic functionality to use LVM snapshots # Version: 1.02: Modified: Adam Wilbraham 20080830 - Added NTFS mounting support # Version: 1.03: Modified: Adam Wilbraham 20080904 - Changed "all" behaviour so it backs up all "*-disk" LVs as opposed to only the running domUs # Version: 1.04: Modified: Adam Wilbraham 20080904 - Added support for backing up a block device to a compressed image (eg for Windows VMs) # Version 1.05: Modified: Pietro Abate 20090201 - Refactoring and added dar incremental backup # initialize our variables domains="null" # the list of domains to backup allDomains="null" # backup all domains? BACKUPDIR="/srv/backup" # the default backup target directory TEMPDIR="/mnt/snapshot" # the mount point to use to mount disk areas XENGROUP="/dev/xenhosts" # the LVM volume group to use shutdownDomains=false # don't shutdown domains by default quiet=false # keep the chatter down DAR=/usr/bin/dar XM=/usr/sbin/xm LVCREATE=/sbin/lvcreate LVREMOVE=/sbin/lvremove LVDISPLAY=/sbin/lvdisplay INCRNO="full" purgeAge="null" # age at which to purge increments globalBackupResult=0 # success status of overall job # settings for logging (syslog) loggerArgs="" # what extra arguments to the logger to use loggerTag="xen-backup" # the tag for our log statements loggerFacility="local3" # the syslog facility to log to # trap user exit and cleanup #trap 'cleanup;exit 1' 1 2 cleanup() { SNAPSHOT=$1 ${logDebug} "Cleaning up" cd / ; umount ${TEMPDIR} > /dev/null ${LVREMOVE} -f ${SNAPSHOT} # restart the domain if test ${shutdownDomains} = "true" ; then ${logDebug} "Restarting domain" ${XM} create ${domain}.cfg > /dev/null fi } # function to print a usage message and bail usageAndBail() { cat << EOT Usage: xenBackup [OPTION]... Backup xen domains to a target area. different backup engines may be specified to produce a tarfile, an exact mirror of the disk area or a mirror with incremental backup. -d backup only the specified DOMAINs (comma seperated list) -t target LOCATION for the backup e.g. /tmp or root@www.example.com:/tmp -a backup all domains -s shutdown domains before backup (and restart them afterwards) -q run in quiet mode, output still goes to syslog -p purge increments older than TIME_SPEC. -i sequence number for the incremental backup. If not specified the default is a full backup Example 1 Backup all domains to the /tmp directgory $ xenBackup -a -t /tmp Example 2 Backup domain: "wiki" to directory /var/xenImages on machine backupServer, $ xenBackup -d wiki -t root@backupServer:/var/xenImages Example 3 Backup domains "domainOne" and "domainTwo" purging old increments older than 5 days $ xenBackup -d "domainOne, domainTwo" -p 5D EOT exit 1; } # parse the command line arguments while getopts p:i:qsad:t:h o ; do case "$o" in q) quiet="true";; s) shutdownDomains="true";; a) allDomains="true";; d) domains="$OPTARG";; t) BACKUPDIR="$OPTARG";; i) INCRNO="$OPTARG";; p) purgeAge="$OPTARG";; h) usageAndBail;; [?]) usageAndBail esac done # if quiet don't output logging to standard error if test ${quiet} = "false" ; then loggerArgs="-s" fi # setup logging subsystem. using syslog via logger logCritical="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.crit" logWarning="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.warning" logDebug="logger -t ${loggerTag} ${loggerArgs} -p ${loggerFacility}.debug" # make sure only root can run our script test $(id -u) = 0 || { ${logCritical} "This script must be run as root"; exit 1; } # make sure that the guest manager is available test -x ${XM} || { ${logCritical} "xen guest manager (${XM}) not found"; exit 1; } # assemble the list of domains to backup if test ${allDomains} = "true" ; then # all domains, even if not running domainList=`${LVDISPLAY} -c ${XENGROUP} | cut -d':' -f1 | cut -d'/' -f4 | egrep -v "\-tempsnap|\-swap" | grep "\-disk" | sed {s/-disk//}` else # make sure we've got some domains specified if test "${domains}" = "null" ; then usageAndBail fi # create the domain list by mapping commas to spaces domainList=`echo ${domains} | tr -d " " | tr , " "` fi # function to do a "dar" of domain backupDomain () { domain=$1 test -x ${DAR} || { ${logCritical} "dar (${DAR}) not found"; exit 1; } if test ${quiet} = "false" ; then verbosity="3" else verbosity="0" fi backupname=${BACKUPDIR}/${domain} mkdir -p ${BACKUPDIR} > /dev/null 2>&1 if test ${INCRNO} = "full" ; then ${logDebug} "full backup for domain ${domain} to ${BACKUPDIR}" ${DAR} -R ${TEMPDIR}/ -c "${backupname}_full" else refbackup="" if [ "$INCRNO" = 1 ] ; then refbackup="${backupname}_full" else refno=$[$INCRNO-1] refbackup="${backupname}_incr$refno" fi ${logDebug} "incremantal backup n. ${INCRNO} for domain ${domain} to ${BACKUPDIR}" ${DAR} -R / -c "${backupname}_incr$INCRNO" -A "$refbackup" fi backupResult=$? return ${backupResult} } # backup the specified domains for domain in ${domainList} ; do date=`/bin/date` ${logDebug} "${date}" ${logDebug} "backing up domain: ${domain}" # make sure that the domain is shutdown if required if test ${shutdownDomains} = "true" ; then ${logDebug} "shutting down domain ${domain}" ${XM} shutdown -w ${domain} > /dev/null fi # unmount mount point if already mounted umount ${TEMPDIR} > /dev/null 2>&1 # create LVM snapshot of the domain we want to backup and mount it read-only ${LVCREATE} -s -L 5G -n ${domain}-disk-tempsnap ${XENGROUP}/${domain}-disk SNAPSHOT=${XENGROUP}/${domain}-disk-tempsnap test -r ${SNAPSHOT} || { ${logCritical} "xen disk area not readable. are you sure that the domain \"${domain}\" exists?"; cleanup ${SNAPSHOT} ; exit 1; } ${logDebug} "Mounting ${SNAPSHOT} read-only" mount -r ${SNAPSHOT} ${TEMPDIR} || { ${logCritical} "mount failed"; cleanup ${SNAPSHOT} ; exit 1; } # do the backup according to the chosen backup engine backupDomain ${domain} # make sure that the backup was successful if test $? -ne 0 ; then ${logCritical} "FAILURE: error backing up domain ${domain}" globalBackupResult=1 else ${logDebug} "SUCCESS: domain ${domain} backed up" fi # clean up cleanup ${SNAPSHOT} done if test ${globalBackupResult} -eq 0 ; then ${logDebug} "SUCCESS: backup of all domains completed successfully" else ${logCritical} "FAILURE: backup completed with some failures" fi exit ${globalBackupResult}