Thursday, October 8, 2009

Damn Good XenServer backup script

I had a requirement to make a backup script to help a customer with image based backups to an attached USB drive. This is still really 1.0 as a lot of checks and balances have not been added... it just works. I used some logic found on the previous citrix developer sites and created my own that is a little more robust. I used BASH because my perl skills are not that good, yet.

For features we have and install script, we have install, backup, restore and create backup list. All obvious, read the script for details. All these are broken out into functions. The variables at the top can be changed to suite your environment. I have a whole document that describes everything you want to do with this from creating cron jobs and udev rules, etc...

I attach a device, whether it is block or even an NFS share, it should work either way. Format it with ext3 (not FAT32 like most usb drives cause we cant store files over 4GB there!). The script knows whether its 5.5 so it will use the snapshot method. If not it will halt the VM and export it then start it back up. When 6.0 comes out, if Citrix goes down the route, I will have to rework the logic a little bit. This is for 5.0 and 5.5 of XenServer.

Cut and Paste here:
#!/bin/sh

# XenServer vmbackup v0.3
# Author: Jeff O'Brien, CCA,RHCE
# ITPartners, LLC | Westbrook, ME
# 2009-Sept-24

LOGFILE="/var/log/vm-backup.log"
BACKUP_LIST="/root/vm-backup/vm-backup-list.txt"
BACKUP_DIR="/media/backup"
BACKUP_DEV="/dev/sdb1"
GET_AVAIL_BACKUP_SPACE="`df -h |grep backup|awk {'print $4'}`"
GET_VM_UUIDS="`xe vm-list |grep uuid|awk {'print $5'}`"
NUM_BKUP_2_KEEP="2"
DOM0_UUID="`xe vm-list |grep -B 1 Control |grep uuid |awk {'print $5'}`"


install(){
if [ ! -e /media/backup ]; then
mkdir /media/backup
fi


echo "VM-Backup: Please enter install location [/root/vm-backup]:"
read $INSTALLLOC

if [ "$INSTALLLOC" = "" ]; then
INSTALLLOC="/root/vm-backup"
else
INSTALLLOC="$INSTALLLOC"

fi

#Make install location
echo "INFO: Creating installation location in" $INSTALLLOC
mkdir -p $INSTALLLOC

#Copy files...
echo "INFO: Copying vm-backup file(s) to" $INSTALLLOC
mv vmbackup $INSTALLLOC/


touch /var/log/vm-backup.log


#Build log rotate file
LOGROTATEF=/etc/logrotate.d/vm-backup

touch $LOGROTATEF
echo "/var/log/vm-backup.log {" >> $LOGROTATEF
echo " size 20k" >> $LOGROTATEF
echo " notifempty" >> $LOGROTATEF
echo " create 0600 root root" >> $LOGROTATEF
echo "}" >> $LOGROTATEF
}




