mirror of https://github.com/digint/btrbk
btrbk: always preserve latest snapshot/backup pair (instead of latest snapshot and latest backup); remove preserve_latest option of schedule()
parent
032642751b
commit
36cc96fdb8
66
btrbk
66
btrbk
|
@ -1807,20 +1807,30 @@ sub vinfo_subsection($$;$)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub get_snapshot_children($$)
|
sub get_snapshot_children($$;$$)
|
||||||
{
|
{
|
||||||
my $sroot = shift || die;
|
my $sroot = shift || die;
|
||||||
my $svol = shift // die;
|
my $svol = shift // die;
|
||||||
|
my $subvol_dir = shift // "";
|
||||||
|
my $btrbk_basename = shift;
|
||||||
my @ret;
|
my @ret;
|
||||||
|
|
||||||
my $sroot_subvols = vinfo_subvol_list($sroot);
|
my $sroot_subvols = vinfo_subvol_list($sroot);
|
||||||
foreach (@$sroot_subvols) {
|
foreach (@$sroot_subvols) {
|
||||||
next unless($_->{node}{readonly});
|
next unless($_->{node}{readonly});
|
||||||
next unless($_->{node}{parent_uuid} eq $svol->{node}{uuid});
|
next unless($_->{node}{parent_uuid} eq $svol->{node}{uuid});
|
||||||
|
if(defined($btrbk_basename) &&
|
||||||
|
( (not exists($_->{BTRBK_BASENAME})) ||
|
||||||
|
($_->{SUBVOL_DIR} ne $subvol_dir) ||
|
||||||
|
($_->{BTRBK_BASENAME} ne $btrbk_basename)) ) {
|
||||||
|
TRACE "get_snapshot_children: child does not match btrbk filename scheme, skipping: $_->{PRINT}";
|
||||||
|
next;
|
||||||
|
}
|
||||||
TRACE "get_snapshot_children: found: $_->{PRINT}";
|
TRACE "get_snapshot_children: found: $_->{PRINT}";
|
||||||
push(@ret, $_);
|
push(@ret, $_);
|
||||||
}
|
}
|
||||||
DEBUG "Found " . scalar(@ret) . " snapshot children of: $svol->{PRINT}";
|
$subvol_dir .= '/' if($subvol_dir);
|
||||||
|
DEBUG "Found " . scalar(@ret) . " snapshot children of \"$svol->{PRINT}\" in: $sroot->{PRINT}" . (defined($btrbk_basename) ? "/$subvol_dir$btrbk_basename.*" : "");
|
||||||
return @ret;
|
return @ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2651,7 +2661,6 @@ sub schedule(@)
|
||||||
my %args = @_;
|
my %args = @_;
|
||||||
my $schedule = $args{schedule} || die;
|
my $schedule = $args{schedule} || die;
|
||||||
my $preserve = $args{preserve} || die;
|
my $preserve = $args{preserve} || die;
|
||||||
my $preserve_latest = $args{preserve_latest} || 0;
|
|
||||||
my $results_list = $args{results};
|
my $results_list = $args{results};
|
||||||
my $result_hints = $args{result_hints} // {};
|
my $result_hints = $args{result_hints} // {};
|
||||||
|
|
||||||
|
@ -2699,11 +2708,6 @@ sub schedule(@)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($preserve_latest && (scalar @sorted_schedule)) {
|
|
||||||
my $href = $sorted_schedule[-1];
|
|
||||||
$href->{preserve} ||= $preserve_latest;
|
|
||||||
}
|
|
||||||
|
|
||||||
my %first_in_delta_hours;
|
my %first_in_delta_hours;
|
||||||
my %first_in_delta_days;
|
my %first_in_delta_days;
|
||||||
my %first_in_delta_weeks;
|
my %first_in_delta_weeks;
|
||||||
|
@ -4229,7 +4233,8 @@ MAIN:
|
||||||
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
||||||
my $snapdir = config_key($svol, "snapshot_dir") // "";
|
my $snapdir = config_key($svol, "snapshot_dir") // "";
|
||||||
my $snapshot_basename = config_key($svol, "snapshot_name") // die;
|
my $snapshot_basename = config_key($svol, "snapshot_name") // die;
|
||||||
my $preserve_latest = $svol->{SNAPSHOT_CREATED} ? 0 : 1;
|
my @snapshot_children = sort({ cmp_date($a->{BTRBK_DATE}, $b->{BTRBK_DATE}) }
|
||||||
|
get_snapshot_children($sroot, $svol, $snapdir, $snapshot_basename));
|
||||||
|
|
||||||
foreach my $droot (vinfo_subsection($svol, 'target')) {
|
foreach my $droot (vinfo_subsection($svol, 'target')) {
|
||||||
INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/";
|
INFO "Checking for missing backups of subvolume \"$svol->{PRINT}\" in: $droot->{PRINT}/";
|
||||||
|
@ -4237,14 +4242,8 @@ MAIN:
|
||||||
my $resume_total = 0;
|
my $resume_total = 0;
|
||||||
my $resume_success = 0;
|
my $resume_success = 0;
|
||||||
|
|
||||||
foreach my $child (sort { $a->{node}{cgen} <=> $b->{node}{cgen} } get_snapshot_children($sroot, $svol))
|
foreach my $child (@snapshot_children)
|
||||||
{
|
{
|
||||||
unless($child->{BTRBK_DATE} &&
|
|
||||||
($child->{SUBVOL_DIR} eq $snapdir) &&
|
|
||||||
($child->{BTRBK_BASENAME} eq $snapshot_basename)) {
|
|
||||||
TRACE "Backup candidate does not match btrbk filename scheme, skipping: $child->{PRINT}";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1)));
|
next if(scalar(get_receive_targets($droot, $child, exact_match => 1, warn_unexpected => 1)));
|
||||||
|
|
||||||
if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) {
|
if(my $err_vol = vinfo_subvol($droot, $child->{NAME})) {
|
||||||
|
@ -4272,13 +4271,13 @@ MAIN:
|
||||||
}
|
}
|
||||||
push(@schedule, { value => undef,
|
push(@schedule, { value => undef,
|
||||||
btrbk_date => $vol->{BTRBK_DATE},
|
btrbk_date => $vol->{BTRBK_DATE},
|
||||||
preserve => $vol->{node}{FORCE_PRESERVE},
|
# not enforcing resuming of latest snapshot anymore (since v0.23.0)
|
||||||
|
# preserve => $vol->{node}{FORCE_PRESERVE},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
my ($preserve, undef) = schedule(
|
my ($preserve, undef) = schedule(
|
||||||
schedule => \@schedule,
|
schedule => \@schedule,
|
||||||
preserve => config_preserve_hash($droot, "target"),
|
preserve => config_preserve_hash($droot, "target"),
|
||||||
preserve_latest => $preserve_latest,
|
|
||||||
);
|
);
|
||||||
my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes)
|
my @resume = grep defined, @$preserve; # remove entries with no value from list (target subvolumes)
|
||||||
$resume_total = scalar @resume;
|
$resume_total = scalar @resume;
|
||||||
|
@ -4324,11 +4323,12 @@ MAIN:
|
||||||
{
|
{
|
||||||
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
foreach my $sroot (vinfo_subsection($config, 'volume')) {
|
||||||
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
foreach my $svol (vinfo_subsection($sroot, 'subvolume')) {
|
||||||
|
my $snapdir = config_key($svol, "snapshot_dir") // "";
|
||||||
my $snapdir_ts = config_key($svol, "snapshot_dir", postfix => '/') // "";
|
my $snapdir_ts = config_key($svol, "snapshot_dir", postfix => '/') // "";
|
||||||
my $snapshot_basename = config_key($svol, "snapshot_name") // die;
|
my $snapshot_basename = config_key($svol, "snapshot_name") // die;
|
||||||
my $preserve_latest_snapshot = $svol->{SNAPSHOT_CREATED} ? 0 : "preserve forced: latest in list (no snapshot created)";
|
|
||||||
my $preserve_latest_backup = $preserve_latest_snapshot;
|
|
||||||
my $target_aborted = 0;
|
my $target_aborted = 0;
|
||||||
|
my @snapshot_children = sort({ cmp_date($b->{BTRBK_DATE}, $a->{BTRBK_DATE}) } # sort descending
|
||||||
|
get_snapshot_children($sroot, $svol, $snapdir, $snapshot_basename));
|
||||||
|
|
||||||
foreach my $droot (vinfo_subsection($svol, 'target', 1)) {
|
foreach my $droot (vinfo_subsection($svol, 'target', 1)) {
|
||||||
if(ABORTED($droot)) {
|
if(ABORTED($droot)) {
|
||||||
|
@ -4339,16 +4339,18 @@ MAIN:
|
||||||
}
|
}
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
if($droot->{CONFIG}->{target_type} eq "raw") {
|
|
||||||
if(config_key($droot, "incremental")) {
|
# always preserve latest common snapshot/backup pair
|
||||||
# In incremental mode, the latest backup is most certainly our parent.
|
foreach my $child (@snapshot_children) {
|
||||||
# (see note on FORCE_PRESERVE above)
|
my @receive_targets = get_receive_targets($droot, $child);
|
||||||
$preserve_latest_backup ||= "preserve forced: possibly parent of latest backup";
|
if(scalar(@receive_targets)) {
|
||||||
# Note that we could check against $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid} to be certain,
|
DEBUG "Force preserve for latest common snapshot: $child->{PRINT}";
|
||||||
# but this information is not available in $dryrun:
|
$child->{node}{FORCE_PRESERVE} = 'preserve forced: latest common snapshot';
|
||||||
# foreach my $vol (@{vinfo_subvol_list($droot)}) {
|
foreach(@receive_targets) {
|
||||||
# $vol->{FORCE_PRESERVE} = 1 if($vol->{node}{received_uuid} eq $svol->{SNAPSHOT_CREATED}->{node}{parent_uuid});
|
DEBUG "Force preserve for latest common target: $_->{PRINT}";
|
||||||
# }
|
$_->{node}{FORCE_PRESERVE} = 'preserve forced: latest common target';
|
||||||
|
}
|
||||||
|
last;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4358,7 +4360,6 @@ MAIN:
|
||||||
INFO "Cleaning backups of subvolume \"$svol->{PRINT}\": $droot->{PRINT}/$snapshot_basename.*";
|
INFO "Cleaning backups of subvolume \"$svol->{PRINT}\": $droot->{PRINT}/$snapshot_basename.*";
|
||||||
unless(macro_delete($droot, "", $snapshot_basename, $droot,
|
unless(macro_delete($droot, "", $snapshot_basename, $droot,
|
||||||
{ preserve => config_preserve_hash($droot, "target"),
|
{ preserve => config_preserve_hash($droot, "target"),
|
||||||
preserve_latest => $preserve_latest_backup,
|
|
||||||
results => $schedule_results,
|
results => $schedule_results,
|
||||||
result_hints => { topic => "backup", root_path => $droot->{PATH} },
|
result_hints => { topic => "backup", root_path => $droot->{PATH} },
|
||||||
},
|
},
|
||||||
|
@ -4382,9 +4383,8 @@ MAIN:
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
INFO "Cleaning snapshots: $sroot->{PRINT}/$snapdir_ts$snapshot_basename.*";
|
INFO "Cleaning snapshots: $sroot->{PRINT}/$snapdir_ts$snapshot_basename.*";
|
||||||
macro_delete($sroot, $snapdir_ts, $snapshot_basename, $svol,
|
macro_delete($sroot, $snapdir, $snapshot_basename, $svol,
|
||||||
{ preserve => config_preserve_hash($svol, "snapshot"),
|
{ preserve => config_preserve_hash($svol, "snapshot"),
|
||||||
preserve_latest => $preserve_latest_snapshot,
|
|
||||||
results => $schedule_results,
|
results => $schedule_results,
|
||||||
result_hints => { topic => "snapshot", root_path => $sroot->{PATH} },
|
result_hints => { topic => "snapshot", root_path => $sroot->{PATH} },
|
||||||
},
|
},
|
||||||
|
|
19
doc/btrbk.1
19
doc/btrbk.1
|
@ -126,22 +126,23 @@ First, btrbk reads information from the source and target btrfs
|
||||||
filesystems in order to perform sanity checks and identify
|
filesystems in order to perform sanity checks and identify
|
||||||
parent/child and received-from relationships.
|
parent/child and received-from relationships.
|
||||||
.PP
|
.PP
|
||||||
If the checks succeed, btrbk creates snapshots for all the source
|
If the checks succeed, btrbk creates snapshots for the source
|
||||||
subvolumes specified in the configuration file.
|
subvolumes specified in the configuration file, according to the
|
||||||
|
\fIsnapshot_create\fR option.
|
||||||
.PP
|
.PP
|
||||||
Then, for each specified target, btrbk creates the backups as follows:
|
Then, for each specified target, btrbk creates the backups as follows:
|
||||||
After the backups in the target directory are compared to the source
|
After comparing the backups to the source snapshots, btrbk transfers
|
||||||
snapshots, btrbk transfers all missing snapshots needed to satisfy the
|
all missing snapshots needed to satisfy the configured target
|
||||||
configured target retention policy, incrementally from the latest
|
retention policy, incrementally from the latest common parent
|
||||||
common parent subvolume found. If no common parent subvolume is found
|
subvolume found. If no common parent subvolume is found (or if the
|
||||||
(or if the \fIincremental\fR option is set to \[lq]no\[rq]), a full
|
\fIincremental\fR option is set to \[lq]no\[rq]), a full
|
||||||
(non-incremental) backup is created.
|
(non-incremental) backup is created.
|
||||||
.PP
|
.PP
|
||||||
As a last step, unless the \-p (\-\-preserve) option is set, snapshots
|
As a last step, unless the \-p (\-\-preserve) option is set, snapshots
|
||||||
and backup subvolumes that are not preserved by their configured
|
and backup subvolumes that are not preserved by their configured
|
||||||
retention policy will be deleted. Note that the latest snapshot (the
|
retention policy will be deleted. Note that the latest snapshot (the
|
||||||
one created in the first step) is always preserved, regardless of the
|
one created in the first step) as well as the latest snapshot/backup
|
||||||
retention policy.
|
pair are always preserved, regardless of the retention policy.
|
||||||
.PP
|
.PP
|
||||||
Use the \fI\-\-format\fR command line option to switch between
|
Use the \fI\-\-format\fR command line option to switch between
|
||||||
different output formats.
|
different output formats.
|
||||||
|
|
|
@ -100,16 +100,16 @@ valid in the \fIsubvolume\fR section. Defaults to
|
||||||
\fI<subvolume-name>\fR.
|
\fI<subvolume-name>\fR.
|
||||||
.RE
|
.RE
|
||||||
.PP
|
.PP
|
||||||
\fBsnapshot_create\fR always|ondemand|onchange|no
|
\fBsnapshot_create\fR always|onchange|ondemand|no
|
||||||
.RS 4
|
.RS 4
|
||||||
If set to \[lq]ondemand\[rq], snapshots are only created if the target
|
If set to \[lq]always\[rq], snapshots are always created. If set to
|
||||||
subvolume is reachable (useful if you are tight on disk space and you
|
\[lq]onchange\[rq], snapshots are only created if the source subvolume
|
||||||
only need btrbk for backups to an external disk which is not always
|
has changed since the last snapshot (more precisely: if the btrfs
|
||||||
connected). If set to \[lq]onchange\[rq], snapshots are only created
|
generation has been increased since the last snapshot). If set to
|
||||||
if the source subvolume has changed since the last snapshot (more
|
\[lq]ondemand\[rq], snapshots are only created if the target subvolume
|
||||||
precisely: if the btrfs generation has been increased since the last
|
is reachable (useful if you are tight on disk space and you only need
|
||||||
snapshot). If set to \[lq]always\[rq], snapshots are always
|
btrbk for backups to an external disk which is not always
|
||||||
created. If set to \[lq]no\[rq], the snapshots are never created
|
connected). If set to \[lq]no\[rq], the snapshots are never created
|
||||||
(useful if another instance of btrbk is taking care of snapshot
|
(useful if another instance of btrbk is taking care of snapshot
|
||||||
creation). Defaults to \[lq]always\[rq].
|
creation). Defaults to \[lq]always\[rq].
|
||||||
.RE
|
.RE
|
||||||
|
|
Loading…
Reference in New Issue