|
|
|
@ -31,7 +31,6 @@ use Getopt::Long; |
|
|
|
|
umask(022); |
|
|
|
|
|
|
|
|
|
# Some constant |
|
|
|
|
|
|
|
|
|
our %opts = (); |
|
|
|
|
our @vms = (); |
|
|
|
|
our @excludes = (); |
|
|
|
@ -156,19 +155,18 @@ if (($opts{state}) && ($opts{shutdown})){ |
|
|
|
|
print "State and shutdown flags cannot be used together\n"; |
|
|
|
|
exit 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Backup dir needs to be created first |
|
|
|
|
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){ |
|
|
|
@ -204,30 +202,31 @@ foreach our $vm (@vms){ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
############################################################################ |
|
|
|
|
############## FUNCTIONS #################### |
|
|
|
|
############################################################################ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Common routine before backup. Will save the XML description, try to |
|
|
|
|
# create a snapshot of the disks etc... |
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
# 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(); |
|
|
|
@ -239,10 +238,9 @@ sub prepare_backup{ |
|
|
|
|
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}; |
|
|
|
@ -253,25 +251,25 @@ sub prepare_backup{ |
|
|
|
|
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; |
|
|
|
|
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)) ){ |
|
|
|
@ -300,7 +298,7 @@ sub prepare_backup{ |
|
|
|
|
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'){ |
|
|
|
@ -317,7 +315,7 @@ sub prepare_backup{ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 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}){ |
|
|
|
@ -345,10 +343,10 @@ sub run_dump{ |
|
|
|
|
|
|
|
|
|
# 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 ){ |
|
|
|
@ -358,7 +356,7 @@ sub run_dump{ |
|
|
|
|
sleep(1); |
|
|
|
|
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}){ |
|
|
|
@ -378,7 +376,7 @@ sub run_chunkmount{ |
|
|
|
|
|
|
|
|
|
# Now, lets mount guest images with chunkfs |
|
|
|
|
foreach $disk (@disks){ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
my $source = $disk->{source}; |
|
|
|
|
my $dest = "$backupdir/$vm" . '_' . $disk->{target}; |
|
|
|
|
mkdir $dest || die $!; |
|
|
|
@ -446,7 +444,7 @@ sub run_cleanup{ |
|
|
|
|
print "$cnt file(s) removed\n$snap LVM snapshots removed\n$meta metadata files removed\n\n" if $opts{debug}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Print help |
|
|
|
|
sub usage{ |
|
|
|
|
print "usage:\n$0 --action=[dump|cleanup|chunkmount|unlock] --vm=vm1[,vm2,vm3] [--debug] [--exclude=hda,hdb] [--compress] ". |
|
|
|
|
"[--state] [--shutdown] [--shutdown-timeout] [--no-snapshot] [--snapsize=<size>] [--backupdir=/path/to/dir] [--connect=<URI>] ". |
|
|
|
@ -492,7 +490,7 @@ sub usage{ |
|
|
|
|
"after that amount of time (in seconds), the backup will abort. The default timeout is 300 seconds\n\n" . |
|
|
|
|
"\t--blocksize=<blocksize>: Specify block size in bytes (for dd and chunkfs). Default to 262144 (256kB).\n"; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Save a running VM, if it's running |
|
|
|
|
sub save_vm_state{ |
|
|
|
|
if ($dom->is_active()){ |
|
|
|
@ -504,7 +502,7 @@ sub save_vm_state{ |
|
|
|
|
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()){ |
|
|
|
@ -529,7 +527,7 @@ sub restore_vm{ |
|
|
|
|
if ($opts{debug}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Suspend a VM |
|
|
|
|
sub suspend_vm(){ |
|
|
|
|
if ($dom->is_active()){ |
|
|
|
@ -541,7 +539,7 @@ sub suspend_vm(){ |
|
|
|
|
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){ |
|
|
|
@ -554,7 +552,6 @@ sub resume_vm(){ |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Shutdown a VM via ACPI |
|
|
|
|
sub shutdown_vm(){ |
|
|
|
|
if ($dom->is_active()){ |
|
|
|
@ -585,7 +582,7 @@ sub start_vm(){ |
|
|
|
|
print "$vm is not in a shutdown state, 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}); |
|
|
|
@ -593,7 +590,7 @@ sub save_xml{ |
|
|
|
|
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 |
|
|
|
@ -611,7 +608,7 @@ sub create_snapshot{ |
|
|
|
|
} |
|
|
|
|
return $ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Remove an LVM snapshot |
|
|
|
|
sub destroy_snapshot{ |
|
|
|
|
my $ret = 0; |
|
|
|
@ -622,7 +619,7 @@ sub destroy_snapshot{ |
|
|
|
|
} |
|
|
|
|
return $ret; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Lock a VM backup dir |
|
|
|
|
# Just creates an empty lock file |
|
|
|
|
sub lock_vm{ |
|
|
|
@ -631,7 +628,7 @@ sub lock_vm{ |
|
|
|
|
print LOCK ""; |
|
|
|
|
close LOCK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Unlock the VM backup dir |
|
|
|
|
# Just removes the lock file |
|
|
|
|
sub unlock_vm{ |
|
|
|
|