430-backup-vm
#!/bin/bash
#
# Backup VM
#
if [ $# -ne 1 ]; then
  echo "Usage for $0 : VM Name required as argument."
  exit 1
fi
#
if [ $UID -eq 0 ]; then
  echo "This script should NOT be run as root."
  exit 1
fi
#
# Source config file
if [ ! -f ~/.vbconfig ]; then
    echo "Config File ~/.vbconfig not found!"
    echo "Run: set-folders to create .vbconfig"
    exit 1
fi
. ~/.vbconfig
#
VB_VM_NAME=$1
#
# Configuration parameters
#
# Backup Type
# offline first takes the VM to savestate before backup
# online does a live backup
# offline is faster, safer but has slight downtime
# online is slower but keeps system live
#VB_VM_BACKUP_TYPE="online" # Or offline
VB_VM_BACKUP_TYPE="offline"
#
# Fix number of levels of Backup
# 2 Levels are minimal and sufficient
# 3 is Optimal but you can have as many as desired
VB_VM_MAX_LEVEL=3
#
# Indicate snapshot name prefix.
# Create multiple sets by changing LEVELNAME
LEVELNAME="Level"
#
VB_VM_BUSY_CHK="Y" # or N
#
# Begin Backup processing
#
VM_POWERED_ON=`VBoxManage list runningvms|grep -c '^"'${VB_VM_NAME}'"'`
if [ $VM_POWERED_ON -eq 0 ]; then
  echo "Non-running VMs don't require snapshot for backups. Exiting."
  exit
fi
#
# Check if VM is idle or busy
if [[ $VB_VM_BUSY_CHK == "Y" ]]; then
  VM_STATE=`$VB_BIN_LOC/400-busy-chk-vm.bash $VB_VM_NAME`
  if [[ $VM_STATE == "busy" ]]; then
    echo "VM is busy. Not doing shapshot. Exiting."
    exit 1
  fi
fi
#   
# Backup Day of Week, Date, Moth
# Day of Week (e.g. Sunday)
VB_VM_BK_DOW="Sunday"
# Date of the Month (e.g. 16th)
VB_VM_BK_DOM="16"
# Date and Month of the Year (e.g. 25th, November)
VB_VM_BK_DOY="25"
VB_VM_BK_MOY="11"
#
if [ $VB_VM_BK_DOM -eq $VB_VM_BK_DOY ]; then
  echo "Warning:"
  echo "Montly backup date is same as Yearly date"
  echo "Avoid the same date to NOT skip one"
  echo "Monthly backup each year"
fi
#
# Start with the smallest incremental backup
#
# For daily backups
VB_VM_BACKUP_LEVEL=$VB_VM_MAX_LEVEL
#
# For weekly backups (Sunday)
dayofweek=`date +"%A"`
if [[ $VB_VM_MAX_LEVEL -gt 1 ]]; then
  if [[ $dayofweek == $VB_VM_BK_DOW ]]; then
    let VB_VM_BACKUP_LEVEL=$VB_VM_MAX_LEVEL-1
  fi
fi
#
# For monthly backups (16th day)
daynumber=`date +"%d"`
if [[ $VB_VM_MAX_LEVEL -gt 2 ]]; then
  if [[ $daynumber == $VB_VM_BK_DOM ]]; then
    let VB_VM_BACKUP_LEVEL=$VB_VM_MAX_LEVEL-2
  fi
fi
 
# For yearly backups (Nov 25th)
# Annual will only kick in if we have VB_VM_MAX_LEVEL > 3
if [[ $VB_VM_MAX_LEVEL -gt 3 ]]; then
  if [[ $daynumber == $VB_VM_BK_DOY ]]; then
    monthnumber=`date +"%m"`
    if [[ $monthnumber == $VB_VM_BK_MOY ]]; then
      let VB_VM_BACKUP_LEVEL=$VB_VM_MAX_LEVEL-3
    fi
  fi
fi
 
# Check to make sure VB_VM_MAX_LEVEL is not set too low
# Alternatively remove higher level options (yearly)
if [ $VB_VM_BACKUP_LEVEL -lt 1 ]; then
  echo "Config error. Inc Max Level"
  exit 1
fi
#
# Check current Snapshot Level
# There should always be all level from 1 to VB_VM_MAX_LEVEL
CUR_SS=`VBoxManage snapshot "${VB_VM_NAME}" list --machinereadable \
 | grep CurrentSnapshotName|cut -d"\"" -f2`
MAX_SS=${LEVELNAME}_${VB_VM_MAX_LEVEL}
if [[ $MAX_SS != $CUR_SS ]]; then
  # Max Level does not exit in SS. This may be the 1st run, so reset
  echo "Detecting 1st run ..."
  VB_VM_BACKUP_LEVEL=1
else
  echo "At Level $VB_VM_BACKUP_LEVEL / $VB_VM_MAX_LEVEL"
fi
#
# Save State
if [[ $VB_VM_BACKUP_TYPE == "offline" ]]; then
  echo "Sending VM to savestate ..."
  VBoxManage controlvm "${VB_VM_NAME}" savestate
fi
#
echo "Deleting (Merging) Old snapshots ..."
for i in $(eval echo "{${VB_VM_MAX_LEVEL}..${VB_VM_BACKUP_LEVEL}..-1}")
do
  SS_NAME=${LEVELNAME}_${i}
  # Ignore errors with delete on use the redirect to prevent display
  #VBoxManage snapshot "${VB_VM_NAME}" delete $SS_NAME 2> /dev/null
  SS_EXISTS=`VBoxManage snapshot "${VB_VM_NAME}" list|grep -c "Name: ${SS_NAME} "`
  if [ $SS_EXISTS -gt 0 ]; then
    echo "Deleting snapshot $SS_NAME ..."
    VBoxManage snapshot "${VB_VM_NAME}" delete $SS_NAME
  else
    echo "Snapshot $SS_NAME to be deleted does not exist!"
  fi
done
#
echo "Taking New snapshots ..."
for i in $(eval echo "{${VB_VM_BACKUP_LEVEL}..${VB_VM_MAX_LEVEL}}")
do
  SS_NAME=${LEVELNAME}_${i}
  echo "Taking snapshot $SS_NAME ..."
  VBoxManage snapshot "${VB_VM_NAME}" take $SS_NAME --live
done
#
if [[ $VB_VM_BACKUP_TYPE == "offline" ]]; then
  echo "Powering VM back on ..."
  VBoxManage startvm  "${VB_VM_NAME}" --type headless
fi
#
exit