#Build backup file function
build_backup_list(){
#initiate array of uuids as a string... should be all elements
arrayUUID=("$GET_VM_UUIDS")

#Initiate a new array with the removed Domain 0
arrayUUIDfixed=( ${arrayUUID[@]//$DOM0_UUID/} )

echo ${arrayUUIDfixed[@]} > $BACKUP_LIST

}



#Backup Function
backup_images(){

if [ ! -e "$BACKUP_LIST" ]; then
echo "ALERT: Backup file doesn't exist, creating one now..." >> $LOGFILE
build_backup_list
fi

#Begin for loop to parse backup file of uuids to backup
for uuid in `cat $BACKUP_LIST` ;do

#Initiate a couple variables needed along the routine
VMNAME="`xe vm-param-get param-name=name-label uuid=$uuid`"
BKFILENAME="`date +%F`_`xe vm-param-get param-name=name-label uuid=$uuid`.xva"

############# STARTING LINE ######################
echo "" >> $LOGFILE
echo "++++++=============================================++++++" >> $LOGFILE
echo "Starting Job for " $VMNAME " at: " `date` >> $LOGFILE
####################################################

#Do we have a backup directory yet?
echo "INFO: Checking to see if backup directory exists" >> $LOGFILE
if [ -e $BACKUP_DIR/$VMNAME ]; then
echo "INFO: VM has backup directory already, skipping creation" >> $LOGFILE
else
mkdir $BACKUP_DIR/$VMNAME
fi

#Routine to find out how many backups exist if any
NUM_BKUPS="`ls $BACKUP_DIR/$VMNAME |wc -l`"

#How many backups are in that directory and does it meet retention policy, if so

remove the oldest
if [ "$NUM_BKUPS" -lt "$NUM_BKUP_2_KEEP" ]; then
echo "INFO: Number of backups is less than retention policy, no clean up

required" >> $LOGFILE
else
echo "INFO: Cleaning up old backups for " $VMNAME >> $LOGFILE
arrayBackupFiles=(`ls "$BACKUP_DIR/$VMNAME"`)
echo "INFO: " ${arrayBackupFiles[0]} "is the oldest backup and will be

DELETED" >> $LOGFILE
rm -rf $BACKUP_DIR/$VMNAME/${arrayBackupFiles[0]}
fi

GET_VERS="`cat /etc/redhat-release |grep -c 5.5`"

if [ "$GET_VERS" != 1 ]; then
echo "INFO: Stopping Machine - " $VMNAME
xe vm-shutdown vm=$uuid
echo "INFO: Stop command completed, waiting 10 secs just in case"
sleep 10s
echo "INFO: Running wait event for halted system state on " $VMNAME
xe event-wait class=vm uuid=$uuid power-state=halted
echo "INFO: Beginning export of " $VMNAME "to " $BACKUP_DIR
xe vm-export filename=$BACKUP_DIR/$VMNAME/$BKFILENAME vm=$uuid
echo "INFO: Starting " $VMNAME "backup complete"
xe vm-start uuid=$uuid
fi


if [ "$GET_VERS" = 1 ]; then
echo "INFO: SnapShotting Virtual machine " $VMNAME >> $LOGFILE
SNAPSHOT_UUID="`xe vm-snapshot vm=$VMNAME new-name-label="$VMNAME"_snap`"
xe template-param-set is-a-template=false ha-always-run=false

uuid=$SNAPSHOT_UUID
echo "INFO: Exporting Virtual Machine:" $VMNAME >> $LOGFILE
xe vm-export vm="$VMNAME"_snap filename=$BACKUP_DIR/$VMNAME/$BKFILENAME
echo "INFO: Export Complete, removing backup snapshot"
xe vm-uninstall uuid=$SNAPSHOT_UUID force=true
fi


############## FINISH LINE #####################
echo "Finishing Job for " $VMNAME " at: " `date` >> $LOGFILE
echo "++++++=============================================++++++" >> $LOGFILE
echo "" >> $LOGFILE
###################################################

done
}

#Test and if good run backups!
backup(){

MOUNT_TEST="`mount |grep $BACKUP_DEV`"
echo "======================================"
echo " xxxx xxxx eeeeeee nnn nnn"
echo " xx xx ee nn n nn"
echo " xxx eeeee nn n n"
echo " xx xx ee nn nnn"
echo " xxxx xxxx eeeeeee nnn nn"
echo "======================================"
echo "INFO: Backup jobs are starting, please be patient..."
echo "INFO: You may tail -f /var/log/vm-backup.log for details"

echo "INFO: Checking if backup is mounted..." >> $LOGFILE
if [ "$MOUNT_TEST" != "" ]; then
echo "SUCESS: Backup device is mounted" >> $LOGFILE
backup_images
else
echo "INFO: Mounting backup device" >> $LOGFILE
mount $BACKUP_DEV $BACKUP_DIR
if [ $? -eq "0" ]; then
echo "INFO: Mount completed successfully! Continuing" >> $LOGFILE
backup_images
else
echo "INFO: FAILURE to mount backup device exiting..." >> $LOGFILE
exit 1
fi
fi

}



restore(){

echo "RESTORE: Please select an image to restore:"
find $BACKUP_DIR -name *.xva
echo ""
echo -n "Image:"
read IMAGE


echo -n $IMAGE "was selected for restore, continue? [Y/N]:"
read YN

case "$YN" in

Y)
echo $IMAGE "Image is being restored, restoring..."
xe vm-import filename=$IMAGE
;;
y)
echo $IMAGE "Image is being restored, restoring..."
xe vm-import filename=$IMAGE
;;
N)
echo $IMAGE "is not being restored, exiting."
exit 1
;;
n)
echo $IMAGE "is not bein restored, exiting."
exit 1
;;
*)
echo "Uh ah, you didnt say the magic word."
exit 1

esac

}



case "$1" in
backup)
backup
;;

install)
install
;;
restore)
restore
;;
buildlist)
build_backup_list
;;
*)
echo "vmbackup help - v0.3 jobrien http://virtually-engineered.blogspot.com"
echo "Usage: vmbackup {install|buildlist|backup|restore}"
echo ""
echo "vmbackup is a simple script to assist in your backup routines"
echo "provided on an AS-IS basis"
echo "contact jobrien@itpartnersllc.com for issues"
echo ""
echo "install: installs vmbackup to the install location of your choice,"
echo " creates a logrotate entry and necessary files & folders."
echo ""
echo "buildlist: builds a backup list of UUIDs. This is all

inclusive."
echo ""
echo "backup: takes image based backups of VMs listed in

vm-backup-list.txt."
echo " built in retention policy of 2 images, oldest is removed."
echo " uses snapshots for 5.5 and halting for 5.0. Pick a window."
echo ""
echo "restore: will list all xva backup images in the backup directory."
echo " type in which file you want to restore and it will be done."
echo ""
echo "Please create your own crontab entry in accordance to your windows."
exit 1
esac

Thursday, September 24, 2009

Really ignored...

I've really ignored this blog-o-sphere since I created it back in 07. I will start using this to keep my ideas and scripts that are cool for everyones enjoyment. Not that anyone will read this. Maybe a few stopper-bys that I point over ;)...

So Im leaving this off as, I will put more stuff up here. I am working on a new XenServer backup script that has support for 5.0 and 5.5. Has some cool features. Not too bad for a few hours and a bash prompt. What is an RHCE to do in a windows world these days?