#!/bin/bash # # Libvirt VMs backup script # Upload gziped images of VMs volumes to remote server using SSH # Public key authentication is required # Images stored on daily and weekly (each sunday) basis # # Written by Andrii Salnikov aka manf@grid.org.ua # # configuration SE_SSH="cluster.univ@se.imbg.org.ua" SE_PATH="/mnt/se1/backups/cluster.univ.kiev.ua/vm-images/" EXCLUDE_LIST=( '^h7091.*' ) #TODO: handle turned off VMs # functions get_all_vms () { VMS=( $(virsh list --all | sed '$d' | sed -n '3,$p' | awk '{print $2}') ) } check_vm_excluded () { local vm=$1 for exclude in "${EXCLUDE_LIST[@]}"; do echo $vm | grep -q -e "$exclude" [ $? -eq 0 ] && return 1 done return 0 } get_vm_disks () { local vm=$1 virsh domblklist $vm | sed -n 's/[vsh]d[a-z]\s\+\(\/.*\)/\1/p' } check_lvm () { local path=$1 lvs $path > /dev/null 2>&1 return $? } get_lvm_snapshot () { local path=$1 local devname=${path##*/} lvcreate -s -L 1G -n "${devname}-snapshot" $path > /dev/null if [ $? -ne 0 -a $? -ne 5 ]; then echo "ERROR: Failed to create LVM snapshot for: $path" return 1 fi echo "${path%/*}/${devname}-snapshot" return 0 } remove_lvm_snapshot () { local path=$1 sync lvremove -f $path } get_backup_file () { local day_of_week=$( date +"%w" ) # 0-6 local day_of_month=$( date +"%_d" ) # 1-... local week_of_month=$(( ($day_of_month-1)/7 )) [ $week_of_month -eq 4 ] && week_of_month=3 if [ $day_of_week -eq 0 ]; then echo "weekly/w${week_of_month}-" else echo "daily/d${day_of_week}-" fi } make_remotedirs () { ssh $SE_SSH " mkdir -p $SE_PATH/{daily,weekly}" } do_backup () { local disk=$1 local filename=${disk##*/} filename=${filename%-snapshot} local filepath="$( get_backup_file )${filename}.img.gz" make_remotedirs echo "INFO: Backing up $disk to $filepath" dd if=$disk | gzip | ssh $SE_SSH " cat > $SE_PATH/$filepath" } do_xml_backup () { local vmname=$1 local filepath="$( get_backup_file )${vmname}-config.xml.gz" virsh dumpxml $vmname | gzip | ssh $SE_SSH " cat > $SE_PATH/$filepath" } # main cycle get_all_vms for vm in "${VMS[@]}"; do echo "INFO: Processing VM $vm" check_vm_excluded $vm if [ $? -ne 0 ]; then echo "NOTICE: VM $vm has excluded in the configuration" continue fi do_xml_backup $vm vmdisks=$( get_vm_disks $vm ) for disk in $vmdisks; do echo "INFO: Found disk $disk for VM $vm" check_lvm $disk if [ $? -ne 0 ]; then echo "ERROR: VM $vm disk $disk is not LVM volume. Non-LVM volumes is not supported yet." continue 2 else echo "INFO: Creating LVM snapshot for disk $disk" disk_snap=$( get_lvm_snapshot $disk ) [ $? -ne 0 ] && continue do_backup $disk_snap remove_lvm_snapshot $disk_snap fi done done