diff options
-rw-r--r-- | beam.conf | 26 | ||||
-rw-r--r-- | lib/App/Beam.pm | 40 | ||||
-rw-r--r-- | lib/App/Beam/Backend/Tar.pm | 15 | ||||
-rw-r--r-- | lib/App/Beam/Backup.pm | 9 | ||||
-rw-r--r-- | lib/App/Beam/History/Entry.pm | 10 | ||||
-rw-r--r-- | lib/App/Beam/Restore.pm | 10 |
6 files changed, 85 insertions, 25 deletions
@@ -1,11 +1,15 @@ [core] # Location of the state file -# statfile = /var/spool/beam/beam.state - statfile = /tmp/beam.state + statfile = /var/spool/beam/beam.state # Directory for temporary files tempdir = /tmp # archivedir = /var/backups + # List of items to back up. Each item is described in + # section [item NAME]. It is OK to have such sections + # declaring NAMEs not listed in core.items. + # If this variable is absent or empty, all itmes from all + # such sections will be processed. items = www # Configure logging @@ -47,6 +51,9 @@ retain = 8 [backend tar] + # Full pathname of the tar binary + binary = /bin/tar + # Any additional options to pass to tar. Do not place tar operation # switches (as -c, -t, etc.) here! These will be added automatically # by appropriate scripts, depending on the operation being performed. @@ -66,10 +73,17 @@ # This variable must be set snapshot-dir = /var/lib/backups -# [item system] -# backend = tar -# directory = / -# file = etc var/spool/cron +[item system] + # Declare the backend to use for that item. + backend = tar + # The following settings are specific for tar backend: + # Name of the directory to archive. + directory = / + # Whitespace-separated list of file names within that directory + # If emtpy, all files will be archived. + file = etc var/spool/cron + # Additional tar(1) options. These complement backend.tar.options + #options = [item databases] backend = mysql diff --git a/lib/App/Beam.pm b/lib/App/Beam.pm index 3bee8b9..550bfcf 100644 --- a/lib/App/Beam.pm +++ b/lib/App/Beam.pm @@ -9,8 +9,9 @@ use POSIX qw(strftime floor); require App::Beam::Config; our @ISA = qw(App::Beam::Config); - + use App::Beam::History; +use App::Beam::History::Entry qw(:state); my $default_config_file = '/etc/beam.conf'; @@ -255,8 +256,8 @@ sub format_name { my ($self, $name, $idx) = @_; my $rec = $self->{history}->top($idx); return undef unless defined $rec; - $name .= '-' - . $rec->cycle + $name .= '-' if $name ne ''; + $name .= $rec->cycle . '-' . $rec->round . '-' @@ -301,7 +302,7 @@ sub load_backends { } } split(/\s+/, $self->get('core.items'))) { my $pack = "App::Beam::Backend::" . ucfirst($be); - $self->debug(1, "loading $pack"); + $self->debug(2, "loading $pack"); my $obj = eval "use $pack; new $pack(\$self);"; if ($@) { $self->logger('crit', $@); @@ -323,14 +324,14 @@ sub begin { my $options = $self->get('logger.syslog.options') || 'pid'; openlog($tag, $options, $facility) or do { - croak "openlog failed"; + carp "openlog failed"; exit(EX_CONFIG); } } elsif (my $filename = $self->get('logger.file.name')) { my $mode = $self->get('logger.file.append') ? '>>' : '>'; open(STDERR, $mode, $filename) or do { - croak "can't open file $filename for logging"; + carp "can't open file $filename for logging"; exit(EX_CANTCREAT); }; } @@ -356,7 +357,22 @@ sub begin { exit(EX_CANTCREAT); } } - + +use constant { + EX_SUCCESS => 0, # Successful termination + EX_FAILURE => 1, # All operations failed + EX_PARTIAL => 2 # Some operations failed +}; + +sub update_status { + my ($self, $ctr) = @_; + unless ($ctr == STATE_SUCCESS || $ctr == STATE_FAILURE) { + carp "invalid state counter $ctr; assuming STATE_FAILURE"; + $ctr = STATE_FAILURE; + } + $self->{counter}[$ctr]++; +} + sub end { my $self = shift; $self->{history}->top->finish; @@ -365,6 +381,16 @@ sub end { if ($self->get('logger.channel') eq 'syslog') { closelog(); } + + if ($self->{counter}[STATE_SUCCESS] > 0) { + if ($self->{counter}[STATE_FAILURE] > 0) { + exit(EX_PARTIAL); + } else { + exit(EX_SUCCESS); + } + } else { + exit(EX_FAILURE); + } } sub run { diff --git a/lib/App/Beam/Backend/Tar.pm b/lib/App/Beam/Backend/Tar.pm index bbb4730..f469485 100644 --- a/lib/App/Beam/Backend/Tar.pm +++ b/lib/App/Beam/Backend/Tar.pm @@ -106,7 +106,7 @@ sub mksnapshot { if ($self->record->level != 0) { my $prev = $self->snapshot_name($item, 1); if ($prev) { - $self->debug(1, "cp $prev $snapshot"); + $self->debug(2, "cp $prev $snapshot"); unless ($self->dry_run) { use File::Copy; unless (copy($prev, $snapshot)) { @@ -151,7 +151,7 @@ sub backup { $cmd->add('.'); } - $self->debug(1, "running ".$cmd->command_line); + $self->debug(2, "running ".$cmd->command_line); my $ret = STATE_SUCCESS; unless ($self->dry_run) { $cmd->run; @@ -186,7 +186,6 @@ sub find_full_backup { sub restore_from { my ($self, $index, $item) = @_; - print "restore $item from $index\n"; my $basename = $self->{beam}->format_name($item, $index); croak "undefined basename" unless defined $basename; my $archive = $self->get('core', 'archivedir') @@ -194,6 +193,7 @@ sub restore_from { . $basename . '.' . $self->get('backend', 'tar', 'suffix'); + $self->debug(1, "restoring \"$item\" from $archive"); my $cmd = new App::Beam::Command($self->get('backend', 'tar', 'binary')); $cmd->set_logger(CHAN_STDERR, sub { $self->logger('err', $_) }); @@ -208,7 +208,7 @@ sub restore_from { $cmd->add('-C', $self->get('item', $item, 'directory')); $cmd->add('--listed', '/dev/null'); - $self->debug(1, "running ".$cmd->command_line); + $self->debug(2, "running ".$cmd->command_line); my $ret = STATE_SUCCESS; unless ($self->dry_run) { $cmd->run; @@ -224,10 +224,15 @@ sub restore { my $i = $self->find_full_backup($index, $item); return unless defined $i; + my $result = STATE_SUCCESS; + while ($i >= $index) { - $self->restore_from($i, $item); + if ($self->restore_from($i, $item) != STATE_SUCCESS) { + $result = STATE_FAILURE; + } $i--; } + return $result; } 1; diff --git a/lib/App/Beam/Backup.pm b/lib/App/Beam/Backup.pm index c2da254..68d7360 100644 --- a/lib/App/Beam/Backup.pm +++ b/lib/App/Beam/Backup.pm @@ -7,6 +7,7 @@ require App::Beam; our @ISA = qw(App::Beam); use Unix::Sysexits; +use App::Beam::History::Entry qw(state_string); =head1 NAME @@ -43,11 +44,15 @@ sub run { #GetOptionsFromArray(\@_, ...); my @items = $self->check_items(@_); - + $self->debug(1, "creating backup " . $self->format_name); foreach my $item (@items) { + $self->debug(1, "backing up $item"); my $backend = $self->{backend}{$self->get("item.$item.backend")}; $self->{history}->top->begin_entry($item); - $self->{history}->top->finish_entry($item, $backend->backup($item)); + my $ret = $backend->backup($item); + $self->{history}->top->finish_entry($item, $ret); + $self->update_status($ret); + $self->debug(1, "$item: " . state_string($ret)); } } diff --git a/lib/App/Beam/History/Entry.pm b/lib/App/Beam/History/Entry.pm index 8443f3f..e5a8c7e 100644 --- a/lib/App/Beam/History/Entry.pm +++ b/lib/App/Beam/History/Entry.pm @@ -5,7 +5,7 @@ use Carp; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(STATE_PENDING STATE_SUCCESS STATE_FAILURE); +our @EXPORT_OK = qw(STATE_PENDING STATE_SUCCESS STATE_FAILURE state_string); our %EXPORT_TAGS = ( state => [ qw(STATE_PENDING STATE_SUCCESS STATE_FAILURE) ] ); @@ -40,9 +40,15 @@ sub state { return $self->{state}; } +sub state_string { + my ($state) = @_; + return $state unless $state <= $#state_str; + return $state_str[$state]; +} + sub state_name { my ($self) = @_; - return $state_str[$self->state]; + return state_string($self->state); } sub start_time { diff --git a/lib/App/Beam/Restore.pm b/lib/App/Beam/Restore.pm index 7e3129a..5c2dcf2 100644 --- a/lib/App/Beam/Restore.pm +++ b/lib/App/Beam/Restore.pm @@ -8,7 +8,7 @@ our @ISA = qw(App::Beam); use Unix::Sysexits; use App::Beam; -use App::Beam::History::Entry qw(:state); +use App::Beam::History::Entry qw(state_string :state); use Getopt::Long qw(GetOptionsFromArray); =head1 NAME @@ -69,13 +69,17 @@ sub run { unless defined $index; } - $self->debug(1, "restoring from backup $index"); + $self->debug(1, "restoring from backup " . + $self->format_name(undef, $index)); $self->abend(EX_UNAVAILABLE, "backup $index was not successful") unless $self->{history}->top($index)->state == STATE_SUCCESS; foreach my $item (@items) { + $self->debug(1, "restoring $item"); my $backend = $self->{backend}{$self->get("item.$item.backend")}; - $backend->restore($index, $item); + my $status = $backend->restore($index, $item); + $self->update_status($status); + $self->debug(1, "$item: " . state_string($status)); } } |