#!/bin/bash

if [ -z "$1" ]; then
  echo "Usage: $0 [-d] [-u] guest_name [partitions ...]"
  echo "Example: $0 --stop --start www_server p1 p2"
  exit
fi

TGT="ftp://vs@backupserver/ImageBackup"

compress="pigz -c"; cext=".gz" # fastest compression
#compress="gzip -c -1"; cext=".gz"
#compress="xz -c -3 -T 24"; cext=".xz"
#compress="bzip2 -c"; cext=".bz2"
#compress="lbzip2 -c"; cext=".bz2"
#compress="cat"; cext="" # no compression

checksum="md5sum"
#checksum="md5deep"

date=`date +%Y%m%d`

# override parameters from configuration file
[ -f /etc/sysconfig/virt-backup ] && . /etc/sysconfig/virt-backup

drop_cache() {
  echo 3 > /proc/sys/vm/drop_caches
}

cycle_dropping_buffers() {
  # drop buffers in intervals to avoid swapping
  trap "break" USR1 TERM
  while drop_cache; do
    sleep 1m
  done
}

# check for utilities
check_bin() {
  if [ ! -x /usr/bin/$1 ]; then
    if [ ! -x /bin/$1 ]; then
      echo "ERROR: missing command: $1"
      exit 101
    fi
  fi
}
check_bin pv
check_bin $compress
check_bin $checksum
check_bin mktemp

check_guest_state() {
  # exit if domain is already running
  if [ "`virsh domstate $1`" = "running" ]; then
    echo "ERROR: Domain is already running!"
    if [ "$FORCE_BACKUP" = "yes" ]; then
      exit 100
    fi
  fi
}

backup_disk() {
  # backup_disk /dev/mapper/diskname [size]
  name=`basename $1`
  cycle_dropping_buffers & # run on background
  DROPPER_PID=$!
  echo "Starting backup for $1 ..."
  chksum_file=`mktemp /tmp/virt-backup-md5.XXXXXXXX`
  if [ "$2" ]; then
    # pv for CentOS 6 does not accept --stop-at-size parameter
    dd if=$1 bs=$2 count=1
  else
    pv $1
  fi | \
    tee >($checksum > $chksum_file) | \
    $compress | \
    curl -s -S -T - -n $TGT/$guest/${name}_${date}.img$cext
  kill -USR1 $DROPPER_PID
  drop_cache
  cat $chksum_file | curl -s -S -T - -n s$TGT/$guest/${name}_${date}.md5
  rm -f $chksum_file
}

backup_guest() {
  guest=$1
  shift

  # create directory
  curl -s -n \
    -Q "mkdir ImageBackup/$guest" \
    -Q "chmod 0770 ImageBackup/$guest" \
    s$TGT #2>/dev/null

  # upload guest configuration
  virsh dumpxml $guest | curl -s -S -T - -n $TGT/$guest/${guest}_${date}.xml

  disks=`virsh-xpath --guest $guest --query '//devices/disk/source/@dev' | cut -d'"' -f2`
  # send files
  for disk in $disks; do
    if [ "$1" ]; then
      backup_disk $disk 512 # backup boot sector and partition table
      for part in "$@"; do
        backup_disk "${disk}${part}"
      done
    else
      backup_disk $disk
    fi
  done
}

while getopts "udf" opt; do
  case "$opt" in
    u) 	POST_START=yes ;;
    d) 	PRE_STOP=yes ;;
    f)	FORCE_BACKUP=yes ;;
    \?)	exit 1 ;;
  esac  
done
shift $((OPTIND-1))

if [ "$PRE_STOP" = "yes" ]; then
  unset FORCE_BACKUP
  virsh shutdown $1
  virsh console $1
fi

check_guest_state "$1"
backup_guest "$@"

if [ "$POST_START" = "yes" ]; then
  virsh start $1
fi
