=item status
-(string) -- "OK", "PARTIAL", or "FAIL"
+(string) -- The status of the dump - "OK", "PARTIAL", or "FAIL". If a disk
+failed to dump at all, then it is not part of the catalog and thus will not
+have an associated dump row.
=item message
=item kb
-(integer) -- size (in kb) of this part
+(integer) -- size (in kb) of the dump on disk
=item orig_kb
-(integer) -- size (in kb) of the complete dump (uncompress and uncrypted).
+(integer) -- size (in kb) of the complete dump (before compression or encryption); undef
+if not available
=item sec
=item status
-(string) -- "OK", "PARTIAL" or some other descriptor
+(string) -- The status of the part - "OK", "PARTIAL", or "FAILED".
=item partnum
Return the most recent write timestamp.
+=item get_latest_write_timestamp(type => 'amvault')
+=item get_latest_write_timestamp(types => [ 'amvault', .. ])
+
+Return the timestamp of the most recent dump of the given type or types. The
+available types are given below for C<get_run_type>.
+
=item get_labels_written_at_timestamp($ts)
Return a list of labels for volumes written at the given timestamp.
+=item get_run_type($ts)
+
+Return the type of run made at the given timestamp. The result is one of
+C<amvault>, C<amdump>, C<amflush>, or the default, C<unknown>.
+
=back
=head2 PARTS
This function returns a sequence of dumps. Values in C<%parameters> restrict
the set of dumps that are returned. The same keys as are used for C<get_parts>
-are available here, with the exception of C<label> and C<labels>. The
-C<status> key applies to the dump status, not the status of its constituent
-parts.
+are available here, with the exception of C<label> and C<labels>. In this
+case, the C<status> parameter applies to the dump status, not the status of its
+constituent parts.
=item sort_dumps([ $key1, $key2 ], @dumps)
use Amanda::Tapelist;
use Amanda::Config qw( :init :getconf config_dir_relative );
use Amanda::Util qw( quote_string weaken_ref );
+use File::Glob qw( :glob );
use warnings;
use strict;
# tapelist cache
my $tapelist = undef;
-my $tapelist_filename = undef;
# utility function
sub zeropad {
}
sub get_latest_write_timestamp {
+ my %params = @_;
+
+ if ($params{'type'}) {
+ push @{$params{'types'}}, $params{'type'};
+ }
+
# get all of the timestamps and select the last one
my @timestamps = get_write_timestamps();
if (@timestamps) {
- return $timestamps[-1];
+ # if we're not looking for a particular type, then this is easy
+ if (!exists $params{'types'}) {
+ return $timestamps[-1];
+ }
+
+ # otherwise we need to search backward until we find a logfile of
+ # the right type
+ while (@timestamps) {
+ my $ts = pop @timestamps;
+ my $typ = get_run_type($ts);
+ if (grep { $_ eq $typ } @{$params{'types'}}) {
+ return $ts;
+ }
+ }
}
return undef;
}
+sub get_run_type {
+ my ($write_timestamp) = @_;
+
+ # find all of the logfiles with that name
+ my $logdir = getconf($CNF_LOGDIR);
+ my @matches = File::Glob::bsd_glob("$logdir/log.$write_timestamp.*", GLOB_NOSORT);
+ if ($write_timestamp =~ /000000$/) {
+ my $write_datestamp = substr($write_timestamp, 0, 8);
+ push @matches, File::Glob::bsd_glob("$logdir/log.$write_datestamp.*", GLOB_NOSORT);
+ }
+
+ for my $lf (@matches) {
+ open(my $fh, "<", $lf) or next;
+ while (<$fh>) {
+ # amflush and amvault put their own names in
+ return $1 if (/^START (amflush|amvault)/);
+ # but for amdump we see planner
+ return 'amdump' if (/^START planner/);
+ }
+ }
+
+ return "unknown";
+}
+
+
# this generic function implements the loop of scanning logfiles to find
# the requested data; get_parts and get_dumps then adjust the results to
# match what the user expects.
and !exists($labels_hash{$find_result->{'label'}}));
if ($get_what eq 'parts') {
next if (exists($params{'status'})
+ and defined $find_result->{'status'}
and $find_result->{'status'} ne $params{'status'});
}
my $dump_timestamp = zeropad($find_result->{'timestamp'});
my $dumpkey = join("\0", $find_result->{'hostname'}, $find_result->{'diskname'},
- $write_timestamp, $find_result->{'level'});
+ $write_timestamp, $find_result->{'level'}, $dump_timestamp);
my $dump = $dumps{$dumpkey};
if (!defined $dump) {
$dump = $dumps{$dumpkey} = {
label => $find_result->{'label'},
filenum => $find_result->{'filenum'},
dump => $dump,
- status => $find_result->{'status'},
+ status => $find_result->{'status'} || 'FAILED',
sec => $find_result->{'sec'},
kb => $find_result->{'kb'},
orig_kb => $find_result->{'orig_kb'},
%part = (
holding_file => $find_result->{'label'},
dump => $dump,
- status => $find_result->{'status'},
+ status => $find_result->{'status'} || 'FAILED',
sec => 0.0,
kb => $find_result->{'kb'},
orig_kb => $find_result->{'orig_kb'},
partnum => 1,
);
# and fix up the dump, too
- $dump->{'status'} = $find_result->{'status'};
+ $dump->{'status'} = $find_result->{'status'} || 'FAILED';
$dump->{'kb'} = $find_result->{'kb'};
$dump->{'sec'} = $find_result->{'sec'};
}
$status = 'PARTIAL';
} elsif ($type == $L_FAIL) {
$status = 'FAIL';
+ } elsif ($type == $L_SUCCESS) {
+ $status = "OK";
} else {
next;
}
($hostname, $str) = Amanda::Util::skip_quoted_string($str);
($diskname, $str) = Amanda::Util::skip_quoted_string($str);
($dump_timestamp, $str) = Amanda::Util::skip_quoted_string($str);
- if ($status ne 'FAIL') {
+ if ($status ne 'FAIL' and $type != $L_SUCCESS) { # nparts is not in SUCCESS lines
($nparts, $str) = Amanda::Util::skip_quoted_string($str);
} else {
$nparts = 0;
($level, $str) = Amanda::Util::skip_quoted_string($str);
if ($status ne 'FAIL') {
my $s = $str;
- ($secs, $kb, $str) = ($str =~ /^\[sec ([0-9.]+) kb (\d+) .*\] ?(.*)$/)
+ ($secs, $kb, $str) = ($str =~ /^\[sec ([-0-9.]+) kb (\d+).*\] ?(.*)$/)
or die("'$s'");
+ $secs = 0.1 if ($secs <= 0);
}
if ($status ne 'OK') {
$message = $str;
and !match_datestamp("".$ds->{'datestamp'}, $dump_timestamp));
next if (defined $ds->{'level'}
and !match_level("".$ds->{'level'}, $level));
-
+ next if (defined $ds->{'write_timestamp'}
+ and !match_datestamp("".$ds->{'write_timestamp'}, $write_timestamp));
$ok = 1;
last;
}
next unless $ok;
}
- my $dumpkey = join("\0", $hostname, $diskname, $write_timestamp, $level);
+ my $dumpkey = join("\0", $hostname, $diskname, $write_timestamp,
+ $level, zeropad($dump_timestamp));
my $dump = $dumps{$dumpkey};
if (!defined $dump) {
# this will happen when a dump has no parts - a FAILed dump.
$dump = $dumps{$dumpkey} = {
- dump_timestamp => $dump_timestamp,
+ dump_timestamp => zeropad($dump_timestamp),
write_timestamp => $write_timestamp,
hostname => $hostname,
diskname => $diskname,
level => $level+0,
+ orig_kb => undef,
+ status => "FAILED",
+ # message set below
nparts => $nparts, # hopefully 0?
+ # kb set below
+ # sec set below
};
}
print $logfh
"START taper datestamp $write_timestamp label $dump->{label} tape $i\n";
- if (!defined $tapelist_filename) {
- $tapelist_filename = config_dir_relative(getconf($CNF_TAPELIST));
+ if (!defined $tapelist) {
+ _load_tapelist();
+ } else {
+ # reload the tapelist immediately, in case it's been modified
+ $tapelist->reload();
}
- # reload the tapelist immediately, in case it's been modified
- $tapelist = Amanda::Tapelist::read_tapelist($tapelist_filename);
-
# see if we need to add an entry to the tapelist for this dump
if (!grep { $_->{'label'} eq $dump->{'label'}
and zeropad($_->{'datestamp'}) eq zeropad($dump->{'write_timestamp'})
- } @$tapelist) {
- $tapelist->add_tapelabel($write_timestamp, $dump->{'label'});
- $tapelist->write($tapelist_filename);
+ } @{$tapelist->{tles}}) {
+ $tapelist->reload(1);
+ $tapelist->add_tapelabel($write_timestamp, $dump->{'label'}, undef, 1);
+ $tapelist->write();
}
}
sub _load_tapelist {
if (!defined $tapelist) {
- $tapelist_filename = config_dir_relative(getconf($CNF_TAPELIST));
- $tapelist = Amanda::Tapelist::read_tapelist($tapelist_filename);
+ my $tapelist_filename = config_dir_relative(getconf($CNF_TAPELIST));
+ $tapelist = Amanda::Tapelist->new($tapelist_filename);
}
}
sub _clear_cache { # (used by installcheck)
- $tapelist = $tapelist_filename = undef;
+ $tapelist = undef;
}
1;