From 16355b848dc8ec9fa299e0daa862dd947dc446e7 Mon Sep 17 00:00:00 2001 From: Axel Burri Date: Mon, 25 May 2015 14:38:32 +0200 Subject: [PATCH] btrbk: added "snapshot_create onchange", which skips snapshot creation if the latest snapshot is up-to-date (i.e. has same generation as the source subvolume) --- ChangeLog | 6 ++++++ btrbk | 47 ++++++++++++++++++++++++++++++++++++++++------- doc/btrbk.conf.5 | 21 ++++++++++++--------- 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3bd6581..c8d6abe 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +btrbk-current + + * Added "snapshot_create onchange", which skips snapshot creation if + the latest snapshot is up-to-date (i.e. has same generation as the + source subvolume) + btrbk-0.18.0 * MIGRATION diff --git a/btrbk b/btrbk index 6c80baa..2212dc8 100755 --- a/btrbk +++ b/btrbk @@ -47,7 +47,7 @@ use Date::Calc qw(Today Delta_Days Day_of_Week); use Getopt::Std; use Data::Dumper; -our $VERSION = "0.18.0"; +our $VERSION = "0.19.0-dev"; our $AUTHOR = 'Axel Burri '; our $PROJECT_HOME = ''; @@ -62,7 +62,7 @@ my %config_options = ( # NOTE: keys "volume", "subvolume" and "target" are hardcoded snapshot_dir => { default => undef, accept_file => { relative => 1 } }, snapshot_name => { default => undef, accept_file => { name_only => 1 }, context => [ "subvolume" ] }, - snapshot_create => { default => "always", accept => [ "no", "always", "ondemand" ] }, + snapshot_create => { default => "always", accept => [ "no", "always", "ondemand", "onchange" ] }, incremental => { default => "yes", accept => [ "yes", "no", "strict" ] }, resume_missing => { default => "yes", accept => [ "yes", "no" ] }, preserve_day_of_week => { default => "sunday", accept => [ (keys %day_of_week_map) ] }, @@ -1173,6 +1173,27 @@ sub get_latest_common($$$;$) } +sub get_latest_snapshot_child($$) +{ + my $sroot = shift || die; + my $svol = shift // die; + my $latest = undef; + my $gen = -1; + foreach (get_snapshot_children($sroot, $svol)) { + if($_->{gen} > $gen) { + $latest = $_; + $gen = $_->{gen}; + } + } + if($latest) { + DEBUG "Latest snapshot child for \"$svol->{PRINT}#$svol->{gen}\" is: $latest->{PRINT}#$latest->{gen}"; + } else { + DEBUG "No latest snapshots found for: $svol->{PRINT}"; + } + return $latest; +} + + sub _origin_tree { my $prefix = shift; @@ -1782,13 +1803,23 @@ MAIN: my $snapshot_basename = config_key($config_subvol, "snapshot_name") // die; # check if we need to create a snapshot - my $snapshot_create = config_key($config_subvol, "snapshot_create") // "no"; - if($snapshot_create eq "no") { - DEBUG "Snapshot creation disabled: snapshot_create=no"; + my $snapshot_create = config_key($config_subvol, "snapshot_create"); + if(not $snapshot_create) { + DEBUG "Snapshot creation disabled (snapshot_create=no)"; next; } - if($snapshot_create eq "always") { - DEBUG "Snapshot creation enabled: snapshot_create=always"; + elsif($snapshot_create eq "always") { + DEBUG "Snapshot creation enabled (snapshot_create=always)"; + } + elsif($snapshot_create eq "onchange") { + # check if latest snapshot is up-to-date with source subvolume (by generation) + my $latest = get_latest_snapshot_child($sroot, $svol); + if($latest && ($latest->{gen} == $svol->{gen})) { + INFO "Snapshot creation skipped: snapshot_create=onchange, snapshot is up-to-date: $latest->{PRINT}"; + $config_subvol->{SNAPSHOT_UP_TO_DATE} = $latest; + next; + } + DEBUG "Snapshot creation enabled: snapshot_create=onchange, gen=$svol->{gen} > snapshot_gen=$latest->{gen}"; } elsif($snapshot_create eq "ondemand") { my $snapshot_needed = 0; @@ -2106,6 +2137,7 @@ MAIN: push @subvol_out, "!!! Subvolume \"$svol->{PRINT}\" aborted: $config_subvol->{ABORTED}"; $err_count++ unless($config_subvol->{ABORTED_NOERR}); } + push @subvol_out, "=== $config_subvol->{SNAPSHOT_UP_TO_DATE}->{PRINT}" if($config_subvol->{SNAPSHOT_UP_TO_DATE}); push @subvol_out, "+++ $config_subvol->{SNAPSHOT}->{PRINT}" if($config_subvol->{SNAPSHOT}); if($config_subvol->{SUBVOL_DELETED}) { push @subvol_out, "--- $_->{PRINT}" foreach(sort { $a->{PATH} cmp $b->{PATH} } @{$config_subvol->{SUBVOL_DELETED}}); @@ -2146,6 +2178,7 @@ MAIN: print " Date: " . localtime($start_time) . "\n"; print " Config: $config->{SRC_FILE}\n"; print "\nLegend:\n"; + print " === up-to-date subvolume (source snapshot)\n"; print " +++ created subvolume (source snapshot)\n"; print " --- deleted subvolume\n"; print " *** received subvolume (non-incremental)\n"; diff --git a/doc/btrbk.conf.5 b/doc/btrbk.conf.5 index fcf8dd6..5006c82 100644 --- a/doc/btrbk.conf.5 +++ b/doc/btrbk.conf.5 @@ -55,15 +55,18 @@ will fail if it is not present. backup). This option is only valid in the \fItarget\fR section. Defaults to \fI\fR. .TP -\fBsnapshot_create\fR always|ondemand|no -If set to \[lq]ondemand\[rq], the snapshots are only created if the -target subvolume is reachable (useful if you are tight on disk space -and you only need btrbk for backups to an external disk which is not -always connected). If set to \[lq]always\[rq], the snapshots are -always created, regardless of the target reachability. If set to -\[lq]no\[rq], the snapshots are never created (useful in conjunction -with the \fIresume_missing\fR option if another instance of btrbk is -taking care of snapshot creation). Defaults to \[lq]always\[rq]. +\fBsnapshot_create\fR always|ondemand|onchange|no +If set to \[lq]ondemand\[rq], snapshots are only created if the target +subvolume is reachable (useful if you are tight on disk space and you +only need btrbk for backups to an external disk which is not always +connected). If set to \[lq]onchange\[rq], snapshots are only created +if the source subvolume has changed since the last snapshot (more +precisely: if the btrfs generation has been increased since the last +snapshot). If set to \[lq]always\[rq], snapshots are always +created. If set to \[lq]no\[rq], the snapshots are never created +(useful in conjunction with the \fIresume_missing\fR option if another +instance of btrbk is taking care of snapshot creation). Defaults to +\[lq]always\[rq]. .TP \fBincremental\fR yes|no|strict If set, incremental backups are created. If set to \[lq]strict\[rq],