Sunday, October 14, 2018

Backup running VirtualBox machine (with only a tiny interupt)!

There is a lot to be said about backups. I won't even try to cover all bases. I'll just show you what I'm using, that works for me. Yes.

In a nutshel this is what happens
  • some sanity checks and destination creation.
  • copy xml-config for chosen VM.
  • create snapshot for chosen VM so that original vdi-file is released.
    • will pause running VM for 1-2s. There is a --live switch for the snapshot command but I have not tested this.
  • copy original vdi-file to backup destination.
  • remove snapshot thus "merging" the changes back into the running vdi.
  • ???
  • profit!

Input is name of VM-machine and index of backup.
Output is a backup of your machine ... who'da thunk it.

The usage of rsync is mostly cause of access to --progress but this could be substituted for simple "cp" or similar.

Update 1: Now using --live switch for the snapshot command. Without it one guest with USB attached kept getting Aborted when snapshot was taken. With --live it's working again.
Update 2: The --live switch was not the issue :(. Apparently snapshots does not work with anything lower than the USB 3.0 controller. This controller does however lose the attached devices when you snapshot the guest instead. Bah. Workaround is a detach and re-attach of the device to the affected guest. In my case this is not a problem but if you are attaching a disk drive or something that the guest is using constantly this might lead to bad stuff. Take care.
Update 3: So the UUID changes with every boot and every physical reattach of the device, how very practical. Well, let's fix that.

That's it. It works for me, ymmw. :)

#!/bin/bash

if (( "$#" < 2 )); then
  echo "Usage: `basename $0` VM_NAME BACKUP_INDEX"
  exit 65
fi

# using rsync for progress while copying
# not using deltas (-W) for vdi/vmdk as it tends to break
copycmd="rsync -ahW --progress"

dest="/mnt/vmbackup/$1/$2"

# check if VM exists
vboxmanage showvminfo "$1" &> /dev/null || { echo "OOPS: VM not found"; exit 1; }

# change dir
# (change this if using non default)
cd ~/"VirtualBox VMs"/ || { echo "OOPS: directory not found, check script"; exit 1; }

# create directory at destination with index
mkdir -p $dest

echo "[START] Backup of $1 is GO!"
echo
echo "[i] copy $1/*.vbox to backup ..."
$copycmd "$1/"*.vbox* "$dest"
$copycmd -R "$1/./Logs/" "$dest"
sleep 1
echo

echo "[i] create snapshot of $1"
vboxmanage snapshot "$1" take backup --live
sleep 1
echo

# reattaching USB-device, it gets lost on snapshot for some reason.
# find device_guid with vboxmanage list usbhost.
if [[ "$1" == "your_special_usb_needing_guest_name" ]]; then
  # this is not pretty but it works.
  fluff=$(vboxmanage list usbhost | grep "0x1781" -B 1 | head -n 1 | sed -e 's/^UUID:\s*//')
  echo "[i] doing special stuff to host $1 ..."
  vboxmanage controlvm "$1" usbdetach $fluff
  sleep 1
  vboxmanage controlvm "$1" usbattach $fluff
  sleep 1
  echo
fi

echo "[i] copy $1/*.vdi and $1/*.vmdk to backup ..."
$copycmd "$1/"*.vdi "$dest"  2> /dev/null
$copycmd "$1/"*.vmdk "$dest" 2> /dev/null
sleep 1
echo

# "merge" the changes that has been made since we snapshotted back into running VM.
# yes, delete is sort of merge, look it up ... it's complicated.
echo "[i] \"merging\" snapshot back into $1 ..."
vboxmanage snapshot "$1" delete backup
sleep 1
echo

echo "[END] ^_^"
exit 0