Skip to content

Instantly share code, notes, and snippets.

@reinistihovs
Forked from cabal95/vm-backup.sh
Last active April 23, 2020 00:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save reinistihovs/1e43b5dea2142dfdc2e6ac6e45f5dc6a to your computer and use it in GitHub Desktop.
Save reinistihovs/1e43b5dea2142dfdc2e6ac6e45f5dc6a to your computer and use it in GitHub Desktop.
I use this script to backup my QEMU/KVM/libVirt virtual machines. The script requires KVM 2.1+ since it uses the live blockcommit mode. This means the data in the snapshot disk is rolled back into the original instead of the other way around. Script does NOT handle spaces in paths.
#!/bin/bash
#To exclude a domain, please add to its name "nobackup"
#First shutdown the guest, then use this command: virsh domrename oldname newname.
DATE=`date +%Y-%m-%d.%H:%M:%S`
LOG=/var/log/kvm-backup.$DATE.LOG
BACKUPROOT=/backup
DOMAINS=$(virsh list --all | tail -n +3 | awk '{print $2}')
for DOMAIN in $DOMAINS; do
echo "-----------WORKER START $DOMAIN-----------" > $LOG
echo "Starting backup for $DOMAIN on $(date +'%d-%m-%Y %H:%M:%S')" >> $LOG
if [[ $DOMAIN == *"nobackup"* ]];then
echo "Skipping $DOMAIN , because its excluded." > $LOG
exit 1
fi
VMSTATE=`virsh list --all | grep $DOMAIN | awk '{print $3}'`
if [[ $VMSTATE != "running" ]]; then
exit 1
fi
BACKUPFOLDER=$BACKUPROOT/KVM-BACKUPS/$DOMAIN
mkdir -p $BACKUPFOLDER
TARGETS=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $3}')
IMAGES=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $4}')
DISKSPEC=""
for TARGET in $TARGETS; do
DISKSPEC="$DISKSPEC --diskspec $TARGET,snapshot=external"
done
virsh snapshot-create-as --domain $DOMAIN --name "backup-$DOMAIN" --no-metadata --atomic --disk-only $DISKSPEC >> $LOG
if [ $? -ne 0 ]; then
echo "Failed to create snapshot for $DOMAIN" > $LOG
exit 1
fi
for IMAGE in $IMAGES; do
NAME=$(basename $IMAGE)
if test -f "$BACKUPFOLDER/$NAME"; then
echo "Backup exists, merging only changes to image" > $LOG
rsync -apvz --inplace $IMAGE $BACKUPFOLDER/$NAME >> $LOG
else
echo "Backup does not exist, creating a full sparse copy" > $LOG
rsync -apvz --sparse $IMAGE $BACKUPFOLDER/$NAME >> $LOG
fi
done
BACKUPIMAGES=$(virsh domblklist $DOMAIN --details | grep disk | awk '{print $4}')
for TARGET in $TARGETS; do
virsh blockcommit $DOMAIN $TARGET --active --pivot >> $LOG
if [ $? -ne 0 ]; then
echo "Could not merge changes for disk of $TARGET of $DOMAIN. VM may be in invalid state." > $LOG
exit 1
fi
done
for BACKUP in $BACKUPIMAGES; do
if [[ $BACKUP == *"backup-"* ]];then
echo "deleted temporary image $BACKUP" > $LOG
rm -f $BACKUP
fi
done
virsh dumpxml $DOMAIN > $BACKUPFOLDER/$DOMAIN.xml
echo "-----------WORKER END $DOMAIN-----------" >> $LOG
echo "Finished backup of $DOMAIN at $(date +'%d-%m-%Y %H:%M:%S')" >> $LOG
done
exit 0
@reinistihovs
Copy link
Author

My version uses rsync --sparse and --inplace options, to make the backup proccess 10x faster, only the changes in the qcow2 files are copied and updated to backup destination.
Also it is possible to exclude a guest by changing its name with -nobackup in the end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment