# HG changeset patch # User Heiko Schlittermann (JUMPER) # Date 1438093413 -7200 # Node ID 0bab1e2baa76803c918813097ffc982b6a2635fc Initial Version diff -r 000000000000 -r 0bab1e2baa76 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 +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] [ []...] + +=head1 DESCRIPTION + +for all active PostgreSQL clusters and writes the backups as TAR archives. + +If a I and optionally Is are specified on the command line, +all clusters (or the specified clusters) of the I are saved. + +Without any I/I specification all B clusters are backed up. + +=head1 OPTIONS + +=over + +=item B<-d>|B<--basedir> I + +The directory where the subdirectories for the backups will be placed. (default: F<.>) + +=item B<-k>|B<--keep> I + +The number of generations to keep. Old backups will be only removed if B +specified backups succeed. (no default) + +=item B<--dry> + +Dry run, show what will be done. (default: undef) + +=back + +=head1 AUTHOR + +Heiko Schlittermann L + +=cut