#!/bin/bash helpFunction() { echo "" echo "Usage: $0 -v VmName -t TargetHost -d TargetDataset -l LIMIT" echo -e "\t-v Name of the vm to backup" echo -e "\t-t Backup target host name" echo -e "\t-s VM source zfs dataset" echo -e "\t-d Backup target zfs dataset" echo -e "\t-l Limit Bandwith" exit 1 # Exit script after printing help } while getopts "v:t:s:d:l:" opt do case "$opt" in v ) VM="$OPTARG" ;; t ) DESTHOST="$OPTARG" ;; s ) SRCZFS="$OPTARG" ;; d ) DESTZFS="$OPTARG" ;; l ) LIMIT="$OPTARG" ;; ? ) helpFunction ;; # Print helpFunction in case parameter is non-existent esac done # Print helpFunction in case parameters are empty if [ -z "$VM" ] || [ -z "$DESTHOST" ] || [ -z "$DESTZFS" ] || [ -z "$SRCZFS" ] || [ -z "$LIMIT" ] then echo "Some or all of the parameters are empty"; helpFunction fi echo "$(date +%Y-%m-%d_%H:%M:%S): Starting Backup of $VM" DAYS=$(zfs get backup:days -H -o value $SRCZFS) if [[ $DAYS == "-" ]] then DAYS=7 fi echo "$(date +%Y-%m-%d_%H:%M:%S): Keeping backups for $DAYS days" DAYS=$(($DAYS + 1)) DATE=$(date "+%Y%m%d%H%M") DATEOLD=$(date -d -"$DAYS"day "+%Y%m%d") SNAPNAME="backup_$DATE" abortFunction() { zfs set backup:success="false" $SRCZFS zfs set backup:failed="$DATE" $SRCZFS exit 1 } VMDISKS=$(virsh domblklist $VM | egrep -o vd.) DISKSPEC="" for disk in $VMDISKS do DISKSPEC+="--diskspec $disk,file=/srv/snapshots/$VM/$VM-$disk-$SNAPNAME.qcow2,snapshot=external " done # create snapshot dataset zfs create tank/snapshots/$VM # create virsh xml-dump of vm virsh dumpxml $VM > /srv/vms/$VM/vm-$VM.xml echo "$(date +%Y-%m-%d_%H:%M:%S): Create VM snapshot of $VM" virsh snapshot-create-as --domain $VM --name $SNAPNAME --quiesce --atomic --disk-only $DISKSPEC >/dev/null 2>&1 if [ $? -ne 0 ] then virsh snapshot-create-as --domain $VM --name $SNAPNAME --disk-only $DISKSPEC >/dev/null 2>&1 if [ $? -ne 0 ] then echo "VM snapshot creation failed" >&2 abortFunction else echo "$(date +%Y-%m-%d_%H:%M:%S): Snapshot created without --quiesce" fi fi echo "$(date +%Y-%m-%d_%H:%M:%S): Creating ZFS snapshot $SRCZFS@$SNAPNAME" zfs snapshot "$SRCZFS"@"$SNAPNAME" if [ $? -ne 0 ] then echo "ZFS snapshot creation failed" >&2 abortFunction fi echo "$(date +%Y-%m-%d_%H:%M:%S): Deleting vm disk snapshots" for disk in $VMDISKS do virsh blockcommit $VM $disk --pivot --active >/dev/null done VMDISKS=$(virsh domblklist $VM | egrep vd.) for disk in $VMDISKS do if [[ $disk == *"snapshot"* ]] then echo "Snapshot deletion failed for $disk" >&2 abortFunction fi done virsh snapshot-delete $VM $SNAPNAME --metadata > /dev/null SUCCESS=$(zfs get backup:success -H -o value $SRCZFS) LASTBACKUP=$(zfs get backup:date -H -o value $SRCZFS) if [[ $SUCCESS == "true" ]] then LASTSNAP="backup_$LASTBACKUP" echo "$(date +%Y-%m-%d_%H:%M:%S): Sending incremental snapshot from $LASTSNAP" ssh $DESTHOST sudo zfs rollback "$DESTZFS"@"$LASTSNAP" zfs send -R -i "$SRCZFS"@"$LASTSNAP" "$SRCZFS"@"$SNAPNAME" | pv -L $LIMIT | ssh $DESTHOST sudo zfs recv $DESTZFS else if [[ $LASTBACKUP != "-" ]] then LASTSNAP="backup_$LASTBACKUP" echo "$(date +%Y-%m-%d_%H:%M:%S): Trying to send incremental snapshot from $LASTSNAP" ssh $DESTHOST sudo zfs rollback "$DESTZFS"@"$LASTSNAP" zfs send -R -i "$SRCZFS"@"$LASTSNAP" "$SRCZFS"@"$SNAPNAME" | pv -L $LIMIT | ssh $DESTHOST sudo zfs recv $DESTZFS else echo "$(date +%Y-%m-%d_%H:%M:%S): Sending full snapshot" zfs send -R "$SRCZFS"@"$SNAPNAME" | pv -L $LIMIT | ssh $DESTHOST sudo zfs recv -F $DESTZFS fi fi if [ $? -eq 0 ] then zfs set backup:success="true" $SRCZFS zfs set backup:date="$DATE" $SRCZFS zfs set backup:failed="-" $SRCZFS if [ -n $LASTSNAP ] then echo "$(date +%Y-%m-%d_%H:%M:%S): Deleting old ZFS snapshot $LASTSNAP" zfs destroy "$SRCZFS"@"$LASTSNAP" fi else abortFunction fi zfs destroy tank/snapshots/$VM # cleanup old backups OLDSNAPS=$(ssh $DESTHOST zfs list -r -t snapshot -o name $DESTZFS 2>/dev/null | grep $DATEOLD) if [ ${#OLDSNAPS} -gt 0 ] then for oldsnap in $OLDSNAPS do echo "$(date +%Y-%m-%d_%H:%M:%S): Deleting old snapshots on target" ssh $DESTHOST sudo zfs destroy $oldsnap done else echo "$(date +%Y-%m-%d_%H:%M:%S): No old snapshots to cleanup on target" fi echo "$(date +%Y-%m-%d_%H:%M:%S): Backup Completed"