|
|
|
@ -64,6 +64,7 @@ my %config_options = (
|
|
|
|
|
receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => { absolute => 1 }, deprecated => "removed" },
|
|
|
|
|
incremental => { default => "yes", accept => [ "yes", "no", "strict" ] },
|
|
|
|
|
snapshot_create_always => { default => undef, accept => [ "yes", "no" ] },
|
|
|
|
|
resume_missing => { default => undef, accept => [ "yes", "no" ] },
|
|
|
|
|
preserve_day_of_week => { default => "sunday", accept => [ (keys %day_of_week_map) ] },
|
|
|
|
|
snapshot_preserve_daily => { default => "all", accept => [ "all" ], accept_numeric => 1 },
|
|
|
|
|
snapshot_preserve_weekly => { default => 0, accept => [ "all" ], accept_numeric => 1 },
|
|
|
|
@ -801,7 +802,7 @@ sub btrfs_subvolume_delete($@)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub btrfs_send_receive($$$$;$)
|
|
|
|
|
sub btrfs_send_receive($$$;$)
|
|
|
|
|
{
|
|
|
|
|
my $src = shift || die;
|
|
|
|
|
my $target = shift || die;
|
|
|
|
@ -835,19 +836,80 @@ sub btrfs_send_receive($$$$;$)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# sets $config->{ABORTED} on failure
|
|
|
|
|
# sets $config->{subvol_received}
|
|
|
|
|
sub macro_send_receive($@)
|
|
|
|
|
{
|
|
|
|
|
my $config = shift || die;
|
|
|
|
|
my %info = @_;
|
|
|
|
|
my $incremental = config_key($config, "incremental");
|
|
|
|
|
|
|
|
|
|
INFO "Receiving from snapshot: $info{src}";
|
|
|
|
|
|
|
|
|
|
# add info to $config->{subvol_received}
|
|
|
|
|
my $src_name = $info{src};
|
|
|
|
|
$src_name =~ s/^.*\///;
|
|
|
|
|
$info{received_name} = "$info{target}/$src_name";
|
|
|
|
|
$config->{subvol_received} //= [];
|
|
|
|
|
push(@{$config->{subvol_received}}, \%info);
|
|
|
|
|
|
|
|
|
|
if($incremental)
|
|
|
|
|
{
|
|
|
|
|
# create backup from latest common
|
|
|
|
|
if($info{parent}) {
|
|
|
|
|
INFO "Incremental from parent snapshot: $info{parent}";
|
|
|
|
|
}
|
|
|
|
|
elsif($incremental ne "strict") {
|
|
|
|
|
INFO "No common parent subvolume present, creating full backup";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
WARN "Backup to $info{target} failed: no common parent subvolume found, and option \"incremental\" is set to \"strict\"";
|
|
|
|
|
$info{ERROR} = 1;
|
|
|
|
|
$config->{ABORTED} = "No common parent subvolume found, and option \"incremental\" is set to \"strict\"";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
INFO "Option \"incremental\" is not set, creating full backup";
|
|
|
|
|
delete $info{parent};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(btrfs_send_receive($info{src}, $info{target}, $info{parent}, $config)) {
|
|
|
|
|
return 1;
|
|
|
|
|
} else {
|
|
|
|
|
$info{ERROR} = 1;
|
|
|
|
|
$config->{ABORTED} = "btrfs send/receive command failed";
|
|
|
|
|
return undef;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub get_date_tag($)
|
|
|
|
|
{
|
|
|
|
|
my $name = shift;
|
|
|
|
|
$name =~ s/_([0-9]+)$//;
|
|
|
|
|
my $postfix_counter = $1;
|
|
|
|
|
my $date = undef;
|
|
|
|
|
if($name =~ /\.([0-9]{4})([0-9]{2})([0-9]{2})$/) {
|
|
|
|
|
$date = [ $1, $2, $3 ];
|
|
|
|
|
}
|
|
|
|
|
return ($date, $postfix_counter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub get_snapshot_children($$)
|
|
|
|
|
{
|
|
|
|
|
my $sroot = shift || die;
|
|
|
|
|
my $svol = shift || die;
|
|
|
|
|
my $svol_node = subvol($sroot, $svol);
|
|
|
|
|
die("subvolume info not present: $sroot/$svol") unless($svol_node);
|
|
|
|
|
DEBUG "Getting snapshot children of: $sroot/$svol";
|
|
|
|
|
my @ret;
|
|
|
|
|
foreach (values %{$vol_info{$sroot}}) {
|
|
|
|
|
next unless($_->{node}->{parent_uuid} eq $svol_node->{uuid});
|
|
|
|
|
DEBUG "Found snapshot child: $_->{SUBVOL_PATH}";
|
|
|
|
|
TRACE "get_snapshot_children: Found snapshot child: $_->{SUBVOL_PATH}";
|
|
|
|
|
push(@ret, $_);
|
|
|
|
|
}
|
|
|
|
|
DEBUG "Found " . scalar(@ret) . " snapshot children of: $sroot/$svol";
|
|
|
|
|
return @ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -858,29 +920,44 @@ sub get_receive_targets_by_uuid($$)
|
|
|
|
|
my $uuid = shift || die;
|
|
|
|
|
die("root subvolume info not present: $droot") unless($vol_info{$droot});
|
|
|
|
|
die("subvolume info not present: $uuid") unless($uuid_info{$uuid});
|
|
|
|
|
DEBUG "Getting receive targets in \"$droot/\" for: $uuid_info{$uuid}->{path}";
|
|
|
|
|
my @ret;
|
|
|
|
|
foreach (values %{$vol_info{$droot}}) {
|
|
|
|
|
next unless($_->{node}->{received_uuid} eq $uuid);
|
|
|
|
|
DEBUG "Found receive target: $_->{SUBVOL_PATH}";
|
|
|
|
|
TRACE "get_receive_targets_by_uuid: Found receive target: $_->{SUBVOL_PATH}";
|
|
|
|
|
push(@ret, $_);
|
|
|
|
|
}
|
|
|
|
|
DEBUG "Found " . scalar(@ret) . " receive targets in \"$droot/\" for: $uuid_info{$uuid}->{path}";
|
|
|
|
|
return @ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sub get_latest_common($$$)
|
|
|
|
|
sub get_latest_common($$$;$)
|
|
|
|
|
{
|
|
|
|
|
my $sroot = shift || die;
|
|
|
|
|
my $svol = shift || die;
|
|
|
|
|
my $droot = shift || die;
|
|
|
|
|
my $threshold_gen = shift; # skip all snapshot children with generation >= $threshold_gen
|
|
|
|
|
|
|
|
|
|
die("source subvolume info not present: $sroot") unless($vol_info{$sroot});
|
|
|
|
|
die("target subvolume info not present: $droot") unless($vol_info{$droot});
|
|
|
|
|
|
|
|
|
|
my $debug_src = "$sroot/$svol";
|
|
|
|
|
$debug_src .= "@" . $threshold_gen if($threshold_gen);
|
|
|
|
|
|
|
|
|
|
# sort children of svol descending by generation
|
|
|
|
|
foreach my $child (sort { $b->{node}->{gen} <=> $a->{node}->{gen} } get_snapshot_children($sroot, $svol)) {
|
|
|
|
|
TRACE "get_latest_common: checking source snapshot: $child->{SUBVOL_PATH}";
|
|
|
|
|
if($threshold_gen && ($child->{node}->{gen} >= $threshold_gen)) {
|
|
|
|
|
TRACE "get_latest_common: skipped gen=$child->{node}->{gen} >= $threshold_gen: $child->{SUBVOL_PATH}";
|
|
|
|
|
next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($child->{RECEIVE_TARGET_PRESENT} && ($child->{RECEIVE_TARGET_PRESENT} eq $droot)) {
|
|
|
|
|
# little hack to keep track of previously received subvolumes
|
|
|
|
|
DEBUG("Latest common snapshots for: $debug_src: src=$child->{FS_PATH} target=<previously received>");
|
|
|
|
|
return ($child, undef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($vol_btrfs_progs_compat{$droot}) {
|
|
|
|
|
# guess matches by subvolume name (node->received_uuid is not available if BTRFS_PROGS_COMPAT is set)
|
|
|
|
|
my $child_name = $child->{node}->{REL_PATH};
|
|
|
|
@ -889,7 +966,7 @@ sub get_latest_common($$$)
|
|
|
|
|
my $backup_name = $backup->{node}->{REL_PATH};
|
|
|
|
|
$backup_name =~ s/^.*\///; # strip path
|
|
|
|
|
if($backup_name eq $child_name) {
|
|
|
|
|
DEBUG("Latest common snapshots for: $sroot/$svol: src=$child->{FS_PATH} target=$backup->{FS_PATH} (NOTE: guessed by subvolume name)");
|
|
|
|
|
DEBUG("Latest common snapshots for: $debug_src: src=$child->{FS_PATH} target=$backup->{FS_PATH} (NOTE: guessed by subvolume name)");
|
|
|
|
|
return ($child, $backup);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -897,13 +974,13 @@ sub get_latest_common($$$)
|
|
|
|
|
else {
|
|
|
|
|
foreach (get_receive_targets_by_uuid($droot, $child->{node}->{uuid})) {
|
|
|
|
|
TRACE "get_latest_common: found receive target: $_->{FS_PATH}";
|
|
|
|
|
DEBUG("Latest common snapshots for: $sroot/$svol: src=$child->{FS_PATH} target=$_->{FS_PATH}");
|
|
|
|
|
DEBUG("Latest common snapshots for: $debug_src: src=$child->{FS_PATH} target=$_->{FS_PATH}");
|
|
|
|
|
return ($child, $_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TRACE "get_latest_common: no matching targets found for: $child->{FS_PATH}";
|
|
|
|
|
}
|
|
|
|
|
DEBUG("No common snapshots for \"$sroot/$svol\" found in src=$sroot/ target=$droot/");
|
|
|
|
|
DEBUG("No common snapshots for \"$debug_src\" found in src=$sroot/ target=$droot/");
|
|
|
|
|
return (undef, undef);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -949,16 +1026,19 @@ sub schedule_deletion(@)
|
|
|
|
|
my $preserve_daily = $args{preserve_daily} // die;
|
|
|
|
|
my $preserve_weekly = $args{preserve_weekly} // die;
|
|
|
|
|
my $preserve_monthly = $args{preserve_monthly} // die;
|
|
|
|
|
my $log_verbose = $args{log_verbose};
|
|
|
|
|
|
|
|
|
|
INFO "Filter scheme: preserving all within $preserve_daily days";
|
|
|
|
|
INFO "Filter scheme: preserving first in week (starting on $preserve_day_of_week), for $preserve_weekly weeks";
|
|
|
|
|
INFO "Filter scheme: preserving last weekly of month, for $preserve_monthly months";
|
|
|
|
|
if($log_verbose) {
|
|
|
|
|
INFO "Filter scheme: preserving all within $preserve_daily days";
|
|
|
|
|
INFO "Filter scheme: preserving first in week (starting on $preserve_day_of_week), for $preserve_weekly weeks";
|
|
|
|
|
INFO "Filter scheme: preserving last weekly of month, for $preserve_monthly months";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# first, do our calendar calculations
|
|
|
|
|
# note: our week starts on $preserve_day_of_week
|
|
|
|
|
my $delta_days_to_eow_from_today = $day_of_week_map{$preserve_day_of_week} - Day_of_Week(@today) - 1;
|
|
|
|
|
$delta_days_to_eow_from_today = $delta_days_to_eow_from_today + 7 if($delta_days_to_eow_from_today < 0);
|
|
|
|
|
DEBUG "last day before next $preserve_day_of_week is in $delta_days_to_eow_from_today days";
|
|
|
|
|
TRACE "last day before next $preserve_day_of_week is in $delta_days_to_eow_from_today days";
|
|
|
|
|
foreach my $href (@$schedule)
|
|
|
|
|
{
|
|
|
|
|
my @date = @{$href->{date}};
|
|
|
|
@ -1002,13 +1082,14 @@ sub schedule_deletion(@)
|
|
|
|
|
foreach my $href (sort { $a->{sort} cmp $b->{sort} } @$schedule)
|
|
|
|
|
{
|
|
|
|
|
if($href->{preserve}) {
|
|
|
|
|
INFO "=== $href->{sort}: $href->{preserve}";
|
|
|
|
|
INFO "=== $href->{sort}: $href->{preserve}" if($log_verbose);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
INFO "<<< $href->{sort}";
|
|
|
|
|
INFO "<<< $href->{sort}" if($log_verbose);
|
|
|
|
|
push(@delete, $href->{name});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
DEBUG "Preserving " . (@$schedule - @delete) . "/" . @$schedule . " items" unless($log_verbose);
|
|
|
|
|
return @delete;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1511,48 +1592,95 @@ MAIN:
|
|
|
|
|
my $droot = $config_target->{droot} || die;
|
|
|
|
|
my $target_type = $config_target->{target_type} || die;
|
|
|
|
|
|
|
|
|
|
my $success = 0;
|
|
|
|
|
if($target_type eq "send-receive")
|
|
|
|
|
{
|
|
|
|
|
INFO "Creating subvolume backup ($target_type) for: $sroot/$svol";
|
|
|
|
|
INFO "Using previously created snapshot: $snapshot";
|
|
|
|
|
|
|
|
|
|
if(config_key($config_target, "receive_log")) {
|
|
|
|
|
WARN "Ignoring deprecated option \"receive_log\" for target: $droot"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my $incremental = config_key($config_target, "incremental");
|
|
|
|
|
if($incremental)
|
|
|
|
|
my $parent_snap = "";
|
|
|
|
|
|
|
|
|
|
# resume missing backups (resume_missing)
|
|
|
|
|
if(config_key($config_target, "resume_missing"))
|
|
|
|
|
{
|
|
|
|
|
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot);
|
|
|
|
|
if($latest_common_src && $latest_common_target) {
|
|
|
|
|
my $parent_snap = $latest_common_src->{FS_PATH};
|
|
|
|
|
INFO "Incremental from parent snapshot: $parent_snap";
|
|
|
|
|
$success = btrfs_send_receive($snapshot, $droot, $parent_snap, $config_target);
|
|
|
|
|
INFO "Checking for missing backups of subvolume \"$sroot/$svol\" in: $droot/";
|
|
|
|
|
my $found_missing = 0;
|
|
|
|
|
# sort children of svol ascending by generation
|
|
|
|
|
foreach my $child (sort { $a->{node}->{gen} <=> $b->{node}->{gen} } get_snapshot_children($sroot, $svol))
|
|
|
|
|
{
|
|
|
|
|
last if($config_target->{ABORTED});
|
|
|
|
|
|
|
|
|
|
DEBUG "Checking for missing receive targets for \"$child->{FS_PATH}\" in: $droot/";
|
|
|
|
|
|
|
|
|
|
# TODO: fix for btrfs_progs_compat
|
|
|
|
|
if(scalar get_receive_targets_by_uuid($droot, $child->{node}->{uuid})) {
|
|
|
|
|
DEBUG "Found matching receive target, skipping: $child->{FS_PATH}";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
DEBUG "No matching receive targets found, checking schedule for: $child->{FS_PATH}";
|
|
|
|
|
|
|
|
|
|
# check if the target would be preserved
|
|
|
|
|
my ($date, undef) = get_date_tag($child->{SUBVOL_PATH});
|
|
|
|
|
next unless($date);
|
|
|
|
|
if(scalar schedule_deletion(
|
|
|
|
|
schedule => [ { name => $child->{FS_PATH}, sort => $child->{SUBVOL_PATH}, date => $date } ],
|
|
|
|
|
today => \@today,
|
|
|
|
|
preserve_day_of_week => config_key($config_target, "preserve_day_of_week"),
|
|
|
|
|
preserve_daily => config_key($config_target, "target_preserve_daily"),
|
|
|
|
|
preserve_weekly => config_key($config_target, "target_preserve_weekly"),
|
|
|
|
|
preserve_monthly => config_key($config_target, "target_preserve_monthly"),
|
|
|
|
|
))
|
|
|
|
|
{
|
|
|
|
|
DEBUG "Target would have been deleted by target_perserve rules, skipping resume of: $child->{FS_PATH}";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
$found_missing++;
|
|
|
|
|
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot, $child->{node}->{gen});
|
|
|
|
|
$parent_snap = $latest_common_src->{FS_PATH} if($latest_common_src);
|
|
|
|
|
|
|
|
|
|
INFO "Resuming subvolume backup (send-receive) for: $child->{FS_PATH}";
|
|
|
|
|
if(macro_send_receive($config_target,
|
|
|
|
|
src => $child->{FS_PATH},
|
|
|
|
|
target => $droot,
|
|
|
|
|
parent => $parent_snap,
|
|
|
|
|
resume => 1, # propagated to $config_target->{subvol_received}
|
|
|
|
|
))
|
|
|
|
|
{
|
|
|
|
|
# tag the source snapshot, so that get_latest_common() above can make use of the newly received subvolume
|
|
|
|
|
$child->{RECEIVE_TARGET_PRESENT} = $droot;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
# note: ABORTED flag is already set by macro_send_receive()
|
|
|
|
|
ERROR("Error while resuming backups, aborting");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
elsif($incremental ne "strict") {
|
|
|
|
|
INFO "No common parent subvolume present, creating full backup";
|
|
|
|
|
$config_target->{subvol_non_incremental} = 1;
|
|
|
|
|
$success = btrfs_send_receive($snapshot, $droot, undef, $config_target);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
WARN "Backup to $droot failed: no common parent subvolume found, and option \"incremental\" is set to \"strict\"";
|
|
|
|
|
|
|
|
|
|
if($found_missing) {
|
|
|
|
|
INFO "Resumed $found_missing backups";
|
|
|
|
|
} else {
|
|
|
|
|
INFO "No missing backups found";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
INFO "Creating full backup (option \"incremental\" is not set)";
|
|
|
|
|
$config_target->{subvol_non_incremental} = 1;
|
|
|
|
|
$success = btrfs_send_receive($snapshot, $droot, undef, $config_target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# skip creation if resume_missing failed
|
|
|
|
|
next if($config_target->{ABORTED});
|
|
|
|
|
|
|
|
|
|
# finally receive the previously created snapshot
|
|
|
|
|
INFO "Creating subvolume backup (send-receive) for: $sroot/$svol";
|
|
|
|
|
my ($latest_common_src, $latest_common_target) = get_latest_common($sroot, $svol, $droot);
|
|
|
|
|
$parent_snap = $latest_common_src ? $latest_common_src->{FS_PATH} : undef;
|
|
|
|
|
macro_send_receive($config_target,
|
|
|
|
|
src => $snapshot,
|
|
|
|
|
target => $droot,
|
|
|
|
|
parent => $parent_snap
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ERROR "Unknown target type \"$target_type\", skipping: $sroot/$svol";
|
|
|
|
|
}
|
|
|
|
|
if($success) {
|
|
|
|
|
$config_target->{subvol_created} = "$droot/$snapshot_name";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
$config_target->{ABORTED} = "btrfs send/receive command failed";
|
|
|
|
|
$config_target->{ABORTED} = "Unknown target type \"$target_type\"";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1591,9 +1719,9 @@ MAIN:
|
|
|
|
|
INFO "Cleaning backups of subvolume \"$sroot/$svol\": $droot/$svol.*";
|
|
|
|
|
my @schedule;
|
|
|
|
|
foreach my $vol (keys %{$vol_info{$droot}}) {
|
|
|
|
|
if($vol =~ /^$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
|
|
|
|
push(@schedule, { name => "$droot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
|
|
|
|
}
|
|
|
|
|
my ($date, undef) = get_date_tag($vol);
|
|
|
|
|
next unless($date && ($vol =~ /^svol\./));
|
|
|
|
|
push(@schedule, { name => "$droot/$vol", sort => $vol, date => $date });
|
|
|
|
|
}
|
|
|
|
|
my @delete = schedule_deletion(
|
|
|
|
|
schedule => \@schedule,
|
|
|
|
@ -1602,6 +1730,7 @@ MAIN:
|
|
|
|
|
preserve_daily => config_key($config_target, "target_preserve_daily"),
|
|
|
|
|
preserve_weekly => config_key($config_target, "target_preserve_weekly"),
|
|
|
|
|
preserve_monthly => config_key($config_target, "target_preserve_monthly"),
|
|
|
|
|
log_verbose => 1,
|
|
|
|
|
);
|
|
|
|
|
my $ret = btrfs_subvolume_delete($config_target, @delete);
|
|
|
|
|
if(defined($ret)) {
|
|
|
|
@ -1625,9 +1754,9 @@ MAIN:
|
|
|
|
|
INFO "Cleaning snapshots: $sroot/$snapdir$svol.*";
|
|
|
|
|
my @schedule;
|
|
|
|
|
foreach my $vol (keys %{$vol_info{$sroot}}) {
|
|
|
|
|
if($vol =~ /^$snapdir$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
|
|
|
|
push(@schedule, { name => "$sroot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
|
|
|
|
}
|
|
|
|
|
my ($date, undef) = get_date_tag($vol);
|
|
|
|
|
next unless($date && ($vol =~ /^$snapdir$svol\./));
|
|
|
|
|
push(@schedule, { name => "$sroot/$vol", sort => $vol, date => $date });
|
|
|
|
|
}
|
|
|
|
|
my @delete = schedule_deletion(
|
|
|
|
|
schedule => \@schedule,
|
|
|
|
@ -1636,6 +1765,7 @@ MAIN:
|
|
|
|
|
preserve_daily => config_key($config_subvol, "snapshot_preserve_daily"),
|
|
|
|
|
preserve_weekly => config_key($config_subvol, "snapshot_preserve_weekly"),
|
|
|
|
|
preserve_monthly => config_key($config_subvol, "snapshot_preserve_monthly"),
|
|
|
|
|
log_verbose => 1,
|
|
|
|
|
);
|
|
|
|
|
my $ret = btrfs_subvolume_delete($config_subvol, @delete);
|
|
|
|
|
if(defined($ret)) {
|
|
|
|
@ -1663,6 +1793,12 @@ MAIN:
|
|
|
|
|
print "Backup Summary ($version_info)\n\n";
|
|
|
|
|
print " Date: " . localtime($start_time) . "\n";
|
|
|
|
|
print " Config: $config->{SRC_FILE}\n";
|
|
|
|
|
print "\nLegend:\n";
|
|
|
|
|
print " +++ created subvolume (source snapshot)\n";
|
|
|
|
|
print " --- deleted subvolume (source snapshot)\n";
|
|
|
|
|
print " *** received subvolume (non-incremental)\n";
|
|
|
|
|
print " >>> received subvolume (incremental)\n";
|
|
|
|
|
# print " %>> received subvolume (incremental, resume_missing)\n";
|
|
|
|
|
print "--------------------------------------------------------------------------------";
|
|
|
|
|
foreach my $config_vol (@{$config->{VOLUME}})
|
|
|
|
|
{
|
|
|
|
@ -1688,20 +1824,28 @@ MAIN:
|
|
|
|
|
}
|
|
|
|
|
foreach my $config_target (@{$config_subvol->{TARGET}})
|
|
|
|
|
{
|
|
|
|
|
if($config_target->{ABORTED}) {
|
|
|
|
|
print "!!! Target \"$config_target->{droot}\" aborted: $config_target->{ABORTED}\n";
|
|
|
|
|
$err_count++ unless($config_target->{ABORTED_NOERR});
|
|
|
|
|
}
|
|
|
|
|
# if($config_target->{schedule}) {
|
|
|
|
|
# foreach (sort { $a->{sort} cmp $b->{sort} } @{$config_target->{schedule}}) {
|
|
|
|
|
# print(($_->{preserve} ? "===" : "---") . " $_->{name}\n");
|
|
|
|
|
# }
|
|
|
|
|
# }
|
|
|
|
|
my $create_mode = ($config_target->{subvol_non_incremental} ? "***" : ">>>");
|
|
|
|
|
print "$create_mode $config_target->{subvol_created}\n" if($config_target->{subvol_created});
|
|
|
|
|
|
|
|
|
|
foreach(@{$config_target->{subvol_received} // []}) {
|
|
|
|
|
my $create_mode = "***";
|
|
|
|
|
$create_mode = ">>>" if($_->{parent});
|
|
|
|
|
# substr($create_mode, 0, 1, '%') if($_->{resume});
|
|
|
|
|
$create_mode = "!!!" if($_->{ERROR});
|
|
|
|
|
print "$create_mode $_->{received_name}\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($config_target->{subvol_deleted}) {
|
|
|
|
|
print "--- $_\n" foreach(sort { $b cmp $a} @{$config_target->{subvol_deleted}});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if($config_target->{ABORTED}) {
|
|
|
|
|
print "!!! Target \"$config_target->{droot}\" aborted: $config_target->{ABORTED}\n";
|
|
|
|
|
$err_count++ unless($config_target->{ABORTED_NOERR});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|