Initial Version
authorHeiko Schlittermann (JUMPER) <hs@schlittermann.de>
Tue, 28 Jul 2015 16:23:33 +0200
changeset 0 0bab1e2baa76
child 1 7bbd7b8e1cbe
Initial Version
pg-backup
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pg-backup	Tue Jul 28 16:23:33 2015 +0200
@@ -0,0 +1,172 @@
+#! /usr/bin/perl
+# (c) 2015 Heiko Schlittermann <hs@schlittermann.de>
+use strict;
+use 5.10.0;
+use warnings;
+use Pod::Usage;
+use Getopt::Long;
+use File::Spec::Functions;
+	use Data::Dumper;
+
+my $opt_tasks;
+my $opt_dry;
+my $opt_keep = undef;
+
+my $opt_basedir = '.';
+my $opt_dirname = '$version-$cluster-$date';
+
+delete @ENV{grep /^LC_/ => keys %ENV};
+
+exit main() if not caller;
+
+sub main {
+
+	GetOptions(
+		'k|keep' => \$opt_keep,
+		'd|basedir=s' => \$opt_basedir,
+		'dry' => \$opt_dry,
+		'h|help' => sub { pod2usage(-verbose => 1, -exit => 0) },
+		'm|man' => sub { pod2usage(-verbose => 2, -exit => 0,
+			-noperldoc => system('perldoc -V >/dev/null 2>&1')) },
+	);
+
+	my @clusters = ls_clusters();
+
+	my @tasks = sort do {
+		if (@ARGV) {
+			my $v = $ARGV[0];
+			if (@ARGV > 1) {
+					 my %h = map { $_->{cluster} => $_ } grep { $_->{version} eq $v } @clusters;
+					 @h{@ARGV[1..$#ARGV]};
+			}
+			else { 
+					grep { $_->{version} eq $v } @clusters;
+			}
+		}
+		else {
+			grep { $_->{status} eq 'online' } @clusters;
+		}
+	};
+
+	foreach my $task (@tasks) {
+		my $dirname = do {
+			state $date = date();
+			my $version = $task->{version};
+			my $cluster = $task->{cluster};
+			eval "\"$opt_dirname\"";
+		};
+		$task->{dirname} = catfile($opt_basedir, $dirname);
+	}
+
+	# now get the real jobe done
+	my @results;
+	foreach my $task (@tasks) {
+
+		rmdir glob("$opt_basedir/*");
+		mkdir $task->{dirname}
+			or die "$0: Can't mkdir $task->{dirname}: $!\n";
+
+		my @cmd = (pg_basebackup => 
+				'--format' => 't',
+				'--xlog',
+				'--cluster' => $task->{name},
+				'--pgdata' => $task->{dirname},
+				'--gzip',
+				-t 0 ? '--progress' : ());
+
+		if ($opt_dry) {
+			print sprintf "%s %s\n", $task->{name}, "@cmd";
+			next;
+		}
+
+		system @cmd;
+		warn "$0: `@cmd` failed\n" if $?;
+		push @results, { task => $task, exit => $? };
+	}
+
+	# check the results
+	
+	foreach (@results) {
+		printf "%-10s %-20s %s\n",
+			$_->{exit} ? "FAIL:$_->{exit}" : "OK",
+			$_->{task}{cluster}{name},
+			$_->{task}{dirname};
+	}
+
+	return 1 if grep { $_->{exit} != 0 } @results;
+	return 0 if not $opt_keep;
+
+	# care about the backups to keep, if everything went fine so far
+	
+}
+
+sub date {
+	my @now = localtime;
+	sprintf "%4d-%02d-%02dT%02d-%02d-%02d",
+		$now[5]+1900,
+		$now[4]+1,
+		$now[3],
+		@now[reverse 0..2];
+}
+
+sub ls_clusters {
+	my @clusters;
+
+	foreach ( map { [(split)[0..3]]}  `pg_lsclusters -h` ) {
+		push @clusters, {
+				name => "$_->[0]/$_->[1]",
+				version => $_->[0],
+				cluster => $_->[1],
+				port    => $_->[2],
+				status  => $_->[3],
+			};
+	}
+	return @clusters;
+};
+
+exit main() if not caller;
+
+
+__END__
+
+=head1 NAME
+ 
+ pg-backup - backup all active PostgreSQL clusters
+
+=head1 SYNOPSIS
+
+ pg-backup [options] [<version> [<cluster>]...]
+
+=head1 DESCRIPTION
+
+for all active PostgreSQL clusters and writes the backups as TAR archives.
+
+If a I<version> and optionally I<cluster>s are specified on the command line,
+all clusters (or the specified clusters) of the I<version> are saved.
+
+Without any I<version>/I<cluster> specification all B<online> clusters are backed up.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-d>|B<--basedir> I<dir>
+
+The directory where the subdirectories for the backups will be placed. (default: F<.>)
+
+=item B<-k>|B<--keep> I<n>
+
+The number of generations to keep. Old backups will be only removed if B<all>
+specified backups succeed. (no default)
+
+=item B<--dry>
+
+Dry run, show what will be done. (default: undef)
+
+=back
+
+=head1 AUTHOR
+
+Heiko Schlittermann L<mailto:hs@schlittermann.de>
+
+=cut