mirror of https://github.com/digint/btrbk
btrbk: print summary at end of "execute" action; added option -q (quiet)
parent
978948f877
commit
5299a57c2b
136
btrbk
136
btrbk
|
@ -102,15 +102,16 @@ sub HELP_MESSAGE
|
||||||
print STDERR "options:\n";
|
print STDERR "options:\n";
|
||||||
print STDERR " --help display this help message\n";
|
print STDERR " --help display this help message\n";
|
||||||
print STDERR " --version display version information\n";
|
print STDERR " --version display version information\n";
|
||||||
print STDERR " -c FILE config file to be processed on execute command (defaults to \"$default_config\")\n";
|
print STDERR " -c FILE specify configuration file (defaults to \"$default_config\")\n";
|
||||||
print STDERR " -v be verbose (set loglevel=info)\n";
|
print STDERR " -v be verbose (set loglevel=info)\n";
|
||||||
print STDERR " -l LEVEL set loglevel (1=warn, 2=info, 3=debug, 4=trace)\n";
|
print STDERR " -q be quiet (do not print summary at end of \"execute\" command)\n";
|
||||||
|
print STDERR " -l LEVEL set loglevel (warn, info, debug, trace)\n";
|
||||||
print STDERR "\n";
|
print STDERR "\n";
|
||||||
print STDERR "commands:\n";
|
print STDERR "commands:\n";
|
||||||
print STDERR " tree shows backup tree\n";
|
print STDERR " execute perform backup operations as defined in configuration\n";
|
||||||
print STDERR " execute perform all backups, and delete old snapshots based on configured backup scheme\n";
|
print STDERR " dryrun don't run btrfs commands, just show what would be executed\n";
|
||||||
print STDERR " dryrun don't run btrfs commands, just show what would be executed\n";
|
print STDERR " tree shows backup tree\n";
|
||||||
print STDERR " diff <from> <to> shows new files for subvolume <from>, against subvolume <to>\n";
|
print STDERR " diff <from> <to> shows new files since subvolume <from> for subvolume <to>\n";
|
||||||
print STDERR "\n";
|
print STDERR "\n";
|
||||||
print STDERR "For additional information, see $PROJECT_HOME\n";
|
print STDERR "For additional information, see $PROJECT_HOME\n";
|
||||||
}
|
}
|
||||||
|
@ -693,7 +694,7 @@ sub get_latest_common($$$)
|
||||||
sub check_backup_scheme(@)
|
sub check_backup_scheme(@)
|
||||||
{
|
{
|
||||||
my %args = @_;
|
my %args = @_;
|
||||||
my $check = $args{check} || die;
|
my $schedule = $args{schedule} || die;
|
||||||
my @today = @{$args{today}};
|
my @today = @{$args{today}};
|
||||||
my $preserve_day_of_week = $args{preserve_day_of_week} || die;
|
my $preserve_day_of_week = $args{preserve_day_of_week} || die;
|
||||||
my $preserve_daily = $args{preserve_daily} // die;
|
my $preserve_daily = $args{preserve_daily} // die;
|
||||||
|
@ -705,7 +706,7 @@ sub check_backup_scheme(@)
|
||||||
DEBUG "next $preserve_day_of_week is in $delta_dow days";
|
DEBUG "next $preserve_day_of_week is in $delta_dow days";
|
||||||
|
|
||||||
my @last_in_week;
|
my @last_in_week;
|
||||||
foreach my $href (sort { $a->{sort} cmp $b->{sort} } @$check) # sorted ascending
|
foreach my $href (sort { $a->{sort} cmp $b->{sort} } @$schedule) # sorted ascending
|
||||||
{
|
{
|
||||||
my @date = @{$href->{date}};
|
my @date = @{$href->{date}};
|
||||||
my $delta_days = Delta_Days(@date, @today);
|
my $delta_days = Delta_Days(@date, @today);
|
||||||
|
@ -746,7 +747,7 @@ sub check_backup_scheme(@)
|
||||||
}
|
}
|
||||||
|
|
||||||
my @delete;
|
my @delete;
|
||||||
foreach my $href (sort { $a->{sort} cmp $b->{sort} } @$check) # sorted ascending
|
foreach my $href (sort { $a->{sort} cmp $b->{sort} } @$schedule) # sorted ascending
|
||||||
{
|
{
|
||||||
if($href->{preserve}) {
|
if($href->{preserve}) {
|
||||||
INFO "$href->{sort}: $href->{preserve}";
|
INFO "$href->{sort}: $href->{preserve}";
|
||||||
|
@ -768,7 +769,7 @@ MAIN:
|
||||||
my @today = Today();
|
my @today = Today();
|
||||||
|
|
||||||
my %opts;
|
my %opts;
|
||||||
unless(getopts('s:t:c:vl:p', \%opts)) {
|
unless(getopts('s:t:c:vql:p', \%opts)) {
|
||||||
VERSION_MESSAGE();
|
VERSION_MESSAGE();
|
||||||
HELP_MESSAGE(0);
|
HELP_MESSAGE(0);
|
||||||
exit 1;
|
exit 1;
|
||||||
|
@ -783,9 +784,10 @@ MAIN:
|
||||||
elsif(lc($loglevel) eq "trace") { $loglevel = 4; }
|
elsif(lc($loglevel) eq "trace") { $loglevel = 4; }
|
||||||
elsif($loglevel =~ /^[0-9]+$/) { ; }
|
elsif($loglevel =~ /^[0-9]+$/) { ; }
|
||||||
else {
|
else {
|
||||||
$loglevel = $opts{v} ? 2 : 0;
|
$loglevel = $opts{v} ? 2 : 1;
|
||||||
}
|
}
|
||||||
my $config_file = $opts{c} || $default_config;
|
my $config_file = $opts{c} || $default_config;
|
||||||
|
my $quiet = $opts{q};
|
||||||
|
|
||||||
# check command line options
|
# check command line options
|
||||||
if($opts{h} || (not $command)) {
|
if($opts{h} || (not $command)) {
|
||||||
|
@ -929,8 +931,8 @@ MAIN:
|
||||||
{
|
{
|
||||||
my $svol = $config_subvol->{svol} || die;
|
my $svol = $config_subvol->{svol} || die;
|
||||||
unless(subvol($sroot, $svol)) {
|
unless(subvol($sroot, $svol)) {
|
||||||
WARN "Subvolume \"$svol\" not present in btrfs subvolume list for \"$sroot\", skipping section";
|
$config_subvol->{ABORTED} = "Subvolume \"$svol\" not present in btrfs subvolume list for \"$sroot\"";
|
||||||
$config_subvol->{ABORTED} = 1;
|
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
foreach my $config_target (@{$config_subvol->{TARGET}})
|
foreach my $config_target (@{$config_subvol->{TARGET}})
|
||||||
|
@ -938,8 +940,8 @@ MAIN:
|
||||||
my $droot = $config_target->{droot} || die;
|
my $droot = $config_target->{droot} || die;
|
||||||
$vol_info{$droot} //= btr_subtree($droot);
|
$vol_info{$droot} //= btr_subtree($droot);
|
||||||
unless($vol_info{$droot}) {
|
unless($vol_info{$droot}) {
|
||||||
WARN "Failed to read btrfs subvolume list for \"$droot\", skipping target";
|
$config_target->{ABORTED} = "Failed to read btrfs subvolume list for \"$droot\"";
|
||||||
$config_target->{ABORTED} = 1;
|
WARN "Skipping target: $config_target->{ABORTED}";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1037,9 +1039,8 @@ MAIN:
|
||||||
next if($config_target->{ABORTED});
|
next if($config_target->{ABORTED});
|
||||||
my $droot = $config_target->{droot} || die;
|
my $droot = $config_target->{droot} || die;
|
||||||
if(subvol($droot, $snapshot_name)) {
|
if(subvol($droot, $snapshot_name)) {
|
||||||
# TODO: this seems not right here: maybe just skip this check, and panic later
|
$config_target->{ABORTED} = "Snapshot already exists at destination: $droot/$snapshot_name";
|
||||||
WARN "Snapshot already exists at destination, skipping target: $droot/$snapshot_name";
|
WARN "Skipping target: $config_target->{ABORTED}";
|
||||||
$config_target->{ABORTED} = 1;
|
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
if($config_target->{target_type} eq "send-receive") {
|
if($config_target->{target_type} eq "send-receive") {
|
||||||
|
@ -1047,8 +1048,8 @@ MAIN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unless($create_snapshot) {
|
unless($create_snapshot) {
|
||||||
WARN "No snapshots to be created, skipping subvolume: $sroot/$svol";
|
$config_subvol->{ABORTED} = "No targets defined for subvolume: $sroot/$svol";
|
||||||
$config_subvol->{ABORTED} = 1;
|
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1060,8 +1061,8 @@ MAIN:
|
||||||
INFO "Creating subvolume snapshot for: $sroot/$svol";
|
INFO "Creating subvolume snapshot for: $sroot/$svol";
|
||||||
|
|
||||||
unless(btrfs_snapshot("$sroot/$svol", $snapshot)) {
|
unless(btrfs_snapshot("$sroot/$svol", $snapshot)) {
|
||||||
WARN "Failed to create snapshot, skipping subvolume: $sroot/$svol";
|
$config_subvol->{ABORTED} = "Failed to create snapshot, skipping subvolume: $sroot/$svol";
|
||||||
$config_subvol->{ABORTED} = 1;
|
WARN "Skipping subvolume section: $config_subvol->{ABORTED}";
|
||||||
}
|
}
|
||||||
$snapshot_cache{"$sroot/$svol"} = { name => $snapshot_name,
|
$snapshot_cache{"$sroot/$svol"} = { name => $snapshot_name,
|
||||||
file => $snapshot };
|
file => $snapshot };
|
||||||
|
@ -1128,7 +1129,12 @@ MAIN:
|
||||||
else {
|
else {
|
||||||
ERROR "Unknown target type \"$target_type\", skipping: $sroot/$svol";
|
ERROR "Unknown target type \"$target_type\", skipping: $sroot/$svol";
|
||||||
}
|
}
|
||||||
$config_target->{ABORTED} = 1 unless($success);
|
if($success) {
|
||||||
|
$config_target->{subvol_created} = "$droot/$snapshot_name";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$config_target->{ABORTED} = "btrfs send/receive command failed";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1155,46 +1161,112 @@ MAIN:
|
||||||
# delete backups
|
# delete backups
|
||||||
#
|
#
|
||||||
INFO "Cleaning backups of subvolume \"$sroot/$svol\": $droot/$svol.*";
|
INFO "Cleaning backups of subvolume \"$sroot/$svol\": $droot/$svol.*";
|
||||||
my @check;
|
my @schedule;
|
||||||
foreach my $vol (keys %{$vol_info{$droot}}) {
|
foreach my $vol (keys %{$vol_info{$droot}}) {
|
||||||
if($vol =~ /^$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
if($vol =~ /^$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
||||||
push(@check, { name => "$droot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
push(@schedule, { name => "$droot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my @delete = check_backup_scheme(
|
my @delete = check_backup_scheme(
|
||||||
check => \@check,
|
schedule => \@schedule,
|
||||||
today => \@today,
|
today => \@today,
|
||||||
preserve_day_of_week => config_key($config_target, "preserve_day_of_week"),
|
preserve_day_of_week => config_key($config_target, "preserve_day_of_week"),
|
||||||
preserve_daily => config_key($config_target, "target_preserve_daily"),
|
preserve_daily => config_key($config_target, "target_preserve_daily"),
|
||||||
preserve_weekly => config_key($config_target, "target_preserve_weekly"),
|
preserve_weekly => config_key($config_target, "target_preserve_weekly"),
|
||||||
preserve_monthly => config_key($config_target, "target_preserve_monthly"),
|
preserve_monthly => config_key($config_target, "target_preserve_monthly"),
|
||||||
);
|
);
|
||||||
btrfs_subvolume_delete(@delete);
|
if(btrfs_subvolume_delete(@delete)) {
|
||||||
|
$config_target->{subvol_deleted} = \@delete;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$config_target->{ABORTED} = "btrfs subvolume delete command failed";
|
||||||
|
}
|
||||||
|
$config_target->{schedule} = \@schedule;
|
||||||
}
|
}
|
||||||
|
|
||||||
#
|
#
|
||||||
# delete snapshots
|
# delete snapshots
|
||||||
#
|
#
|
||||||
INFO "Cleaning snapshots: $sroot/$snapdir$svol.*";
|
INFO "Cleaning snapshots: $sroot/$snapdir$svol.*";
|
||||||
my @check;
|
my @schedule;
|
||||||
foreach my $vol (keys %{$vol_info{$sroot}}) {
|
foreach my $vol (keys %{$vol_info{$sroot}}) {
|
||||||
if($vol =~ /^$snapdir$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
if($vol =~ /^$snapdir$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/) {
|
||||||
push(@check, { name => "$sroot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
push(@schedule, { name => "$sroot/$vol", sort => $vol, date => [ $1, $2, $3 ] });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
my @delete = check_backup_scheme(
|
my @delete = check_backup_scheme(
|
||||||
check => \@check,
|
schedule => \@schedule,
|
||||||
today => \@today,
|
today => \@today,
|
||||||
preserve_day_of_week => config_key($config_subvol, "preserve_day_of_week"),
|
preserve_day_of_week => config_key($config_subvol, "preserve_day_of_week"),
|
||||||
preserve_daily => config_key($config_subvol, "snapshot_preserve_daily"),
|
preserve_daily => config_key($config_subvol, "snapshot_preserve_daily"),
|
||||||
preserve_weekly => config_key($config_subvol, "snapshot_preserve_weekly"),
|
preserve_weekly => config_key($config_subvol, "snapshot_preserve_weekly"),
|
||||||
preserve_monthly => config_key($config_subvol, "snapshot_preserve_monthly"),
|
preserve_monthly => config_key($config_subvol, "snapshot_preserve_monthly"),
|
||||||
);
|
);
|
||||||
btrfs_subvolume_delete(@delete);
|
if(btrfs_subvolume_delete(@delete)) {
|
||||||
|
$config_subvol->{subvol_deleted} = \@delete;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$config_subvol->{ABORTED} = "btrfs subvolume delete command failed";
|
||||||
|
}
|
||||||
|
$config_subvol->{schedule} = \@schedule;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# TODO: print summary (add some text to ABORTED flags)
|
|
||||||
|
#
|
||||||
|
# print summary
|
||||||
|
#
|
||||||
|
unless($quiet)
|
||||||
|
{
|
||||||
|
my $err_count = 0;
|
||||||
|
print "--------------------------------------------------------------------------------\n";
|
||||||
|
print "$version_info\n";
|
||||||
|
print "--------------------------------------------------------------------------------";
|
||||||
|
foreach my $config_vol (@{$config->{VOLUME}})
|
||||||
|
{
|
||||||
|
if($config_vol->{ABORTED}) {
|
||||||
|
print "!!! $config_vol->{sroot}: ABORTED: $config_vol->{ABORTED}\n";
|
||||||
|
$err_count++;
|
||||||
|
}
|
||||||
|
foreach my $config_subvol (@{$config_vol->{SUBVOLUME}})
|
||||||
|
{
|
||||||
|
print "\n$config_vol->{sroot}/$config_subvol->{svol}\n";
|
||||||
|
if($config_subvol->{ABORTED}) {
|
||||||
|
print "!!! Subvolume \"$config_subvol->{svol}\" aborted: $config_subvol->{ABORTED}\n";
|
||||||
|
$err_count++;
|
||||||
|
}
|
||||||
|
# if($config_subvol->{schedule}) {
|
||||||
|
# foreach (sort { $a->{sort} cmp $b->{sort} } @{$config_subvol->{schedule}}) {
|
||||||
|
# print(($_->{preserve} ? "===" : "---") . " $_->{name}\n");
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
if($config_subvol->{subvol_deleted}) {
|
||||||
|
print "--- $_\n" foreach(@{$config_subvol->{subvol_deleted}});
|
||||||
|
}
|
||||||
|
print "+++ $config_subvol->{snapshot}\n" if($config_subvol->{snapshot});
|
||||||
|
foreach my $config_target (@{$config_subvol->{TARGET}})
|
||||||
|
{
|
||||||
|
if($config_target->{ABORTED}) {
|
||||||
|
print "!!! Target \"$config_target->{droot}\" aborted: $config_target->{ABORTED}\n";
|
||||||
|
$err_count++;
|
||||||
|
}
|
||||||
|
# if($config_target->{schedule}) {
|
||||||
|
# foreach (sort { $a->{sort} cmp $b->{sort} } @{$config_target->{schedule}}) {
|
||||||
|
# print(($_->{preserve} ? "===" : "---") . " $_->{name}\n");
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
if($config_target->{subvol_deleted}) {
|
||||||
|
print "--- $_\n" foreach(@{$config_target->{subvol_deleted}});
|
||||||
|
}
|
||||||
|
print "+++ $config_target->{subvol_created}\n" if($config_target->{subvol_created});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if($err_count) {
|
||||||
|
print "\nNOTE: Some errors occurred, which may result in missing backups!\n";
|
||||||
|
print "Please check warning and error messages above.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue