Quick and dirty script for making cold backups of KVM Virtual Machines

Note that this depends on “virsh”, the libvirt client, and “bzip2”.

Basically, what this script does is send a shutdown signal to each of the specified nodes, back up the disk images, and then bzip them in the background while it restarts the node. So having said this, you’ll want to make sure your services on your VM’s are set to shut down and start up gracefully when the OS is shut down / restarted.



#!/bin/bash

#
# This is who we'll back up
#
machines="Dev Test Foobar"

#
# Keep the house clean
#
days_to_keep="7"

#
# Store backups here. NFS to another machine would make sense. 
# so that your backups are not local.
#
backup_dir="/VirtualMachines/KVM/backups"

#
# Logs go here.
#
log_dir="/VirtualMachines/KVM/backups/logs"

#
# End parameters
#
#=================================================================

#
# Timestamp for the log file
#
right_now=`date '+%m%d%Y_%H%M%p'`

exec 1>/${log_dir}/backup_vms.${right_now}.log 2>&1

print_date() {
   date '+%m%d%Y_%H%M%p'
}

for machine in $machines
do
   if [[ ! -d ${backup_dir}/${machine} ]];
   then
      mkdir -p ${backup_dir}/${machine}
   fi

   echo "Backing up VM configuration"
   virsh dumpxml $machine > ${backup_dir}/${machine}/${machine}.xml

   echo "Sending shutdown signal to $machine"
   virsh shutdown $machine
   echo "   Return code: $?"
   
   echo -n "Waiting for machine to shut down "
   for i in 1 2 3 4 5
   do
      echo -n "."
      virsh list | grep -v "^$" | grep -v "^ Id" | grep -v "\-\-\-\-\-" | awk '{print $2" "$3}' | grep $machine | while read name state
      do
         if [[ $state -eq "running" ]]
         then
            sleep 60
         fi
      done
   done

   echo "Copying disk(s)"
   virsh domblklist $machine | grep -v "^$" | grep -v "^Target" | grep -v "\-\-\-\-\-" | awk '{print $2}' | while read disk
   do
      echo "   $disk ..."
      copy_disk="${backup_dir}/${machine}/`basename ${disk}`.`print_date`"
      echo "   Copying $disk to $copy_disk"
      fuser $disk 1>/dev/null 2>&1
      if (( $? == 0 ))
      then
         echo "   Disk $disk is still in use! "
      else
         echo "   Copy started at `print_date`"
         cp $disk $copy_disk
         echo "   Return code: $?"
         echo "   Copy ended at `print_date`"
         echo "   Backgrounding bzip of $copy_disk"
         nohup bzip2 $copy_disk &
      fi
   done

   echo "Starting machine $machine"
   virsh start $machine
   echo "   Return code: $?"
   echo

done

   echo "Removing old backups."
   find $backup_dir -type f -mtime +$days_to_keep -ls
   find $backup_dir -type f -mtime +$days_to_keep -exec rm -f {} \;