diff --git a/btrbk b/btrbk index e749130..24a24b5 100755 --- a/btrbk +++ b/btrbk @@ -67,12 +67,10 @@ my %config_options = ( incremental => { default => "yes", accept => [ "yes", "no", "strict" ] }, receive_log => { default => undef, accept => [ "sidecar", "no" ], accept_file => "absolute" }, snapshot_create_always => { default => undef, accept => [ "yes", "no" ] }, - snapshot_preserve_all => { default => "yes", accept => [ "yes", "no" ] }, # TODO: honor this - snapshot_preserve_days => { default => undef, accept => [ "no" ], accept_number => 1 }, - snapshot_preserve_weekly => { default => undef, accept => [ "no" ], accept_number => 1 }, - target_preserve_all => { default => "yes", accept => [ "yes", "no" ] }, # TODO: honor this - target_preserve_days => { default => undef, accept => [ "no" ], accept_number => 1 }, - target_preserve_weekly => { default => undef, accept => [ "no" ], accept_number => 1 }, + snapshot_preserve_days => { default => "all", accept => [ "no", "all" ], accept_number => 1 }, + snapshot_preserve_weekly => { default => 0, accept => [ "no" ], accept_number => 1 }, + target_preserve_days => { default => "all", accept => [ "no", "all" ], accept_number => 1 }, + target_preserve_weekly => { default => 0, accept => [ "no" ], accept_number => 1 }, ); my @config_target_types = qw(send-receive); @@ -679,39 +677,50 @@ sub check_backup_scheme(@) my %args = @_; my $vol_date = $args{vol_date} || die; my $today = $args{today} || die; - my $keep_days = $args{keep_days} || die; - my $weekly_threshold = $args{weekly_threshold} || die; - my $keep_info = $args{keep_info} || die; - my $keep = 0; + my $week_start = $args{week_start} || die; + my $preserve_days = $args{preserve_days} // die; + my $preserve_weekly = $args{preserve_weekly} // die; + my $preserve_info = $args{preserve_info} || die; + my $preserve = 0; my ($vol_y, $vol_m, $vol_d) = @$vol_date; - my $dd = Delta_Days(@$vol_date, @$today); - if($dd <= $keep_days) - { - $keep = "less than $keep_days days old (age=$dd days)"; - DEBUG "$keep"; - } + # calculate weekly_threshold + my @weekly_threshold = Add_Delta_Days(@$week_start, (-7 * $preserve_weekly)); + TRACE "weekly_threshold for preserve_weekly=$preserve_weekly: " . join('-', @weekly_threshold); - if(Delta_Days(@$vol_date, @$weekly_threshold) < 0) - { - DEBUG "not older than " . join('-', @$weekly_threshold); - my ($vol_wnr, $vol_wy) = Week_of_Year(@$vol_date); - unless($keep_info->{week}->{"$vol_wy-$vol_wnr"}) + if($preserve_days eq "all") { + $preserve = "preserve_days is set to \"all\""; + DEBUG "$preserve"; + } + else { + my $dd = Delta_Days(@$vol_date, @$today); + if($dd <= $preserve_days) { - $keep_info->{week}->{"$vol_wy-$vol_wnr"} = 1; - $keep = "last in week $vol_wy-$vol_wnr"; - DEBUG "$keep"; + $preserve = "less than $preserve_days days old (age: $dd days)"; + DEBUG "$preserve"; } } - unless($keep_info->{month}->{"$vol_y-$vol_m"}) + if(Delta_Days(@$vol_date, @weekly_threshold) < 0) { - $keep_info->{month}->{"$vol_y-$vol_m"} = 1; - $keep = "last in month $vol_y-$vol_m"; - DEBUG "$keep"; + DEBUG "not older than " . join('-', @weekly_threshold); + my ($vol_wnr, $vol_wy) = Week_of_Year(@$vol_date); + unless($preserve_info->{week}->{"$vol_wy-$vol_wnr"}) + { + $preserve_info->{week}->{"$vol_wy-$vol_wnr"} = 1; + $preserve = "last in week #$vol_wnr, $vol_wy"; + DEBUG "$preserve"; + } } - return $keep; + unless($preserve_info->{month}->{"$vol_y-$vol_m"}) + { + $preserve_info->{month}->{"$vol_y-$vol_m"} = 1; + $preserve = "last in month $vol_y-$vol_m"; + DEBUG "$preserve"; + } + + return $preserve; } @@ -1052,6 +1061,7 @@ 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"; @@ -1070,11 +1080,11 @@ MAIN: if($latest_common_src && $latest_common_dst) { my $parent_snap = $latest_common_src->{FS_PATH}; INFO "Incremental from parent snapshot: $parent_snap"; - btrfs_send_receive($snapshot, $droot, $parent_snap, $receive_log); + $success = btrfs_send_receive($snapshot, $droot, $parent_snap, $receive_log); } elsif($incremental ne "strict") { INFO "No common parent subvolume present, creating full backup"; - btrfs_send_receive($snapshot, $droot, undef, $receive_log); + $success = btrfs_send_receive($snapshot, $droot, undef, $receive_log); } else { WARN "Backup to $droot failed: no common parent subvolume found, and option \"incremental\" is set to \"strict\""; @@ -1082,12 +1092,13 @@ MAIN: } else { INFO "Creating full backup (option \"incremental\" is not set)"; - btrfs_send_receive($snapshot, $droot, undef, $receive_log); + $success = btrfs_send_receive($snapshot, $droot, undef, $receive_log); } } else { ERROR "Unknown target type \"$target_type\", skipping: $sroot/$svol"; } + $config_target->{ABORTED} = 1 unless($success); } } } @@ -1098,98 +1109,95 @@ MAIN: { $dryrun = 1; # TODO: gather all information first, then delete all backups/snapshots at the end - # TODO: always keep first/last + # TODO: always preserve first/last + + my @last_sunday; + if(Day_of_Week(@today) == 7) { # today is sunday + @last_sunday = @today; + } + else { + @last_sunday = Add_Delta_Days(Monday_of_Week(Week_of_Year(@today)), -1); + } + DEBUG "last sunday: " . join('-', @last_sunday); # - # remove backups following a keep_days/keep_weekly scheme + # remove backups following a preserve_days/preserve_weekly scheme # - foreach my $job (@$jobs) + foreach my $config_vol (@{$config->{VOLUME}}) { - next if($job->{ABORTED}); - - my $sroot = $job->{sroot} || die; - my $svol = $job->{svol} || die; - my $droot = $job->{droot} || die; - my $job_opts = $job->{options} || die; - - unless(ref($job_opts->{preserve})) { - INFO "Skip cleaning of subvolume backups (option preserve is not set): $sroot/$svol"; - next; - } - - INFO "Cleaning subvolume backups of job: $sroot/$svol"; - my $keep_days = $job_opts->{preserve}->{daily}; - my $keep_weekly = $job_opts->{preserve}->{weekly}; - - # calculate weekly_threshold - my @last_sunday; - if(Day_of_Week(@today) == 7) { # today is sunday - @last_sunday = @today; - } - else { - @last_sunday = Add_Delta_Days(Monday_of_Week(Week_of_Year(@today)), -1); - } - my @weekly_threshold = Add_Delta_Days(@last_sunday, (-7 * $keep_weekly)); - DEBUG "last sunday: " . join('-', @last_sunday); - DEBUG "weekly_threshold for keep_weekly=$keep_weekly: " . join('-', @weekly_threshold); - - # - # delete backups - # - my $keep_info = {}; - my @delete_backups; - INFO "Cleaning backups: $droot/$svol.*"; - foreach my $vol (sort { $b cmp $a } keys %{$vol_info{$droot}}) + next if($config_vol->{ABORTED}); + my $sroot = $config_vol->{sroot} || die; + foreach my $config_subvol (@{$config_vol->{SUBVOLUME}}) { - next unless($vol =~ /^$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/); - my @vol_date = ($1, $2, $3); + next if($config_subvol->{ABORTED}); + my $svol = $config_subvol->{svol} || die; + INFO "Cleaning subvolume backups for: $sroot/$svol"; + foreach my $config_target (@{$config_subvol->{TARGET}}) + { + next if($config_target->{ABORTED}); + my $droot = $config_target->{droot} || die; - DEBUG "Checking: $vol"; - my $keep = check_backup_scheme( - vol_date => \@vol_date, - today => \@today, - weekly_threshold => \@weekly_threshold, - keep_days => $keep_days, - keep_info => $keep_info, - ); - if($keep) { - INFO "$vol: preserved: $keep"; + # + # delete backups + # + my $preserve_info = {}; + my @delete_backups; + INFO "Cleaning backups: $droot/$svol.*"; + foreach my $vol (sort { $b cmp $a } keys %{$vol_info{$droot}}) + { + next unless($vol =~ /^$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/); + my @vol_date = ($1, $2, $3); + + DEBUG "Checking: $vol"; + my $preserve = check_backup_scheme( + vol_date => \@vol_date, + today => \@today, + week_start => \@last_sunday, # TODO: configurable + preserve_days => config_key($config_target, "target_preserve_days"), + preserve_weekly => config_key($config_target, "target_preserve_weekly"), + preserve_info => $preserve_info, + ); + if($preserve) { + INFO "$vol: preserved: $preserve"; + } + else { + INFO "$vol: DELETE"; + push @delete_backups, "$droot/$vol"; + } + } + btrfs_subvolume_delete(@delete_backups); } - else { - INFO "$vol: DELETE"; - push @delete_backups, "$droot/$vol"; + + # + # delete snapshots + # + my $preserve_info = {}; + my @delete_snapshots; + INFO "Cleaning snapshots: $sroot/$snapdir$svol.*"; + foreach my $vol (sort { $b cmp $a } keys %{$vol_info{$sroot}}) + { + next unless($vol =~ /^$snapdir$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/); + my @vol_date = ($1, $2, $3); + + DEBUG "Checking: $vol"; + my $preserve = check_backup_scheme( + vol_date => \@vol_date, + today => \@today, + week_start => \@last_sunday, # TODO: configurable + preserve_days => config_key($config_subvol, "snapshot_preserve_days"), + preserve_weekly => config_key($config_subvol, "snapshot_preserve_weekly"), + preserve_info => $preserve_info, + ); + if($preserve) { + INFO "$vol: preserved: $preserve"; + } + else { + INFO "$vol: DELETE"; + push @delete_snapshots, "$sroot/$vol"; + } } + btrfs_subvolume_delete(@delete_snapshots); } - btrfs_subvolume_delete(@delete_backups); - - # - # delete snapshots - # - $keep_info = {}; - my @delete_snapshots; - INFO "Cleaning snapshots: $sroot/$snapdir$svol.*"; - foreach my $vol (sort { $b cmp $a } keys %{$vol_info{$sroot}}) - { - next unless($vol =~ /^$snapdir$svol\.([0-9]{4})([0-9]{2})([0-9]{2})/); - my @vol_date = ($1, $2, $3); - - DEBUG "Checking: $vol"; - my $keep = check_backup_scheme( - vol_date => \@vol_date, - today => \@today, - weekly_threshold => \@weekly_threshold, - keep_days => $keep_days, - keep_info => $keep_info, - ); - if($keep) { - INFO "$vol: preserved: $keep"; - } - else { - INFO "$vol: DELETE"; - push @delete_snapshots, "$sroot/$vol"; - } - } - btrfs_subvolume_delete(@delete_snapshots); } } }