Last active
April 3, 2019 11:31
-
-
Save trick77/7ccaf520c595076e8ebb to your computer and use it in GitHub Desktop.
virt-backup.pl: fixed paths so live images will work again in Ubuntu 14.04
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/perl -w | |
# | |
# 1. Install required dependencies: | |
# sudo apt-get install -y libxml-simple-perl pv libsys-virt-perl | |
# 2. Run it like this (assuming the LVM disk size is 20G): | |
# /virt-backup.pl --vm mykvm --state --snapsize=20G --backupdir /tmp --debug --compress | |
# | |
# AUTHOR | |
# Daniel Berteaud <[email protected]> | |
# | |
# COPYRIGHT | |
# Copyright (C) 2009-2011 Daniel Berteaud | |
# | |
# This program is free software; you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation; either version 2 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program; if not, write to the Free Software | |
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
# | |
# This script allows you to backup Virtual Machines managed by libvirt. | |
# It has only be tested with KVM based VM | |
# This script will dump (or mount as a set of chunks): | |
# * each block devices | |
# * optionnally the memory (if --state flag is given) | |
# * the XML description of the VM | |
# | |
# These files are writen in a temporary backup dir. Everything is done | |
# in order to minimize donwtime of the guest. For example, it takes | |
# a snapshot of the block devices (if backed with LVM) so the guest is | |
# just paused for a couple of seconds. Once this is done, the guest is | |
# resumed, and the script starts to dump the snapshot. | |
# | |
# Once a backup is finished, you'll have several files in the backup | |
# directory. Let's take an example with a VM called my_vm which has | |
# two virtual disks: hda and hdb. You have passed the --state flag: | |
# * my_vm.lock: lock file to prevent another backup to run at the same time | |
# * my_vm.xml: this file is the XML description of the VM (for libvirt configuraiton) | |
# * my_vm_hda.img: this file is an image of the hda drive of the guest | |
# * my_vm_hdb.img: this file is an image of the hdb drive of the guest | |
# * my_vm.state: this is a dump of the memory (result of virsh save my_vm my_vm.state) | |
# | |
# This script was made to be ran with BackupPC pre/post commands. | |
# In pre-backup, you dump everything (or mount as a set of chunks), then, backuppc backups, | |
# compress, pools etc... the dumped file. Eventually, when the backup is finished | |
# The script is called with the --action=cleanup flag, which cleanups everything. | |
# (remove the temporary files, umount the fuse mount points if any etc.) | |
# | |
# Some examples: | |
# | |
# Backup the VM named mail01 and devsrv. Also dump the memory. | |
# Exclude any virtual disk attached as vdb or hdb and on the fly | |
# compress the dumped disks (uses gzip by default) | |
# virt-backup.pl --dump --vm=mail01,devsrv --state --exclude=vdb,hdb --compress | |
# | |
# Remove all the files related to mail01 VM in the backup directory | |
# virt-backup.pl --cleanup --vm=mail01 | |
# | |
# Backup devsrv, use 10G for LVM snapshots (if available), do not dump the memory | |
# (the guest will just be paused while we take a snapshot) | |
# Keep the lock file present after the dump | |
# virt-backup.pl --dump --vm=devsrv --snapsize=10G --keep-lock | |
# | |
# Backup devsrv, and disable LVM snapshots | |
# virt-backup.pl --dump --vm=devsrv --no-snapshot | |
# | |
# Backup mail01, and enable debug (verbose output) | |
# virt-backup.pl --dump --vm=mail01 --debug | |
# | |
# Don't dump, but mount as a set of chunks the disks of vm mail01 | |
# virt-backup.pl --action=chunkmount --vm=mail01 | |
# | |
# The idea here is to expose the big blocks/files which represent the VM disks | |
# as small chunks (default is 256kB), then, you can use your favorite backup script/software | |
# to backup /var/lib/libvirt/backup/vm_name/ where you want | |
# This lets you create incremential backups of VM disks, which can save | |
# a lot of space, a lot of bandwidth, and will also be much more efficient | |
# with rsync based backup scripts (because rsync doesn't handle huge files very well | |
# but if very efficient with a lot of small files) | |
# The cleanup routine (--cleanup or --action=cleanup) will unmount all | |
# the chunkfs mount points | |
# | |
### TODO: | |
# - If all the disks cannot be snapshoted, the VM should be locked so it cannot be accidentally started | |
# during a backup | |
# - Add snapshot (LVM) support for image based disk ? (should we detect the mount moint, and block device | |
# of the storage or let the user specify it with a --logical ?) | |
# - Additionnal check that the vm is available after a restore (via $dom->get_info->{status}, ping ?) | |
# - Check if compression utilies are available | |
# - Support per vm excludes in one run | |
# | |
### CHANGES | |
# * 31/07/2011 | |
# - Add new option to mount images as chunked files using chunkfs | |
# - Use 512k chunk size for LVM snapshots which should reduce performance penalty during backups | |
# | |
# * 26/03/2010 | |
# - Initial packaged version | |
use XML::Simple; | |
use Sys::Virt; | |
use Getopt::Long; | |
# Set umask | |
umask(022); | |
# Some constant | |
our %opts = (); | |
our @vms = (); | |
our @excludes = (); | |
our @disks = (); | |
# Sets some defaults values | |
# What to run. The default action is to dump | |
$opts{action} = 'dump'; | |
# Where backups will be stored. This directory must already exists | |
$opts{backupdir} = '/var/lib/libvirt/backup'; | |
# Size of LVM snapshots (which will be used to backup VM with minimum downtown | |
# if the VM store data directly on a LV) | |
$opts{snapsize} = '5G'; | |
# If we should also dump the VM state (dump the mémory, equivalent of virsh save) | |
$opts{state} = 0; | |
# Debug | |
$opts{debug} = 0; | |
# Let the lock file present after the dump is finisehd | |
$opts{keeplock} = 0; | |
# Should we try to create LVM snapshots during the dump ? | |
$opts{snapshot} = 1; | |
# Libvirt URI to connect to | |
$opts{connect} = "qemu:///system"; | |
# Compression used with the dump action (the compression is done on the fly) | |
$opts{compress} = 'none'; | |
# lvcreate path | |
$opts{lvcreate} = '/sbin/lvcreate -c 512'; | |
# lvremove path | |
$opts{lvremove} = '/sbin/lvremove'; | |
# chunkfs path | |
$opts{chunkfs} = '/usr/bin/chunkfs'; | |
# Size of chunks to use with chunkfs or or blocks with dd in bytes (default to 256kB) | |
$opts{blocksize} = '262144'; | |
# nice may be used to reduce CPU priority of compression processes | |
$opts{nice} = 'nice -n 19'; | |
# ionice may be used to reduce disk access priority of dump/chunkfs processes | |
# which can be quite I/O intensive. This only works if your storage | |
# uses the CFQ scheduler (which is the default on EL) | |
$opts{ionice} = 'ionice -c 2 -n 7'; | |
$opts{livebackup} = 1; | |
$opts{wasrunning} = 1; | |
# get command line arguments | |
GetOptions( | |
"debug" => \$opts{debug}, | |
"keep-lock" => \$opts{keeplock}, | |
"state" => \$opts{state}, | |
"snapsize=s" => \$opts{snapsize}, | |
"backupdir=s" => \$opts{backupdir}, | |
"vm=s" => \@vms, | |
"action=s" => \$opts{action}, | |
"cleanup" => \$opts{cleanup}, | |
"dump" => \$opts{dump}, | |
"unlock" => \$opts{unlock}, | |
"connect=s" => \$opts{connect}, | |
"snapshot!" => \$opts{snapshot}, | |
"compress:s" => \$opts{compress}, | |
"exclude=s" => \@excludes, | |
"blocksize=s" => \$opts{blocksize}, | |
"help" => \$opts{help} | |
); | |
# Set compression settings | |
if ($opts{compress} eq 'lzop'){ | |
$opts{compext} = ".lzo"; | |
$opts{compcmd} = "lzop -c"; | |
} | |
elsif ($opts{compress} eq 'bzip2'){ | |
$opts{compext} = ".bz2"; | |
$opts{compcmd} = "bzip2 -c"; | |
} | |
elsif ($opts{compress} eq 'pbzip2'){ | |
$opts{compext} = ".bz2"; | |
$opts{compcmd} = "pbzip2 -c"; | |
} | |
elsif ($opts{compress} eq 'xz'){ | |
$opts{compext} = ".xz"; | |
$opts{compcmd} = "xz -c"; | |
} | |
elsif ($opts{compress} eq 'lzip'){ | |
$opts{compext} = ".lz"; | |
$opts{compcmd} = "lzip -c"; | |
} | |
elsif ($opts{compress} eq 'plzip'){ | |
$opts{compext} = ".lz"; | |
$opts{compcmd} = "plzip -c"; | |
} | |
# Default is gzip | |
elsif (($opts{compress} eq 'gzip') || ($opts{compress} eq '')) { | |
$opts{compext} = ".gz"; | |
$opts{compcmd} = "gzip -c"; | |
} | |
else{ | |
$opts{compext} = ""; | |
$opts{compcmd} = "cat"; | |
} | |
# Allow comma separated multi-argument | |
@vms = split(/,/,join(',',@vms)); | |
@excludes = split(/,/,join(',',@excludes)); | |
# Backward compatible with --dump --cleanup --unlock | |
$opts{action} = 'dump' if ($opts{dump}); | |
$opts{action} = 'cleanup' if ($opts{cleanup}); | |
$opts{action} = 'unlock' if ($opts{unlock}); | |
# Stop here if we have no vm | |
# Or the help flag is present | |
if ((!@vms) || ($opts{help})){ | |
usage(); | |
exit 1; | |
} | |
if (! -d $opts{backupdir} ){ | |
print "$opts{backupdir} is not a valid directory\n"; | |
exit 1; | |
} | |
# Connect to libvirt | |
print "\n\nConnecting to libvirt daemon using $opts{connect} as URI\n" if ($opts{debug}); | |
our $libvirt = Sys::Virt->new( uri => $opts{connect} ) || | |
die "Error connecting to libvirt on URI: $opts{connect}"; | |
print "\n" if ($opts{debug}); | |
foreach our $vm (@vms){ | |
# Create a new object representing the VM | |
print "Checking $vm status\n\n" if ($opts{debug}); | |
our $dom = $libvirt->get_domain_by_name($vm) || | |
die "Error opening $vm object"; | |
our $backupdir = $opts{backupdir}.'/'.$vm; | |
if ($opts{action} eq 'cleanup'){ | |
print "Running cleanup routine for $vm\n\n" if ($opts{debug}); | |
run_cleanup(); | |
} | |
elsif ($opts{action} eq 'unlock'){ | |
print "Unlocking $vm\n\n" if ($opts{debug}); | |
unlock_vm(); | |
} | |
elsif ($opts{action} eq 'dump'){ | |
print "Running dump routine for $vm\n\n" if ($opts{debug}); | |
mkdir $backupdir || die $!; | |
mkdir $backupdir . '.meta' || die $!; | |
run_dump(); | |
} | |
elsif ($opts{action} eq 'chunkmount'){ | |
print "Running chunkmount routine for $vm\n\n" if ($opts{debug}); | |
mkdir $backupdir || die $!; | |
mkdir $backupdir . '.meta' || die $!; | |
run_chunkmount(); | |
} | |
else { | |
usage(); | |
exit 1; | |
} | |
} | |
############################################################################ | |
############## FUNCTIONS #################### | |
############################################################################ | |
sub prepare_backup{ | |
# Create a new XML object | |
my $xml = new XML::Simple (); | |
my $data = $xml->XMLin( $dom->get_xml_description(), forcearray => ['disk'] ); | |
# STop here if the lock file is present, another dump might be running | |
die "Another backup is running\n" if ( -e "$backupdir.meta/$vm.lock" ); | |
# Lock VM: Create a lock file so only one dump process can run | |
lock_vm(); | |
# Save the XML description | |
save_xml(); | |
# Save the VM state if it's running and --state is present | |
# (else, just suspend the VM) | |
$opts{wasrunning} = 0 unless ($dom->is_active()); | |
if ($opts{wasrunning}){ | |
if ($opts{state}){ | |
save_vm_state(); | |
} | |
else{ | |
suspend_vm(); | |
} | |
} | |
# Create a list of disks used by the VM | |
foreach $disk (@{$data->{devices}->{disk}}){ | |
my $source; | |
if ($disk->{type} eq 'block'){ | |
$source = $disk->{source}->{dev}; | |
} | |
elsif ($disk->{type} eq 'file'){ | |
$source = $disk->{source}->{file}; | |
} | |
else{ | |
print "\nSkiping $source for vm $vm as it's type is $disk->{type}: " . | |
" and only block and file are supported\n" if ($opts{debug}); | |
next; | |
} | |
my $target = $disk->{target}->{dev}; | |
# Check if the current disk is not excluded | |
if (grep { $_ eq "$target" } @excludes){ | |
print "\nSkiping $source for vm $vm as it's matching one of the excludes: " . | |
join(",",@excludes)."\n\n" if ($opts{debug}); | |
next; | |
} | |
# If the device is a disk (and not a cdrom) and the source dev exists | |
if (($disk->{device} eq 'disk') && (-e $source)){ | |
print "\nAnalysing disk $source connected on $vm as $target\n\n" if ($opts{debug}); | |
# If it's a block device | |
if ($disk->{type} eq 'block'){ | |
my $time = "_".time(); | |
# Try to snapshot the source if snapshot is enabled | |
if ( ($opts{snapshot}) && (create_snapshot($source,$time)) ){ | |
print "$source seems to be a valid logical volume (LVM), a snapshot has been taken as " . | |
$source . $time ."\n" if ($opts{debug}); | |
$source = $source.$time; | |
push (@disks, {source => $source, target => $target, type => 'snapshot'}); | |
} | |
# Snapshot failed, or disabled: disabling live backups | |
else{ | |
if ($opts{snapshot}){ | |
print "Snapshoting $source has failed (not managed by LVM, or already a snapshot ?)" . | |
", live backup will be disabled\n" if ($opts{debug}) ; | |
} | |
else{ | |
print "Not using LVM snapshots, live backups will be disabled\n" if ($opts{debug}); | |
} | |
$opts{livebackup} = 0; | |
push (@disks, {source => $source, target => $target, type => 'block'}); | |
} | |
} | |
elsif ($disk->{type} eq 'file'){ | |
$opts{livebackup} = 0; | |
push (@disks, {source => $source, target => $target, type => 'file'}); | |
} | |
print "Adding $source to the list of disks to be backed up\n" if ($opts{debug}); | |
} | |
} | |
# Summarize the list of disk to be dumped | |
if ($opts{debug}){ | |
if ($opts{action} eq 'dump'){ | |
print "\n\nThe following disks will be dumped:\n\n"; | |
foreach $disk (@disks){ | |
print "Source: $disk->{source}\tDest: $backupdir/$vm" . '_' . $disk->{target} . | |
".img$opts{compext}\n"; | |
} | |
} | |
elsif($opts{action} eq 'chunkmount'){ | |
print "\n\nThe following disks will be mounted as chunks:\n\n"; | |
foreach $disk (@disks){ | |
print "Source: $disk->{source}\tDest: $backupdir/$vm" . '_' . $disk->{target}; | |
} | |
} | |
} | |
# If livebackup is possible (every block devices can be snapshoted) | |
# We can restore the VM now, in order to minimize the downtime | |
if ($opts{livebackup}){ | |
print "\nWe can run a live backup\n" if ($opts{debug}); | |
if ($opts{wasrunning}){ | |
if ($opts{state}){ | |
restore_vm(); | |
} | |
else{ | |
resume_vm(); | |
} | |
} | |
} | |
} | |
sub run_dump{ | |
# Pause VM, dump state, take snapshots etc.. | |
prepare_backup(); | |
# Now, it's time to actually dump the disks | |
foreach $disk (@disks){ | |
my $source = $disk->{source}; | |
my $dest = "$backupdir/$vm" . '_' . $disk->{target} . ".img$opts{compext}"; | |
print "\nStarting dump of $source to $dest\n\n" if ($opts{debug}); | |
my $ddcmd = "$opts{ionice} dd if=$source bs=$opts{blocksize} | $opts{nice} $opts{compcmd} > $dest 2>/dev/null"; | |
unless( system("$ddcmd") == 0 ){ | |
die "Couldn't dump the block device/file $source to $dest\n"; | |
} | |
# Remove the snapshot if the current dumped disk is a snapshot | |
destroy_snapshot($source) if ($disk->{type} eq 'snapshot'); | |
} | |
# If the VM was running before the dump, restore (or resume) it | |
if ($opts{wasrunning}){ | |
if ($opts{state}){ | |
restore_vm(); | |
} | |
else{ | |
resume_vm(); | |
} | |
} | |
# And remove the lock file, unless the --keep-lock flag is present | |
unlock_vm() unless ($opts{keeplock}); | |
} | |
sub run_chunkmount{ | |
# Pause VM, dump state, take snapshots etc.. | |
prepare_backup(); | |
# Now, lets mount guest images with chunkfs | |
foreach $disk (@disks){ | |
my $source = $disk->{source}; | |
my $dest = "$backupdir/$vm" . '_' . $disk->{target}; | |
mkdir $dest || die $!; | |
print "\nMounting $source on $dest with chunkfs\n\n" if ($opts{debug}); | |
my $cmd = "$opts{ionice} $opts{chunkfs} -o fsname=chunkfs-$vm $opts{blocksize} $source $dest 2>/dev/null"; | |
unless( system("$cmd") == 0 ){ | |
die "Couldn't mount $source on $dest\n"; | |
} | |
} | |
} | |
# Remove the dumps | |
sub run_cleanup{ | |
print "\nRemoving backup files\n" if ($opts{debug}); | |
my $cnt = 0; | |
my $meta = 0; | |
my $snap = 0; | |
# If a state file is present, restore the VM | |
if (-e "$backupdir/$vm.state"){ | |
restore_vm(); | |
} | |
# Else, trys to resume it | |
else{ | |
resume_vm(); | |
} | |
if (open MOUNTS, "</proc/mounts"){ | |
foreach (<MOUNTS>){ | |
my @info = split(/\s+/, $_); | |
next unless ($info[0] eq "chunkfs-$vm"); | |
print "Found chunkfs mount point: $info[1]\n" if ($opts{debug}); | |
my $mp = $info[1]; | |
print "Unmounting chunkfs mount point $mp\n\n" if ($opts{debug}); | |
die "Couldn't unmount $mp\n" unless ( | |
system("/bin/umount $mp 2>/dev/null") == 0 | |
); | |
rmdir $mp || die $!; | |
} | |
close MOUNTS; | |
} | |
$cnt = unlink <$backupdir/*>; | |
if (open SNAPLIST, "<$backupdir.meta/snapshots"){ | |
foreach (<SNAPLIST>){ | |
# Destroy snapshot listed here is they exists | |
# and only if the end with _ and 10 digits | |
chomp; | |
if ((-e $_) && ($_ =~ m/_\d{10}$/)){ | |
print "Found $_ in snapshot list file, will try to remove it\n" if ($opts{debug}); | |
destroy_snapshot($_); | |
$snap++; | |
} | |
} | |
close SNAPLIST; | |
} | |
$meta = unlink <$backupdir.meta/*>; | |
rmdir "$backupdir/"; | |
rmdir "$backupdir.meta"; | |
print "$cnt file(s) removed\n$snap LVM snapshots removed\n$meta metadata files removed\n\n" if $opts{debug}; | |
} | |
sub usage{ | |
print "usage:\n$0 --action=[dump|cleanup|chunkmount|unlock] --vm=vm1[,vm2,vm3] [--debug] [--exclude=hda,hdb] [--compress] ". | |
"[--state] [--no-snapshot] [--snapsize=<size>] [--backupdir=/path/to/dir] [--connect=<URI>] ". | |
"[--keep-lock] [--bs=<block size>]\n" . | |
"\n\n" . | |
"\t--action: What action the script will run. Valid actions are\n\n" . | |
"\t\t- dump: Run the dump routine (dump disk image to temp dir, pausing the VM if needed). It's the default action\n" . | |
"\t\t- cleanup: Run the cleanup routine, cleaning up the backup dir\n" . | |
"\t\t- chunkmount: Mount each device as a chunkfs mount point directly in the backup dir\n" . | |
"\t\t- unlock: just remove the lock file, but don't cleanup the backup dir\n\n" . | |
"\t--vm=name: The VM you want to work on (as known by libvirt). You can backup several VMs in one shot " . | |
"if you separate them with comma, or with multiple --vm argument. You have to use the name of the domain, ". | |
"ID and UUID are not supported at the moment\n\n" . | |
"\n\nOther options:\n\n" . | |
"\t--state: Cleaner way to take backups. If this flag is present, the script will save the current state of " . | |
"the VM (if running) instead of just suspending it. With this you should be able to restore the VM at " . | |
"the exact state it was when the backup started. The reason this flag is optional is that some guests " . | |
"crashes after the restoration, especially when using the kvm-clock. Test this functionnality with" . | |
"your environnement before using this flag on production\n\n" . | |
"\t--no-snapshot: Do not attempt to use LVM snapshots. If not present, the script will try to take a snapshot " . | |
"of each disk of type 'block'. If all disk can be snapshoted, the VM is resumed, or restored (depending " . | |
"on the --state flag) immediatly after the snapshots have been taken, resulting in almost no downtime. " . | |
"This is called a \"live backup\" in this script" . | |
"If at least one disk cannot be snapshoted, the VM is suspended (or stoped) for the time the disks are " . | |
"dumped in the backup dir. That's why you should use a fast support for the backup dir (fast disks, RAID0 " . | |
"or RAID10)\n\n" . | |
"\t--snapsize=<snapsize>: The amount of space to use for snapshots. Use the same format as -L option of lvcreate. " . | |
"eg: --snapsize=15G. Default is 5G\n\n" . | |
"\t--compress[=[gzip|bzip2|pbzip2|lzop|xz|lzip|plzip]]: On the fly compress the disks images during the dump. If you " . | |
"don't specify a compression algo, gzip will be used.\n\n" . | |
"\t--exclude=hda,hdb: Prevent the disks listed from being dumped. The names are from the VM perspective, as " . | |
"configured in livirt as the target element. It can be usefull for example if you want to dump the system " . | |
"disk of a VM, but not the data one which can be backed up separatly, at the files level.\n\n" . | |
"\t--backupdir=/path/to/backup: Use an alternate backup dir. The directory must exists and be writable. " . | |
"The default is /var/lib/libvirt/backup\n\n" . | |
"\t--connect=<URI>: URI to connect to libvirt daemon (to suspend, resume, save, restore VM etc...). " . | |
"The default is qemu:///system.\n\n" . | |
"\t--keep-lock: Let the lock file present. This prevent another " . | |
"dump to run while an third party backup software (BackupPC for example) saves the dumped files.\n\n"; | |
} | |
# Save a running VM, if it's running | |
sub save_vm_state{ | |
if ($dom->is_active()){ | |
print "$vm is running, saving state....\n" if ($opts{debug}); | |
$dom->save("$backupdir/$vm.state"); | |
print "$vm state saved as $backupdir/$vm.state\n" if ($opts{debug}); | |
} | |
else{ | |
print "$vm is not running, nothing to do\n" if ($opts{debug}); | |
} | |
} | |
# Restore the state of a VM | |
sub restore_vm{ | |
if (! $dom->is_active()){ | |
if (-e "$backupdir/$vm.state"){ | |
print "\nTrying to restore $vm from $backupdir/$vm.state\n" if ($opts{debug}); | |
$libvirt->restore_domain("$backupdir/$vm.state"); | |
print "Waiting for restoration to complete\n" if ($opts{debug}); | |
my $i = 0; | |
while ((!$dom->is_active()) && ($i < 120)){ | |
sleep(5); | |
$i = $i+5; | |
} | |
print "Timeout while trying to restore $vm, aborting\n" | |
if (($i > 120) && ($opts{debug})); | |
} | |
else{ | |
print "\nRestoration impossible, $backupdir/$vm.state is missing\n" if ($opts{debug}); | |
} | |
} | |
else{ | |
print "\nCannot start domain restoration, $vm is running (maybe already restored after a live backup ?)\n" | |
if ($opts{debug}); | |
} | |
} | |
# Suspend a VM | |
sub suspend_vm(){ | |
if ($dom->is_active()){ | |
print "$vm is running, suspending\n" if ($opts{debug}); | |
$dom->suspend(); | |
print "$vm now suspended\n" if ($opts{debug}); | |
} | |
else{ | |
print "$vm is not running, nothing to do\n" if ($opts{debug}); | |
} | |
} | |
# Resume a VM if it's paused | |
sub resume_vm(){ | |
if ($dom->get_info->{state} == Sys::Virt::Domain::STATE_PAUSED){ | |
print "$vm is suspended, resuming\n" if ($opts{debug}); | |
$dom->resume(); | |
print "$vm now resumed\n" if ($opts{debug}); | |
} | |
else{ | |
print "$vm is not suspended, nothing to do\n" if ($opts{debug}); | |
} | |
} | |
# Dump the domain description as XML | |
sub save_xml{ | |
print "\nSaving XML description for $vm to $backupdir/$vm.xml\n" if ($opts{debug}); | |
open(XML, ">$backupdir/$vm" . ".xml") || die $!; | |
print XML $dom->get_xml_description(); | |
close XML; | |
} | |
# Create an LVM snapshot | |
# Pass the original logical volume and the suffix | |
# to be added to the snapshot name as arguments | |
sub create_snapshot{ | |
my ($blk,$suffix) = @_; | |
my $ret = 0; | |
print "Running: $opts{lvcreate} -p r -s -n " . $blk . $suffix . | |
" -L $opts{snapsize} $blk > /dev/null 2>&1\n" if $opts{debug}; | |
if ( system("$opts{lvcreate} -s -n " . $blk . $suffix . | |
" -L $opts{snapsize} $blk > /dev/null 2>&1") == 0 ) { | |
$ret = 1; | |
open SNAPLIST, ">>$backupdir.meta/snapshots" or die "Error, couldn't open snapshot list file\n"; | |
print SNAPLIST $blk.$suffix ."\n"; | |
close SNAPLIST; | |
} | |
return $ret; | |
} | |
# Remove an LVM snapshot | |
sub destroy_snapshot{ | |
my $ret = 0; | |
my ($snap) = @_; | |
print "Removing snapshot $snap\n" if $opts{debug}; | |
if (system ("$opts{lvremove} -f $snap > /dev/null 2>&1") == 0 ){ | |
$ret = 1; | |
} | |
return $ret; | |
} | |
# Lock a VM backup dir | |
# Just creates an empty lock file | |
sub lock_vm{ | |
print "Locking $vm\n" if $opts{debug}; | |
open ( LOCK, ">$backupdir.meta/$vm.lock" ) || die $!; | |
print LOCK ""; | |
close LOCK; | |
} | |
# Unlock the VM backup dir | |
# Just removes the lock file | |
sub unlock_vm{ | |
print "Removing lock file for $vm\n\n" if $opts{debug}; | |
unlink <$backupdir.meta/$vm.lock>; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment