Tech Blog

I've seen a few XenServer backup scripts around but I decided none were exactly what I was looking for so I put one together myself.  My version has several configuration options including logging and compression.  It saves the images to an NFS share.

I played around with several different options for compression.  One option was to use the native compression available with "xe vm-export compress=true" but I didn't choose to go that route.  The primary reason is compression is resource intensive.  If you have a lot of virtual machines or some that are very large, compression can put a strain on system resources causing performance issues.  Therefore, I have compression turned off on most systems where I use this script with the exception of those that have a small "footprint".  On those systems, I can turn on compression with little additional overhead.

Without compression, your storage needs will be greater.  To compensate, I schedule the following one-liner to run directly on the server where my backups are stored:

find /opt/backups/VMs/ -type f ! -name '*.gz' -exec gzip "{}" \;

That basically says to find everything in the /opt/backups/VMs directory that doesn't already have a .gz exentions and compress it thereby giving it a .gz extension.  By following this method, I don't have to utilize resources for compression on the hypervisor that is running the script.  Instead, they are compressed directly on the backup system.  You should test this script with compress on and off to see what works best with you.  Furthermore, as with all my scripts, I take no responsibility for the fitness of this script.  It may work for you or it may not!  Please test thouroughly!   

#!/bin/bash
###########################################

### v1.2.0 ###
### By Kyle Harris
### http://www.nashville-linux-guy.com
### Backup XenServer to NFS
### Thanks/credit go to many examples referenced
### in the writing of this script too numerous
### to mention.

### This script comes with no warranty of any kind.
### I assume no liabliity either express or implied.
### Use it at your own risk. Please test first with
### non-critical data.

###########################################
STORE_BY="SERVER_NAME" # either SERVER_NAME or POOL_NAME
TMP_UUID_FILE=/tmp/xen-uuids.txt
NFS_SERVER_IP="192.168.1.200"
MOUNTPOINT="/xenmnt_daily"
FILE_LOCATION_ON_NFS="/opt/backups/VMs/daily"
COMPRESS="YES" # either YES to compress or NO
DAYS_TO_KEEP="7"
LOG="/var/log/XenServer-Backup_daily.log"
###########################################

DATE=$(date +%m-%d-%Y)
shopt -s nocasematch
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
SECONDS=0
echo "Backup:: Script Start -- $(date +%Y%m%d_%H%M)" >> $LOG

if [[ $COMPRESS = "YES" ]]; then
echo "Compression is ON" >> $LOG
elif [[ $COMPRESS = "NO" ]]; then
echo "Compression is OFF" >> $LOG
else
echo "$COMPRESS not set correctly!\n"
fi

if [[ $STORE_BY = "SERVER_NAME" ]]; then
DIR=$(echo $HOSTNAME)
elif [[ $STORE_BY = "POOL_NAME" ]]; then
DIR=$(xe pool-list params=name-label --minimal)
else
echo "$STORE_BY not set correctly!\n"
exit 1
fi

[[ -d $MOUNTPOINT ]] || mkdir -p $MOUNTPOINT
mount -t nfs $NFS_SERVER_IP:$FILE_LOCATION_ON_NFS $MOUNTPOINT
if [ ! $? -eq 0 ]; then
echo "Mount command failed!\n"
exit 2
fi

BACKUPPATH=$MOUNTPOINT/$DIR/$DATE
mkdir -p $BACKUPPATH
if [ ! -d $BACKUPPATH ]; then
echo "$BACKUPPATH not found!\n"
umount $MOUNTPOINT
exit 3
fi

xe vm-list is-control-domain=false is-a-snapshot=false | grep uuid | cut -d":" -f2 > $TMP_UUID_FILE

if [ ! -f $TMP_UUID_FILE ]; then
echo "$TMP_UUID_FILE not found!\n"
umount $MOUNTPOINT
exit 4
fi

while read VMUUID
do
VMNAME=$(xe vm-list uuid=$VMUUID | grep name-label | cut -d":" -f2 | sed 's/^ *//g')
echo "$VMNAME" >> $LOG
SNAPUUID=$(xe vm-snapshot uuid=$VMUUID new-name-label="SNAPSHOT-$VMUUID-$DATE")
xe template-param-set is-a-template=false ha-always-run=false uuid=$SNAPUUID
xe vm-export vm=$SNAPUUID filename="$BACKUPPATH/$VMNAME-$DATE.xva"
if [[ $COMPRESS = "YES" ]]; then
gzip $BACKUPPATH/$VMNAME-$DATE.xva
fi
xe vm-uninstall uuid=$SNAPUUID force=true
done < $TMP_UUID_FILE

find $MOUNTPOINT -mtime +$DAYS_TO_KEEP -type f -print -delete >> $LOG
find $MOUNTPOINT -type d -empty -print -delete >> $LOG
umount $MOUNTPOINT

echo "Backup :: Script End -- $(date +%Y%m%d_%H%M)" >> $LOG
echo "Elapsed Time :: $(($SECONDS / 3600))h:$((($SECONDS / 60) % 60))m:$(($SECONDS % 60))s" >> $LOG 

If you would rather download it, you can do so here.  Thanks for reading!

- Kyle H.