|
|
@ -4,7 +4,7 @@ |
|
|
|
# Daniel Berteaud <daniel@firewall-services.com> |
|
|
|
# Daniel Berteaud <daniel@firewall-services.com> |
|
|
|
# |
|
|
|
# |
|
|
|
# COPYRIGHT |
|
|
|
# COPYRIGHT |
|
|
|
# Copyright (C) 2009-2014 Daniel Berteaud |
|
|
|
# Copyright (C) 2009-2015 Daniel Berteaud |
|
|
|
# |
|
|
|
# |
|
|
|
# This program is free software; you can redistribute it and/or modify |
|
|
|
# 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 |
|
|
|
# it under the terms of the GNU General Public License as published by |
|
|
@ -32,7 +32,7 @@ use File::Spec; |
|
|
|
# Set umask |
|
|
|
# Set umask |
|
|
|
umask(022); |
|
|
|
umask(022); |
|
|
|
|
|
|
|
|
|
|
|
# Some constant |
|
|
|
# Some global vars |
|
|
|
our %opts = (); |
|
|
|
our %opts = (); |
|
|
|
our @vms = (); |
|
|
|
our @vms = (); |
|
|
|
our @excludes = (); |
|
|
|
our @excludes = (); |
|
|
@ -95,6 +95,7 @@ $opts{ionice} = 'ionice -c 2 -n 7'; |
|
|
|
$opts{shutdown} = 0; |
|
|
|
$opts{shutdown} = 0; |
|
|
|
$opts{shutdowntimeout} = 300; |
|
|
|
$opts{shutdowntimeout} = 300; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Those are internal variables, do not modify |
|
|
|
# Those are internal variables, do not modify |
|
|
|
$opts{livebackup} = 1; |
|
|
|
$opts{livebackup} = 1; |
|
|
|
$opts{wasrunning} = 1; |
|
|
|
$opts{wasrunning} = 1; |
|
|
@ -212,9 +213,8 @@ $libvirt2 = ''; |
|
|
|
|
|
|
|
|
|
|
|
if (defined $connect[1]){ |
|
|
|
if (defined $connect[1]){ |
|
|
|
eval { $libvirt2 = Sys::Virt->new( uri => $connect[1] ); }; |
|
|
|
eval { $libvirt2 = Sys::Virt->new( uri => $connect[1] ); }; |
|
|
|
if ($@ && $opts{debug}){ |
|
|
|
print "Error connecting to libvirt on URI: $connect[1], lets hope is out of order\n" |
|
|
|
print "Error connecting to libvirt on URI: $connect[1], lets hope is out of order\n"; |
|
|
|
if ($@ && $opts{debug}); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
our $libvirt = $libvirt1; |
|
|
|
our $libvirt = $libvirt1; |
|
|
@ -296,11 +296,11 @@ sub prepare_backup{ |
|
|
|
# Save the XML description |
|
|
|
# Save the XML description |
|
|
|
save_xml(); |
|
|
|
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()); |
|
|
|
$opts{wasrunning} = 0 unless ($dom->is_active()); |
|
|
|
|
|
|
|
|
|
|
|
if ($opts{wasrunning}){ |
|
|
|
if ($opts{wasrunning}){ |
|
|
|
|
|
|
|
# Save the VM state if it's running and --state is present |
|
|
|
|
|
|
|
# (else, just suspend the VM) |
|
|
|
if ($opts{state}){ |
|
|
|
if ($opts{state}){ |
|
|
|
save_vm_state(); |
|
|
|
save_vm_state(); |
|
|
|
} |
|
|
|
} |
|
|
@ -348,7 +348,11 @@ sub prepare_backup{ |
|
|
|
print "$source seems to be a valid logical volume (LVM), a snapshot has been taken as " . |
|
|
|
print "$source seems to be a valid logical volume (LVM), a snapshot has been taken as " . |
|
|
|
$source . $time ."\n" if ($opts{debug}); |
|
|
|
$source . $time ."\n" if ($opts{debug}); |
|
|
|
$source = $source . $time; |
|
|
|
$source = $source . $time; |
|
|
|
push (@disks, {source => $source, target => $target, type => 'snapshot'}); |
|
|
|
push @disks, { |
|
|
|
|
|
|
|
source => $source, |
|
|
|
|
|
|
|
target => $target, |
|
|
|
|
|
|
|
type => 'snapshot' |
|
|
|
|
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
# Snapshot failed, or disabled: disabling live backups |
|
|
|
# Snapshot failed, or disabled: disabling live backups |
|
|
|
else{ |
|
|
|
else{ |
|
|
@ -360,7 +364,11 @@ sub prepare_backup{ |
|
|
|
print "Not using LVM snapshots, live backups will be disabled\n" if ($opts{debug}); |
|
|
|
print "Not using LVM snapshots, live backups will be disabled\n" if ($opts{debug}); |
|
|
|
} |
|
|
|
} |
|
|
|
$opts{livebackup} = 0; |
|
|
|
$opts{livebackup} = 0; |
|
|
|
push (@disks, {source => $source, target => $target, type => 'block'}); |
|
|
|
push @disks, { |
|
|
|
|
|
|
|
source => $source, |
|
|
|
|
|
|
|
target => $target, |
|
|
|
|
|
|
|
type => 'block' |
|
|
|
|
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
# If the disk is a file |
|
|
|
# If the disk is a file |
|
|
@ -430,11 +438,19 @@ sub prepare_backup{ |
|
|
|
$opts{livebackup} = 0; |
|
|
|
$opts{livebackup} = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
$file =~ s|//|/|g; |
|
|
|
$file =~ s|//|/|g; |
|
|
|
push (@disks, {source => $file, target => $target, type => 'file'}); |
|
|
|
push @disks, { |
|
|
|
|
|
|
|
source => $file, |
|
|
|
|
|
|
|
target => $target, |
|
|
|
|
|
|
|
type => 'file' |
|
|
|
|
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
|
$opts{livebackup} = 0; |
|
|
|
$opts{livebackup} = 0; |
|
|
|
push (@disks, {source => $source, target => $target, type => 'file'}); |
|
|
|
push @disks, { |
|
|
|
|
|
|
|
source => $source, |
|
|
|
|
|
|
|
target => $target, |
|
|
|
|
|
|
|
type => 'file' |
|
|
|
|
|
|
|
}; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
if ($opts{debug} && ($opts{livebackup} || $opts{offline})){ |
|
|
|
if ($opts{debug} && ($opts{livebackup} || $opts{offline})){ |
|
|
@ -510,7 +526,7 @@ sub run_dump{ |
|
|
|
$cmd = "$opts{nice} $opts{ionice} qemu-img convert -O qcow2"; |
|
|
|
$cmd = "$opts{nice} $opts{ionice} qemu-img convert -O qcow2"; |
|
|
|
$cmd .= " -c" if ($opts{compress} ne 'none'); |
|
|
|
$cmd .= " -c" if ($opts{compress} ne 'none'); |
|
|
|
$cmd .= " $source $dest 2>/dev/null 2>&1"; |
|
|
|
$cmd .= " $source $dest 2>/dev/null 2>&1"; |
|
|
|
print "Ignoring compression format, lets use the internal qcow2 compression feature instead\n" |
|
|
|
print "Ignoring compression format, using the internal qcow2 compression\n" |
|
|
|
if ($opts{debug} && $opts{compress} ne 'none') |
|
|
|
if ($opts{debug} && $opts{compress} ne 'none') |
|
|
|
} |
|
|
|
} |
|
|
|
else { |
|
|
|
else { |
|
|
@ -576,7 +592,7 @@ sub run_cleanup{ |
|
|
|
if (-e "$backupdir/$vm.state"){ |
|
|
|
if (-e "$backupdir/$vm.state"){ |
|
|
|
restore_vm(); |
|
|
|
restore_vm(); |
|
|
|
} |
|
|
|
} |
|
|
|
# Else, trys to resume it |
|
|
|
# Else, try to resume it |
|
|
|
else{ |
|
|
|
else{ |
|
|
|
resume_vm(); |
|
|
|
resume_vm(); |
|
|
|
} |
|
|
|
} |
|
|
@ -595,8 +611,8 @@ sub run_cleanup{ |
|
|
|
); |
|
|
|
); |
|
|
|
rmdir $mp || die $!; |
|
|
|
rmdir $mp || die $!; |
|
|
|
} |
|
|
|
} |
|
|
|
# Just wait a second to be sure all fuse resources has been released |
|
|
|
# Just wait 2 seconds to be sure all fuse resources has been released |
|
|
|
sleep(1); |
|
|
|
sleep(2); |
|
|
|
# Now, standard filesystems |
|
|
|
# Now, standard filesystems |
|
|
|
foreach (@mounts){ |
|
|
|
foreach (@mounts){ |
|
|
|
my @info = split(/\s+/, $_); |
|
|
|
my @info = split(/\s+/, $_); |
|
|
@ -660,15 +676,15 @@ sub usage{ |
|
|
|
"\t--state: Cleaner way to take backups. If this flag is present, the script will save the current state of " . |
|
|
|
"\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 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 " . |
|
|
|
"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" . |
|
|
|
"crashes after restoration, especially when using the kvm-clock. Test this functionnality with" . |
|
|
|
"your environnement before using this flag on production. This flag is mutual exclusive with --shutdown\n\n" . |
|
|
|
"your environnement before using this flag on production. This flag is mutually exclusive with --shutdown\n\n" . |
|
|
|
"\t--no-offline: Abort the backup if live backup isn't possible (meaning snapshot failed). This is to prevent a VM " . |
|
|
|
"\t--no-offline: Abort the backup if live backup isn't possible (meaning snapshot failed). This is to prevent a VM " . |
|
|
|
"begin paused for the duration of the backup, in some cases, its better to just abort the backup. Of course " . |
|
|
|
"begin paused for the duration of the backup, in some cases, its better to just abort the backup. Of course " . |
|
|
|
"this flag is mutually exclusive with --no-snapshot\n\n" . |
|
|
|
"this flag is mutually exclusive with --no-snapshot\n\n" . |
|
|
|
"\t--no-snapshot: Do not attempt to use LVM snapshots. If not present, the script will try to take a snapshot " . |
|
|
|
"\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 " . |
|
|
|
"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. " . |
|
|
|
"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" . |
|
|
|
"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 " . |
|
|
|
"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 " . |
|
|
|
"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" . |
|
|
|
"or RAID10)\n\n" . |
|
|
@ -676,7 +692,8 @@ sub usage{ |
|
|
|
"eg: --snapsize=15G. Default is 5G. For thinly provisionned volumes, this will be ignored\n\n" . |
|
|
|
"eg: --snapsize=15G. Default is 5G. For thinly provisionned volumes, this will be ignored\n\n" . |
|
|
|
"\t--compress[=[gzip|bzip2|pbzip2|lzop|xz|lzip|plzip]]: On the fly compress the disks images during the dump. If you " . |
|
|
|
"\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. For the convert action, the compression uses " . |
|
|
|
"don't specify a compression algo, gzip will be used. For the convert action, the compression uses " . |
|
|
|
"the internal qcow2 compression feature, and so, it ignores the compression format.\n\n" . |
|
|
|
"the internal qcow2 compression feature, and so, it ignores the compression format, in this case --compress " . |
|
|
|
|
|
|
|
"is just seen as a boolean flag\n\n" . |
|
|
|
"\t--exclude=hda,hdb: Prevent the disks listed from being dumped. The names are from the VM perspective, as " . |
|
|
|
"\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 " . |
|
|
|
"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" . |
|
|
|
"disk of a VM, but not the data one which can be backed up separatly, at the files level.\n\n" . |
|
|
|