btrbk: action "origin": use --format options; do not recurse into parent chain

pull/88/head
Axel Burri 2016-04-15 22:00:10 +02:00
parent 1151d2c572
commit edfebb8193
4 changed files with 91 additions and 36 deletions

View File

@ -16,6 +16,7 @@ btrbk-current
* Propagate targets defined in "volume" or "root" context to all * Propagate targets defined in "volume" or "root" context to all
"subvolume" sections (close: #78). "subvolume" sections (close: #78).
* Added "archive" command (close: #79). * Added "archive" command (close: #79).
* Changed output format of "origin" command, add table formats.
* Added configuration option "rate_limit" (close: #72). * Added configuration option "rate_limit" (close: #72).
* Added "--print-schedule" command line option. * Added "--print-schedule" command line option.
* Detect interrupted transfers of raw targets (close: #75). * Detect interrupted transfers of raw targets (close: #75).

View File

@ -22,6 +22,8 @@ Key Features:
* Encrypted backups to non-btrfs destinations * Encrypted backups to non-btrfs destinations
* Wildcard subvolumes (useful for docker and lxc containers) * Wildcard subvolumes (useful for docker and lxc containers)
* Transaction log * Transaction log
* Comprehensive list and statistics output
* Resolve and trace btrfs parent-child and received-from relationships
* Display file changes between two backups * Display file changes between two backups
btrbk is designed to run as a cron job for triggering periodic btrbk is designed to run as a cron job for triggering periodic

119
btrbk
View File

@ -181,6 +181,11 @@ my %table_formats = (
raw => [ qw( time localtime type status duration target_url source_url parent_url message ) ], raw => [ qw( time localtime type status duration target_url source_url parent_url message ) ],
tlog => [ qw( localtime type status duration target_url source_url parent_url message ) ], tlog => [ qw( localtime type status duration target_url source_url parent_url message ) ],
}, },
origin_tree => { table => [ qw( tree uuid parent_uuid received_uuid ) ],
long => [ qw( tree uuid parent_uuid received_uuid recursion ) ],
raw => [ qw( tree uuid parent_uuid received_uuid recursion ) ],
},
); );
my %url_cache; # map URL to btr_tree node my %url_cache; # map URL to btr_tree node
@ -189,7 +194,7 @@ my %uuid_cache; # map UUID to btr_tree node
my %realpath_cache; # map URL to realpath (symlink target) my %realpath_cache; # map URL to realpath (symlink target)
my $tree_inject_id = 0; # fake subvolume id for injected nodes (negative) my $tree_inject_id = 0; # fake subvolume id for injected nodes (negative)
my $fake_uuid_prefix = 'XXXXXXXX-XXXX-XXXX-XXXX-'; # plus 0-padded inject_id my $fake_uuid_prefix = 'XXXXXXXX-XXXX-XXXX-XXXX-'; # plus 0-padded inject_id: XXXXXXXX-XXXX-XXXX-XXXX-000000000000
my $dryrun; my $dryrun;
my $loglevel = 1; my $loglevel = 1;
@ -3098,28 +3103,80 @@ sub print_formatted(@)
sub _origin_tree sub _origin_tree
{ {
my $prefix = shift; my $prefix = shift;
my $uuid = shift; my $node = shift // die;
my $lines = shift; my $lines = shift;
my $node = $uuid_cache{$uuid}; my $nodelist = shift;
unless($node) { my $depth = shift // 0;
push(@$lines, ["$prefix<orphaned>", $uuid]); my $seen = shift // [];
return 0; my $norecurse = shift;
} my $uuid = $node->{uuid} || die;
# cache a bit, this might be large
$nodelist //= [ (sort { $a->{REL_PATH} cmp $b->{REL_PATH} } values %uuid_cache) ];
my @url = get_cached_url_by_uuid($uuid); my @url = get_cached_url_by_uuid($uuid);
my $out_path;
if(scalar @url) { if(scalar @url) {
push(@$lines, ["$prefix" . join(" === ", sort map { vinfo($_)->{PRINT} } @url), $uuid]); $out_path = join(" === ", sort map { vinfo($_)->{PRINT} } @url);
} else { } else {
push(@$lines, ["$prefix<BTRFS_ROOT>/$node->{path}", $uuid]); $out_path = _fs_path($node);
}
my $prefix_spaces = ' ' x (($depth * 4) - ($prefix ? 4 : 0));
push(@$lines, { tree => "${prefix_spaces}${prefix}$out_path",
uuid => $node->{uuid},
parent_uuid => $node->{parent_uuid},
received_uuid => $node->{received_uuid},
});
# handle deep recursion
return 0 if(grep /^$uuid$/, @$seen);
if($node->{parent_uuid} ne '-') {
my $parent_node = $uuid_cache{$node->{parent_uuid}};
if($parent_node) {
if($norecurse) {
push(@$lines,{ tree => "${prefix_spaces} ^-- ...",
uuid => $parent_node->{uuid},
parent_uuid => $parent_node->{parent_uuid},
received_uuid => $parent_node->{received_uuid},
recursion => 'stop_recursion',
});
return 0;
}
if($parent_node->{readonly}) {
_origin_tree("^-- ", $parent_node, $lines, $nodelist, $depth + 1, undef, 1); # end recursion
}
else {
_origin_tree("^-- ", $parent_node, $lines, $nodelist, $depth + 1);
}
}
else {
push(@$lines,{ tree => "${prefix_spaces} ^-- <unknown>" });
}
} }
$prefix =~ s/./ /g; return 0 if($norecurse);
push(@$seen, $uuid);
if($node->{received_uuid} ne '-') { if($node->{received_uuid} ne '-') {
_origin_tree("${prefix}^-- ", $node->{received_uuid}, $lines); my $received_uuid = $node->{received_uuid};
} my @receive_parents; # there should be only one!
if($node->{parent_uuid} ne '-') { my @receive_twins;
_origin_tree("${prefix}", $node->{parent_uuid}, $lines);
foreach (@$nodelist) {
next if($_->{uuid} eq $uuid);
if($received_uuid eq $_->{uuid} && $_->{readonly}) {
_origin_tree("", $_, \@receive_parents, $nodelist, $depth, $seen);
}
elsif(($_->{received_uuid} ne '-') && ($received_uuid eq $_->{received_uuid}) && $_->{readonly}) {
_origin_tree("", $_, \@receive_twins, $nodelist, $depth, $seen, 1); # end recursion
}
}
push @$lines, @receive_twins;
push @$lines, @receive_parents;
} }
return 0;
} }
@ -4077,8 +4134,6 @@ MAIN:
# print origin information # print origin information
# #
my $url = $filter_args[0] || die; my $url = $filter_args[0] || die;
my $dump_uuid = 0;
my $vol = vinfo($url, $config); my $vol = vinfo($url, $config);
unless(vinfo_init_root($vol)) { unless(vinfo_init_root($vol)) {
ERROR "Failed to fetch subvolume detail for: $url" . ($err ? ": $err" : ""); ERROR "Failed to fetch subvolume detail for: $url" . ($err ? ": $err" : "");
@ -4090,26 +4145,22 @@ MAIN:
} }
my $lines = []; my $lines = [];
_origin_tree("", $vol->{node}{uuid}, $lines); _origin_tree("", $vol->{node}, $lines);
print_header(title => "Origin Tree", $output_format ||= "custom";
config => $config, if($output_format eq "custom") {
time => $start_time, print_header(title => "Origin Tree",
legend => [ config => $config,
"^-- : received from subvolume", time => $start_time,
"newline : parent subvolume", legend => [
"orphaned: subvolume uuid could not be resolved (probably deleted)", "^-- : parent subvolume",
] "newline : received-from relationship with subvolume (identical content)",
); ]
);
my $len = 0; print join("\n", map { $_->{tree} } @$lines) . "\n";
if($dump_uuid) {
$len = (length($_->[0]) > $len ? length($_->[0]) : $len) foreach(@$lines);
} }
foreach(@$lines) { else {
print "$_->[0]"; print_formatted('origin_tree', $lines );
print ' ' x ($len - length($_->[0]) + 4) . "$_->[1]" if($dump_uuid);
print "\n";
} }
exit 0; exit 0;
} }

View File

@ -254,8 +254,9 @@ STATEMENTS\fR below).
.B origin .B origin
<subvolume> <subvolume>
.RS 4 .RS 4
Print origin information for the given backup subvolume, showing the Print the subvolume origin tree: Shows the parent-child relationships
parent-child relationship as well as the received-from information. as well as the received-from information. Use the \fI\-\-format\fR
command line option to switch between different output formats.
.RE .RE
.PP .PP
.B diff .B diff