[pve-devel] [PATCH] Added differential backup.
Kamil Trzcinski
ayufan at osk-net.pl
Wed Aug 15 02:51:40 CEST 2012
Signed-off-by: Kamil Trzcinski <ayufan at ayufan.eu>
---
PVE/API2/OpenVZ.pm | 7 ++++
PVE/VZDump.pm | 71 +++++++++++++++++++++++++++++++++++++++++++--
PVE/VZDump/OpenVZ.pm | 3 +-
vzdump.conf | 3 ++
www/manager/dc/Backup.js | 21 +++++++++++++-
5 files changed, 99 insertions(+), 6 deletions(-)
diff --git a/PVE/API2/OpenVZ.pm b/PVE/API2/OpenVZ.pm
index 938e4a4..535f5da 100644
--- a/PVE/API2/OpenVZ.pm
+++ b/PVE/API2/OpenVZ.pm
@@ -177,6 +177,13 @@ my $restore_openvz = sub {
my $cmd = ['tar', 'xpf', $archive, '--totals', '--sparse', '-C', $private];
+ if ($archive =~ m!([^/]*vzdump-([a-z]*)-(\d*)-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.(tgz|(tar(\.(gz|lzo))?)))--differential-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.vcdiff(\.(gz|lzo))?$!) {
+ my $fullbackup = $archive;
+ $fullbackup =~ s!([^/]*vzdump-([a-z]+)-(\d+)-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.(tgz|(tar(\.(gz|lzo))?)))--differential-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.vcdiff(\.(gz|lzo))?!$1!;
+ print "extracting from differential archive, using full backup '$fullbackup'\n";
+ $cmd = "pve-xdelta3 -q -d -c -R -s '$fullbackup' '$archive' |tar xpf - --totals --sparse -C '$private'";
+ }
+
if ($archive eq '-') {
print "extracting archive from STDIN\n";
run_command($cmd, input => "<&STDIN");
diff --git a/PVE/VZDump.pm b/PVE/VZDump.pm
index b9595be..3c2c060 100644
--- a/PVE/VZDump.pm
+++ b/PVE/VZDump.pm
@@ -10,6 +10,7 @@ use IO::Select;
use IPC::Open3;
use POSIX qw(strftime);
use File::Path;
+use File::Basename;
use PVE::RPCEnvironment;
use PVE::Storage;
use PVE::Cluster qw(cfs_read_file);
@@ -184,6 +185,7 @@ sub read_vzdump_defaults {
stopwait => 10, # 10 minutes
mode => 'snapshot',
maxfiles => 1,
+ fullbackup => 0,
};
my $fh = IO::File->new ("<$fn");
@@ -214,6 +216,8 @@ sub read_vzdump_defaults {
$res->{size} = int($1);
} elsif ($line =~ m/maxfiles:\s*(\d+)\s*$/) {
$res->{maxfiles} = int($1);
+ } elsif ($line =~ m/fullbackup:\s*(\d+)\s*$/) {
+ $res->{fullbackup} = int($1);
} elsif ($line =~ m/exclude-path:\s*(.*)\s*$/) {
$res->{'exclude-path'} = PVE::Tools::split_args($1);
} elsif ($line =~ m/mode:\s*(stop|snapshot|suspend)\s*$/) {
@@ -681,6 +685,22 @@ sub get_backup_file_list {
return $bklist;
}
+
+sub get_differential_backup_file_list {
+ my ($dir, $bkname, $exclude_fn) = @_;
+
+ my $bklist = [];
+ foreach my $fn (<$dir/${bkname}-*>) {
+ next if $exclude_fn && $fn eq $exclude_fn;
+ if ($fn =~ m!/(${bkname}--differential-(\d{4})_(\d{2})_(\d{2})-(\d{2})_(\d{2})_(\d{2})\.vcdiff(\.(gz|lzo))?)$!) {
+ $fn = "$dir/$1"; # untaint
+ my $t = timelocal ($7, $6, $5, $4, $3 - 1, $2 - 1900);
+ push @$bklist, [$fn, $t];
+ }
+ }
+
+ return $bklist;
+}
sub exec_backup_task {
my ($self, $task) = @_;
@@ -720,9 +740,37 @@ sub exec_backup_task {
if scalar(@$bklist) >= $maxfiles;
}
+ my $ext = '.tar';
+
+ my $fullbackup = undef;
+ if ($opts->{fullbackup} && !$opts->{stdout}) {
+ my $bklist = get_backup_file_list($opts->{dumpdir}, $bkname);
+ $bklist = [ sort { $b->[1] <=> $a->[1] } @$bklist ];
+ my $mintime = timelocal ($lt->sec, $lt->min, $lt->hour,
+ $lt->mday, $lt->mon, $lt->year) -
+ $opts->{fullbackup} * 24 * 60 * 60 -
+ 12 * 60 * 60; # - 12h just to make sure that on last day we create full backup
+
+ foreach my $d (@$bklist) {
+ next if $mintime > $d->[1];
+
+ $fullbackup = $d->[0];
+ $basename = basename($fullbackup);
+ $basename = sprintf "${basename}--differential-%04d_%02d_%02d-%02d_%02d_%02d",
+ $lt->year + 1900, $lt->mon + 1, $lt->mday,
+ $lt->hour, $lt->min, $lt->sec;
+ $ext = ".vcdiff";
+
+ debugmsg ('info', "doing differential backup against '$fullbackup'");
+ last;
+ }
+
+ debugmsg ('info', "doing full backup, because no backup found in last $opts->{fullbackup} day(s)")
+ if !$fullbackup;
+ }
+
my $logfile = $task->{logfile} = "$opts->{dumpdir}/$basename.log";
- my $ext = '.tar';
my ($comp, $comp_ext) = compressor_info($opts->{compress});
if ($comp && $comp_ext) {
$ext .= ".${comp_ext}";
@@ -893,7 +941,7 @@ sub exec_backup_task {
}
debugmsg ('info', "creating archive '$task->{tarfile}'", $logfd);
- $plugin->archive($task, $vmid, $task->{tmptar}, $comp);
+ $plugin->archive($task, $vmid, $task->{tmptar}, $comp, $fullbackup);
rename ($task->{tmptar}, $task->{tarfile}) ||
die "unable to rename '$task->{tmptar}' to '$task->{tarfile}'\n";
@@ -905,7 +953,7 @@ sub exec_backup_task {
# purge older backup
- if ($maxfiles && $opts->{remove}) {
+ if ($maxfiles && $opts->{remove} && !$fullbackup) {
my $bklist = get_backup_file_list($opts->{dumpdir}, $bkname, $task->{tarfile});
$bklist = [ sort { $b->[1] <=> $a->[1] } @$bklist ];
@@ -916,6 +964,16 @@ sub exec_backup_task {
my $logfn = $d->[0];
$logfn =~ s/\.(tgz|(tar(\.(gz|lzo))?))$/\.log/;
unlink $logfn;
+
+ my $dbklist = get_differential_backup_file_list($opts->{dumpdir}, basename($d->[0]));
+
+ foreach my $df (@$dbklist) {
+ debugmsg ('info', "delete old differential backup '$df->[0]'", $logfd);
+ unlink $df->[0];
+ $logfn = $df->[0];
+ $logfn =~ s/\.(vcdiff(\.(gz|lzo))?)$/\.log/;
+ unlink $logfn;
+ }
}
}
@@ -1175,6 +1233,12 @@ my $confdesc = {
optional => 1,
minimum => 1,
},
+ fullbackup => {
+ type => 'integer',
+ description => "Create full backup if last is older than 'fullbackup' days.",
+ optional => 1,
+ minimum => 0,
+ },
remove => {
type => 'boolean',
description => "Remove old backup files if there are more than 'maxfiles' backup files.",
@@ -1209,7 +1273,6 @@ sub verify_vzdump_parameters {
if $param->{exclude} && $param->{vmid};
$param->{all} = 1 if defined($param->{exclude});
-
return if !$check_missing;
raise_param_exc({ vmid => "property is missing"})
diff --git a/PVE/VZDump/OpenVZ.pm b/PVE/VZDump/OpenVZ.pm
index a185f61..887596f 100644
--- a/PVE/VZDump/OpenVZ.pm
+++ b/PVE/VZDump/OpenVZ.pm
@@ -231,7 +231,7 @@ sub assemble {
}
sub archive {
- my ($self, $task, $vmid, $filename, $comp) = @_;
+ my ($self, $task, $vmid, $filename, $comp, $basefile) = @_;
my $findexcl = $self->{vzdump}->{findexcl};
my $findargs = join (' ', @$findexcl) . ' -print0';
@@ -256,6 +256,7 @@ sub archive {
$cmd .= "tar cpf - $taropts --null -T -";
my $bwl = $opts->{bwlimit}*1024; # bandwidth limit for cstream
$cmd .= "|cstream -t $bwl" if $opts->{bwlimit};
+ $cmd .= "|pve-xdelta3 -q -e -c -s '$basefile'" if $basefile;
$cmd .= "|$comp" if $comp;
$cmd .= ")";
diff --git a/vzdump.conf b/vzdump.conf
index b5ff34f..a0a0728 100644
--- a/vzdump.conf
+++ b/vzdump.conf
@@ -12,3 +12,6 @@
#maxfiles: N
#script: FILENAME
#exclude-path: PATHLIST
+#fullbackup: DAYS
+
+
diff --git a/www/manager/dc/Backup.js b/www/manager/dc/Backup.js
index d069d9c..eb440a4 100644
--- a/www/manager/dc/Backup.js
+++ b/www/manager/dc/Backup.js
@@ -171,6 +171,25 @@ Ext.define('PVE.dc.BackupEdit', {
value: 'snapshot',
name: 'mode'
},
+ {
+ xtype: 'numberfield',
+ fieldLabel: gettext('Max Backups'),
+ name: 'maxfiles',
+ minValue: 0,
+ maxValue: 365,
+ value: me.create ? '1' : undefined,
+ allowBlank: false
+ },
+ {
+ xtype: 'numberfield',
+ fieldLabel: gettext('Full Backup Every'),
+ name: 'fullbackup',
+ emptyText : gettext('Days'),
+ minValue: 0,
+ maxValue: 60,
+ value: me.create ? '0' : undefined,
+ allowBlank: true
+ },
vmidField
];
@@ -461,4 +480,4 @@ Ext.define('PVE.dc.BackupView', {
{ name: 'compress', type: 'boolean' }
]
});
-});
\ No newline at end of file
+});
--
1.7.2.5
More information about the pve-devel
mailing list