[mq]: opt-clean
authorHeiko Schlittermann (ZARAFA.ctq.de) hs@schlittermann.de
Wed, 27 Apr 2011 16:58:51 +0200
changeset 48 708e3b8bd670
parent 47 bc0d60c9857b
child 49 ce823daf2141
[mq]: opt-clean
ftbackup.conf.example
sbin/ftbackup
--- a/ftbackup.conf.example	Tue Apr 26 23:24:07 2011 +0200
+++ b/ftbackup.conf.example	Wed Apr 27 16:58:51 2011 +0200
@@ -19,3 +19,10 @@
 # Do we need compression? Unfortunately currently only
 # global
 # COMPRESSION_LEVEL = 6
+
+# Do a full backup every 7 days
+# FULL_CYCLE = 7
+
+# Always keep this number of (full) backups
+# (including the current one)
+# KEEP = 2
--- a/sbin/ftbackup	Tue Apr 26 23:24:07 2011 +0200
+++ b/sbin/ftbackup	Wed Apr 27 16:58:51 2011 +0200
@@ -35,6 +35,7 @@
 my $opt_label   = "daily";
 my $opt_info    = 0;
 my $opt_config  = "";
+my $opt_clean   = 1;
 my $opt_dumpdates = "/var/lib/dumpdates";
 
 sub get_configs(@);
@@ -46,6 +47,7 @@
 sub real_device($);
 sub get_estimate($$);
 sub devno($);
+sub unlink_old_dumps($$);
 
 our @AT_EXIT;
 END { $_->() foreach @AT_EXIT }
@@ -54,8 +56,9 @@
 my %CONFIG = (
     FTP_DIR     => "backup/<LABEL>/<HOSTNAME>",
     FTP_PASSIVE => 1,
+    COMPRESSION_LEVEL => 6,
     FULL_CYCLE  => 7,
-    COMPRESSION_LEVEL => 6,
+    KEEP => 2,
 );
 
 
@@ -74,6 +77,7 @@
         "m|man"     => sub { pod2usage(-exit => 0, -verbose => 3) },
         "C|config=s" => sub { @CONFIGS = ($_[1]) },
 	"V|version" => sub { print "$ME: $VERSION\n"; exit 0 },
+	"c|clean!"  => \$opt_clean,
 	"D|dumpdates=s" => \$opt_dumpdates,
     ) or pod2usage;
 
@@ -166,26 +170,33 @@
             $ftp->try(cwd    => $dir);
 
             #verbose "Now in @{[$ftp->pwd]}.\n" if $ftp;
+	    unlink_old_dumps($ftp, $cf{KEEP} + 1)
+		if $opt_clean;
 
             # examine the situation and decide about the level
             # FIXME: currently we simply run a full dump every FULL_CYCLE
             # days, the intermediate dumps are level 1
             foreach (reverse sort $ftp->ls) {
                 /^(?<date>.*)\.(?<level>\d+)$/ or next;
-                $last[$+{level}] = $+{date};
-                last if $+{level} == 0;
+		$last[$+{level}] = str2time $+{date};
             }
         }
 
+	# now check, which of the old backups can be purged
+	# The config KEEP tells us how many full dumps we need to
+	# keep. The pre-dump cleaning should keep this number
+	# and after successfull dump we need to cleanup again
+	#$last[0] = [ sort { $a->{stamp} <=> $b->{stamp} } @{$last[0]} ];
+
 	# for safety we check if there is really a full dump not older than xxx days
         if ($dev->{level} > 0) {
 	    if (!@last) {
 		$dev->{level} = 0;
 		warn "adjusted backup level to 0, last full backup missing\n";
-	    } elsif ($NOW - str2time($last[0]) > ($cf{FULL_CYCLE} * 86_400)) {
+	    } elsif (($NOW - $last[0]) > ($cf{FULL_CYCLE} * 86_400)) {
 		$dev->{level} = 0;
 		warn sprintf "adjusted backup level to 0, last full backup is %.1f days old\n",
-		    ($NOW - str2time $last[0])/86_400;
+		    ($NOW - $last[0])/86_400;
 	    }
         }
 
@@ -287,6 +298,9 @@
 
 	update_devnames($opt_dumpdates, $dev->{dump} => $dev->{rdev})
 		if $opt_dumpdates;
+
+	unlink_old_dumps($ftp, $cf{KEEP})
+	    if $ftp and $opt_clean;
     }
 
 }
@@ -518,6 +532,34 @@
     return @devs;
 }
 
+sub unlink_old_dumps($$) {
+    my ($ftp, $keep) = @_;
+    my @dumps;
+    foreach ($ftp->ls) {
+	/^(?<date>.*)\.(?<level>\d+)$/ or next;
+	push @{$dumps[$+{level}]} => { file => $_, date => $+{date}, stamp => str2time($+{date})};
+    }
+
+    ### @dumps
+
+    # sort the level 0 dumps by date and remove all but the last $keep
+    # ones.
+    # if we found level 0 dumps, we remove all level 1+ dumps older than
+    # the oldest level 0 dump we'll remove
+    @{$dumps[0]} = reverse sort { $a->{stamp} <=> $b->{stamp} } @{$dumps[0]};
+    my @unlink = @{$dumps[0]}[$keep..$#{$dumps[0]}];
+    push @unlink => grep { $_->{stamp} <= $unlink[0]->{stamp} } @{@dumps[1..$#dumps]}
+	if @unlink;
+    ### @unlink
+
+    foreach (@unlink) {
+	say "DELETE: $_->{file}";
+	next if $opt_dry;
+	$ftp->delete($_->{file});
+    }
+}
+
+
 #/dev/vda1 0 Thu Apr 14 12:54:31 2011 +0200
 #/dev/vda1 1 Thu Apr 14 12:54:16 2011 +0200
 
@@ -567,12 +609,11 @@
 
 Even more debugging is shown using the DEBUG=1 environment setting.
 
-=item B<--clean> [I<days>]
+=item B<--clean> 
 
 Cleanup older backups we do not need (that is: incremental backups with
-no previous full backup. If I<days> are given, then all full backups older than
-the number of I<days> are removed (and all incremental backups based on these
-full backups). (default: 0 and not implemented)
+no previous full backup. The number of old backups we keep 
+is read from the configuration file. (default: 1)
 
 =item B<--dry>
 
@@ -624,8 +665,47 @@
     FTP_HOST	= <no default>
     FTP_DIR	= "backup/<LABEL>/<HOSTNAME>"
     FTP_PASSIVE = 1
+    COMPRESSION_LEVEL = 6
     FULL_CYCLE	= 7
-    COMPRESSION_LEVEL = 6
+    KEEP        = 2
+
+=over
+
+=item KEY
+
+The encryption key to use. (We use symmetric blowfish encryption currently.)
+
+=item FTP_HOST
+
+The FTP host to send the backup to.
+
+=item FTP_DIR
+
+A template for storing the backup file(s). Each dumped file system needs
+its own directory!
+
+=item FTP_PASSIVE
+
+A switch to activate the usage of passive FTP.
+
+=item COMPRESSION_LEVEL
+
+The level of the used gzip compression.
+
+=item FULL_CYCLE
+
+A full backup is forced if the last full backup is older than thi number
+of days.
+
+=item KEEP
+
+The number of full backups (including the current one!) to keep. It means, that
+normally you'll get KEEP backups in your backup directory. Useless
+incremental backups are deleted automgically.
+
+=back
+
+
 
 =head2 F<.netrc>