btrbk: replace "info" command with "usage", with tabular output; add support for right-aligned columns

pull/57/head
Axel Burri 2015-10-19 22:10:08 +02:00
parent fd985d0245
commit 05bfeaff1b
3 changed files with 110 additions and 58 deletions

View File

@ -13,6 +13,7 @@ btrbk-current
(experimental). (experimental).
* Added configuration option "timestamp_format short|long". * Added configuration option "timestamp_format short|long".
* Added transaction log (configuration option "transaction_log"). * Added transaction log (configuration option "transaction_log").
* Replaced "info" command with "usage", with tabular output.
* Bugfix: correctly handle "incremental no" option. * Bugfix: correctly handle "incremental no" option.
* Bugfix: return exit status 10 instead of 0 if one or more backup * Bugfix: return exit status 10 instead of 0 if one or more backup
tasks aborted. tasks aborted.

165
btrbk
View File

@ -142,11 +142,17 @@ my %table_formats = (
raw => [ qw( type source_host source_path snapshot_path snapshot_name status target_host target_path source_rsh ) ], raw => [ qw( type source_host source_path snapshot_path snapshot_name status target_host target_path source_rsh ) ],
}, },
schedule => { table => [ qw( action target_host target_subvol scheme reason ) ], schedule => { table => [ qw( action host subvol scheme reason ) ],
long => [ qw( action host root_path name scheme reason ) ], long => [ qw( action host root_path subvol_path scheme reason ) ],
raw => [ qw( topic action url host path dow d m w) ], raw => [ qw( topic action url host path dow d m w) ],
}, },
usage => { table => [ qw( host path size used free ) ],
long => [ qw( type host path size device_allocated device_unallocated device_missing used free free_min data_ratio metadata_ratio used global_reserve global_reserve_used ) ],
raw => [ qw( type host path size device_allocated device_unallocated device_missing used free free_min data_ratio metadata_ratio used global_reserve global_reserve_used ) ],
RALIGN => { size=>1, device_allocated=>1, device_unallocated=>1, device_missing=>1, used=>1, free=>1, free_min=>1, data_ratio=>1, metadata_ratio=>1, used=>1, global_reserve=>1, global_reserve_used=>1 },
},
action_log => { table => [ qw( type status target_host target_subvol source_host source_subvol parent_subvol ) ], action_log => { table => [ qw( type status target_host target_subvol source_host source_subvol parent_subvol ) ],
long => [ qw( localtime type status duration target_host target_subvol source_host source_subvol parent_subvol message ) ], long => [ qw( localtime type status duration target_host target_subvol source_host source_subvol parent_subvol message ) ],
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 ) ],
@ -207,7 +213,7 @@ sub HELP_MESSAGE
print STDERR " resolve snapshots [filter...] shows snapshots and corresponding targets\n"; print STDERR " resolve snapshots [filter...] shows snapshots and corresponding targets\n";
print STDERR " resolve targets [filter...] shows targets and corresponding snapshots\n"; print STDERR " resolve targets [filter...] shows targets and corresponding snapshots\n";
print STDERR " resolve latest [filter...] shows latest snapshots/targets\n"; print STDERR " resolve latest [filter...] shows latest snapshots/targets\n";
print STDERR " info [filter...] print useful filesystem information\n"; print STDERR " usage [filter...] print filesystem usage\n";
print STDERR " origin <subvol> print origin information for subvolume\n"; print STDERR " origin <subvol> print origin information for subvolume\n";
print STDERR " diff <from> <to> shows new files since subvolume <from> for subvolume <to>\n"; print STDERR " diff <from> <to> shows new files since subvolume <from> for subvolume <to>\n";
print STDERR "\n"; print STDERR "\n";
@ -468,16 +474,19 @@ sub vinfo_set_detail($$)
# returns hash: ( $prefix_{url,path,host,name,subvol_path,rsh} => value, ... ) # returns hash: ( $prefix_{url,path,host,name,subvol_path,rsh} => value, ... )
sub vinfo_prefixed_keys($$) sub vinfo_prefixed_keys($$)
{ {
my $prefix = shift || die; my $prefix = shift // die;
my $vinfo = shift; my $vinfo = shift;
return () unless($vinfo); return () unless($vinfo);
my %ret; my %ret;
foreach (qw( URL PATH HOST NAME SUBVOL_PATH )) { if($prefix) {
$ret{$prefix . '_' . lc($_)} = $vinfo->{$_}; $ret{$prefix} = $vinfo->{PRINT};
$prefix .= '_';
} }
$ret{$prefix . "_subvol"} = $vinfo->{PATH}; foreach (qw( URL PATH HOST NAME SUBVOL_PATH )) {
$ret{$prefix} = $vinfo->{PRINT}; $ret{$prefix . lc($_)} = $vinfo->{$_};
$ret{$prefix . "_rsh"} = ($vinfo->{RSH} ? join(" ", @{$vinfo->{RSH}}) : undef), }
$ret{$prefix . "subvol"} = $vinfo->{PATH};
$ret{$prefix . "rsh"} = ($vinfo->{RSH} ? join(" ", @{$vinfo->{RSH}}) : undef),
return %ret; return %ret;
} }
@ -818,10 +827,53 @@ sub btrfs_filesystem_usage($)
{ {
my $vol = shift || die; my $vol = shift || die;
my $path = $vol->{PATH} // die; my $path = $vol->{PATH} // die;
return run_cmd( cmd => [ qw(btrfs filesystem usage), $path ], my $ret = run_cmd( cmd => [ qw(btrfs filesystem usage), $path ],
rsh => $vol->{RSH}, rsh => $vol->{RSH},
non_destructive => 1 non_destructive => 1
); );
return undef unless(defined($ret));
my %detail;
foreach (split("\n", $ret)) {
if(/^\s+Device size:\s+(\S+)/) {
$detail{size} = $1;
}
elsif(/^\s+Device allocated:\s+(\S+)/) {
$detail{device_allocated} = $1;
}
elsif(/^\s+Device unallocated:\s+(\S+)/) {
$detail{device_unallocated} = $1;
}
elsif(/^\s+Device missing:\s+(\S+)/) {
$detail{device_missing} = $1;
}
elsif(/^\s+Used:\s+(\S+)/) {
$detail{used} = $1;
}
elsif(/^\s+Free \(estimated\):\s+(\S+)\s+\(min: (\S+)\)/) {
$detail{free} = $1;
$detail{free_min} = $2;
}
elsif(/^\s+Data ratio:\s+(\S+)/) {
$detail{data_ratio} = $1;
}
elsif(/^\s+Metadata ratio:\s+(\S+)/) {
$detail{metadata_ratio} = $1;
}
elsif(/^\s+Used:\s+(\S+)/) {
$detail{used} = $1;
}
elsif(/^\s+Global reserve:\s+(\S+)\s+\(used: (\S+)\)/) {
$detail{global_reserve} = $1;
$detail{global_reserve_used} = $2;
}
else {
TRACE "Failed to parse filesystem usage line \"$_\" for: $vol->{PRINT}";
}
DEBUG "Parsed " . scalar(keys %detail) . " filesystem usage detail items: $vol->{PRINT}";
TRACE(Data::Dumper->Dump([$vol], ["btrfs_subvolume_detail($vol->{URL})"]));
}
return \%detail;
} }
@ -1854,6 +1906,8 @@ sub print_formatted(@)
my $title = $args{title}; my $title = $args{title};
my $format = $args{output_format} || $output_format || $default_format; my $format = $args{output_format} || $output_format || $default_format;
my $keys = $table_formats{$format_key}->{$format}; my $keys = $table_formats{$format_key}->{$format};
my $ralign = $table_formats{$format_key}->{RALIGN} // {};
my $table_spacing = 2;
unless($keys) { unless($keys) {
WARN "Unsupported output format \"$format\", defaulting to \"$default_format\" format."; WARN "Unsupported output format \"$format\", defaulting to \"$default_format\" format.";
@ -1905,21 +1959,33 @@ sub print_formatted(@)
} }
# print keys (headings) # print keys (headings)
my $fill = ''; my $fill = 0;
foreach (@$keys) { foreach (@$keys) {
print $fill . $_; print ' ' x $fill;
$fill = ' ' x (2 + $maxlen{$_} - length($_)); $fill = $maxlen{$_} - length($_);
if($ralign->{$_}) {
print ' ' x $fill;
$fill = 0;
}
print $_;
$fill += $table_spacing;
} }
print "\n"; print "\n";
print join(" ", map { '-' x ($maxlen{$_}) } @$keys) . "\n"; print join(' ' x $table_spacing, map { '-' x ($maxlen{$_}) } @$keys) . "\n";
# print values # print values
foreach my $row (@$data) { foreach my $row (@$data) {
my $fill = ''; my $fill = 0;
foreach (@$keys) { foreach (@$keys) {
my $val = $row->{$_}; my $val = $row->{$_};
print $fill . $val; print ' ' x $fill;
$fill = ' ' x (2 + $maxlen{$_} - length($val)); $fill = $maxlen{$_} - length($val);
if($ralign->{$_}) {
print ' ' x $fill;
$fill = 0;
}
print $val;
$fill += $table_spacing;
} }
print "\n"; print "\n";
} }
@ -1997,7 +2063,7 @@ MAIN:
WARN 'found option "--progress", but "pv" is not present: (please install "pv")'; WARN 'found option "--progress", but "pv" is not present: (please install "pv")';
$show_progress = 0; $show_progress = 0;
} }
my ($action_run, $action_info, $action_resolve, $action_diff, $action_origin, $action_config_print, $action_list); my ($action_run, $action_usage, $action_resolve, $action_diff, $action_origin, $action_config_print, $action_list);
my @filter_args; my @filter_args;
my $args_allow_group = 0; my $args_allow_group = 0;
my ($args_expected_min, $args_expected_max) = (0, 0); my ($args_expected_min, $args_expected_max) = (0, 0);
@ -2009,8 +2075,8 @@ MAIN:
$args_allow_group = 1; $args_allow_group = 1;
@filter_args = @ARGV; @filter_args = @ARGV;
} }
elsif ($command eq "info") { elsif ($command eq "usage") {
$action_info = 1; $action_usage = 1;
$args_expected_min = 0; $args_expected_min = 0;
$args_expected_max = 9999; $args_expected_max = 9999;
$args_allow_group = 1; $args_allow_group = 1;
@ -2250,7 +2316,7 @@ MAIN:
# #
# filter subvolumes matching command line arguments # filter subvolumes matching command line arguments
# #
if(($action_run || $action_resolve || $action_info || $action_list || $action_config_print) && scalar(@filter_args)) if(($action_run || $action_resolve || $action_usage || $action_list || $action_config_print) && scalar(@filter_args))
{ {
my %match; my %match;
foreach my $config_vol (@{$config->{VOLUME}}) { foreach my $config_vol (@{$config->{VOLUME}}) {
@ -2324,29 +2390,22 @@ MAIN:
} }
if($action_info) if($action_usage)
{ {
# #
# print filesystem information # print filesystem information
# #
print "================================================================================\n"; my @data;
print "Filesystem information ($version_info)\n\n";
print " Date: " . localtime($start_time) . "\n";
print " Config: $config->{SRC_FILE}\n";
print "================================================================================\n";
my %processed; my %processed;
foreach my $config_vol (@{$config->{VOLUME}}) foreach my $config_vol (@{$config->{VOLUME}}) {
{
next if($config_vol->{ABORTED}); next if($config_vol->{ABORTED});
my $sroot = vinfo($config_vol->{url}, $config_vol); my $sroot = vinfo($config_vol->{url}, $config_vol);
unless($processed{$sroot->{URL}}) unless($processed{$sroot->{URL}}) {
{ my $usage = btrfs_filesystem_usage($sroot) // {};
print "\n--------------------------------------------------------------------------------\n"; push @data, { %$usage,
print "Source volume: $sroot->{PRINT}\n"; type => "source",
print "--------------------------------------------------------------------------------\n"; vinfo_prefixed_keys("", $sroot),
print (btrfs_filesystem_usage($sroot) // ""); };
print "\n";
$processed{$sroot->{URL}} = 1; $processed{$sroot->{URL}} = 1;
} }
} }
@ -2356,22 +2415,20 @@ MAIN:
my $sroot = vinfo($config_vol->{url}, $config_vol); my $sroot = vinfo($config_vol->{url}, $config_vol);
foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) { foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) {
next if($config_subvol->{ABORTED}); next if($config_subvol->{ABORTED});
foreach my $config_target (@{$config_subvol->{TARGET}}) foreach my $config_target (@{$config_subvol->{TARGET}}) {
{
my $droot = vinfo($config_target->{url}, $config_target); my $droot = vinfo($config_target->{url}, $config_target);
unless($processed{$droot->{URL}}) unless($processed{$droot->{URL}}) {
{ my $usage = btrfs_filesystem_usage($droot) // {};
print "\n--------------------------------------------------------------------------------\n"; push @data, { %$usage,
print "Target volume: $droot->{PRINT}\n"; type => "target",
print " ^--- $sroot->{PRINT}\n"; vinfo_prefixed_keys("", $droot),
print "--------------------------------------------------------------------------------\n"; };
print (btrfs_filesystem_usage($droot) // "");
print "\n";
$processed{$droot->{URL}} = 1; $processed{$droot->{URL}} = 1;
} }
} }
} }
} }
print_formatted("usage", \@data);
exit exit_status($config); exit exit_status($config);
} }
@ -3238,13 +3295,7 @@ MAIN:
# print scheduling results # print scheduling results
# #
if($loglevel >= 2) { if($loglevel >= 2) {
my @data = map { $_->{url} = $_->{value}->{URL}; my @data = map { { %$_, vinfo_prefixed_keys("", $_->{value}) }; } @$schedule_results;
$_->{host} = $_->{value}->{HOST};
$_->{path} = $_->{value}->{PATH};
$_->{name} = $_->{value}->{SUBVOL_PATH};
$_->{target} = $_->{value}->{PRINT};
$_;
} @$schedule_results;
my @data_backup = map { $_->{topic} eq "backup" ? $_ : () } @data; my @data_backup = map { $_->{topic} eq "backup" ? $_ : () } @data;
my @data_snapshot = map { $_->{topic} eq "snapshot" ? $_ : () } @data; my @data_snapshot = map { $_->{topic} eq "snapshot" ? $_ : () } @data;

View File

@ -161,7 +161,7 @@ STATEMENTS\fR below). Use the \fI\-\-format\fR command line option to
switch between different output formats. switch between different output formats.
.RE .RE
.PP .PP
.B info .B usage
[filter...] [filter...]
.RS 4 .RS 4
Print filesystem usage information for all source/target Print filesystem usage information for all source/target