diff --git a/virt-backup b/virt-backup index a8b45a7..d22f7c6 100644 --- a/virt-backup +++ b/virt-backup @@ -4,7 +4,7 @@ # Daniel Berteaud # # 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 # it under the terms of the GNU General Public License as published by @@ -32,7 +32,7 @@ use File::Spec; # Set umask umask(022); -# Some constant +# Some global vars our %opts = (); our @vms = (); our @excludes = (); @@ -95,6 +95,7 @@ $opts{ionice} = 'ionice -c 2 -n 7'; $opts{shutdown} = 0; $opts{shutdowntimeout} = 300; + # Those are internal variables, do not modify $opts{livebackup} = 1; $opts{wasrunning} = 1; @@ -161,17 +162,17 @@ else{ } # Allow comma separated multi-argument -@vms = split(/,/,join(',',@vms)); -@excludes = split(/,/,join(',',@excludes)); -@connect = split(/,/,join(',',@connect)); +@vms = split(/,/, join(',',@vms)); +@excludes = split(/,/, join(',',@excludes)); +@connect = split(/,/, join(',',@connect)); # Define a default libvirt URI $connect[0] = "qemu:///system" unless (defined $connect[0]); # Backward compatible with --dump --cleanup --unlock -$opts{action} = 'dump' if ($opts{dump}); +$opts{action} = 'dump' if ($opts{dump}); $opts{action} = 'cleanup' if ($opts{cleanup}); -$opts{action} = 'unlock' if ($opts{unlock}); +$opts{action} = 'unlock' if ($opts{unlock}); # Stop here if we have no vm # Or the help flag is present @@ -212,9 +213,8 @@ $libvirt2 = ''; if (defined $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; @@ -240,9 +240,9 @@ foreach our $vm (@vms){ $dom = $libvirt1->get_domain_by_name($vm); } } - our $backupdir = $opts{backupdir}.'/'.$vm; - our $lockdir = ($opts{lockdir} eq '') ? "$backupdir.meta" : $opts{lockdir}; - our $time = "_".time(); + our $backupdir = $opts{backupdir} . '/' . $vm; + our $lockdir = ($opts{lockdir} eq '') ? "$backupdir.meta" : $opts{lockdir}; + our $time = "_".time(); if ($opts{action} eq 'cleanup'){ print "Running cleanup routine for $vm\n\n" if ($opts{debug}); run_cleanup(1); @@ -251,17 +251,17 @@ foreach our $vm (@vms){ print "Unlocking $vm\n\n" if ($opts{debug}); unlock_vm(); } - elsif ($opts{action} eq 'dump' || $opts{action} eq 'convert'){ + elsif ($opts{action} eq 'dump' || $opts{action} eq 'convert'){ print "Running dump routine for $vm\n\n" if ($opts{debug}); - mkdir $backupdir || die $!; - mkdir $backupdir . '.meta' || die $!; + mkdir $backupdir || die $!; + mkdir $backupdir . '.meta' || die $!; mkdir $backupdir . '.mount' || 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 $!; + mkdir $backupdir . '.meta' || die $!; mkdir $backupdir . '.mount' || die $!; run_chunkmount(); } @@ -296,11 +296,11 @@ sub prepare_backup{ # 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}){ + # Save the VM state if it's running and --state is present + # (else, just suspend the VM) if ($opts{state}){ save_vm_state(); } @@ -331,7 +331,7 @@ sub prepare_backup{ # 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}); + join(",", @excludes)."\n\n" if ($opts{debug}); next; } @@ -347,8 +347,12 @@ sub prepare_backup{ 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'}); + $source = $source . $time; + push @disks, { + source => $source, + target => $target, + type => 'snapshot' + }; } # Snapshot failed, or disabled: disabling live backups else{ @@ -360,20 +364,24 @@ sub prepare_backup{ 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'}); + push @disks, { + source => $source, + target => $target, + type => 'block' + }; } } # If the disk is a file elsif ($disk->{type} eq 'file'){ # Try to find the mount point, and the backing device my @df = `df -PT $source`; - my ($dev,undef,undef,undef,undef,undef,$mount) = split /\s+/, $df[1]; + my ($dev, undef, undef, undef, undef, undef, $mount) = split /\s+/, $df[1]; # Ok, we now have the backing device which probably looks like /dev/mapper/vg-lv # We cannot pass this arg to lvcreate to take a snapshot, we need to detect Volume Group # name and Logical Volume name my $lvm = ''; if ($opts{lvm} eq '' and $dev =~ m!^/dev/!){ - my (undef,$lv,$vg) = split (/\s+/, `$opts{lvs} --noheadings -o lv_name,vg_name $dev $file, target => $target, type => 'file'}); + push @disks, { + source => $file, + target => $target, + type => 'file' + }; } else { $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})){ @@ -507,10 +523,10 @@ sub run_dump{ my $cmd = ''; if ($opts{action} eq 'convert'){ print "\nStarting conversion in qcow2 format of $source to $dest\n\n" if ($opts{debug}); - $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 .= " $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') } else { @@ -549,7 +565,7 @@ sub run_chunkmount{ foreach $disk (@disks){ my $source = $disk->{source}; - my $dest = "$backupdir/$vm" . '_' . $disk->{target}; + 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"; @@ -576,7 +592,7 @@ sub run_cleanup{ if (-e "$backupdir/$vm.state"){ restore_vm(); } - # Else, trys to resume it + # Else, try to resume it else{ resume_vm(); } @@ -595,8 +611,8 @@ sub run_cleanup{ ); rmdir $mp || die $!; } - # Just wait a second to be sure all fuse resources has been released - sleep(1); + # Just wait 2 seconds to be sure all fuse resources has been released + sleep(2); # Now, standard filesystems foreach (@mounts){ 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 " . "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. This flag is mutual exclusive with --shutdown\n\n" . + "crashes after restoration, especially when using the kvm-clock. Test this functionnality with" . + "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 " . "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" . "\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" . + "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" . @@ -676,7 +692,8 @@ sub usage{ "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 " . "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 " . "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" . @@ -826,10 +843,10 @@ sub save_xml{ # to be added to the snapshot name as arguments sub create_snapshot{ my ($blk,$suffix) = @_; - my $ret = 0; + my $ret = 0; my $lock = $blk; - $lock =~ s/\//\-/g; - $lock = $opts{backupdir} . '/' . $lock . '.lock'; + $lock =~ s/\//\-/g; + $lock = $opts{backupdir} . '/' . $lock . '.lock'; my $cmd = "$opts{lvcreate} -s -n " . $blk . $suffix; my ($pool) = split (/\s+/, `$opts{lvs} --noheadings -o pool_lv $blk