Imported Upstream version 3.2.1
[debian/amanda] / perl / Amanda / Report.pm
index a94912aaa9a2cd925892a546834196585e205516..94e79e75bc13cc7af8a7374c2da730889a0a89e6 100644 (file)
@@ -43,7 +43,9 @@ This module reads the logfile passed to it and aggregates the data in
 a format of nested hashes for convenient output.  All data read in is
 stored in C<< $report->{data} >>.
 
 a format of nested hashes for convenient output.  All data read in is
 stored in C<< $report->{data} >>.
 
-=head2 my $report = Amanda::Report->new($logfile, $historical);
+=head2 Creating a Report
+
+  my $report = Amanda::Report->new($logfile, $historical);
 
 The constructor reads the logfile and produces the report, which can then be
 queried with the other methods.  C<$logfile> should specify the path to the
 
 The constructor reads the logfile and produces the report, which can then be
 queried with the other methods.  C<$logfile> should specify the path to the
@@ -52,25 +54,31 @@ logfile, then C<$historical> should be false.  Non-historical reports may draw
 information from the current Amanda environment, e.g., holding disks and info
 files.
 
 information from the current Amanda environment, e.g., holding disks and info
 files.
 
-=head2 my $datestamp = $report->get_timestamp();
+=head2 Summary Information
+
+Note that most of the data provided by these methods is simply a reference to
+data stored within the report, and should thus be considered read-only.  For
+example, do not use C<shift> or C<pop> to destructively consume lists.
+
+  my $datestamp = $report->get_timestamp();
 
 This returns the run timestamp for this dump run.  This is determined from one
 of several START entries.  This returns a full 14-digit timestamp regardless of
 the setting of C<usetimestamps> now or during the dump run.
 
 
 This returns the run timestamp for this dump run.  This is determined from one
 of several START entries.  This returns a full 14-digit timestamp regardless of
 the setting of C<usetimestamps> now or during the dump run.
 
-=head2 my @hosts = $report->get_hosts();
+  my @hosts = $report->get_hosts();
 
 This method returns a list containing the hosts that have been seen in
 a logfile.  In a scalar context, C<get_hosts> returns the number of
 hosts seen.
 
 
 This method returns a list containing the hosts that have been seen in
 a logfile.  In a scalar context, C<get_hosts> returns the number of
 hosts seen.
 
-=head2 my @disks = $report->get_disks($hostname);
+  my @disks = $report->get_disks($hostname);
 
 This method returns a list of disks that were archived under the given
 C<$hostname>.  In a scalar context, this method returns the number of
 disks seen, belonging to the hostname.
 
 
 This method returns a list of disks that were archived under the given
 C<$hostname>.  In a scalar context, this method returns the number of
 disks seen, belonging to the hostname.
 
-=head2 my @dles = $report->get_dles();
+  my @dles = $report->get_dles();
 
 This method returns a list of list references.  Each referenced list
 contains a hostname & disk pair that has been reported by either the
 
 This method returns a list of list references.  Each referenced list
 contains a hostname & disk pair that has been reported by either the
@@ -85,70 +93,153 @@ in the logfile.
         [ 'example3', '/var/www' ],
     );
 
         [ 'example3', '/var/www' ],
     );
 
-=head2 my $dle = $report->get_dle_info($hostname, $disk [,$field] );
+  if ( $report->get_flag($flag) ) { ... }
+
+The C<get_flag> method accesses a number of flags that represent the state of
+the dump.  A true value is returned if the flag is set, and undef otherwise.
+The available flags are:
+
+=over
 
 
-This method returns all the information stored in the per-DLE section
-for the given C<$hostname> and C<disk>.  The returned value is a hash
-reference to the data as it is stored in the internal data
-structure. Modifying the return value will modify the values in the
-C<Amanda::Report> object.
+=item C<got_finish>
 
 
-=head2 my $info = $report->get_program_info($program [,$field, $default] );
+This flag is true when the driver finished
+correctly.  It indicates that the dump run has finished and cleaned
+up.
+
+=item C<degraded_mode>
+
+This flag is set if the taper encounters an
+error that forces it into degraded mode.
 
 
-This method returns a reference to the data for the given C<$program>.
-If the optional argument C<$field> is provided, that field in the
-indicated program is returned.  If the key C<$field> does not exist in
-the program, then it is inserted with the value C<$default>, The
-returned value is a reference to the internal C<Amanda::Report> data
-structure and will in turn modify the C<$report> object.
+=item C<amflush_run>
 
 
-=head2 if ( $report->get_flag($flag) ) { ... }
+This flag is set if amflush is run instead of planner.
 
 
-This method accesses a number of flags that represent the state of the
-dump.  A true value is returned if the flag is set, and undef
-otherwise.
+=item C<amvault_run>
+
+This flag is set if the run was by amvault.
+
+=item C<normal_run>
+
+This flag is set when planner is run.  Its value
+should be opposite of C<amflush_run>.
+
+=item C<dump_failed>
+
+If a dump failed.
+
+=item C<dump_strange>
+
+If a dump end in strange result.
+
+=item C<results_missing>
+
+If this was a normal run, but some DLEs named by the
+planner do not have any results, then this flag is set.  Users should look for
+DLEs with an empty C<dump> key to enumerate the missing results.
+
+=item C<historical>
+
+This flag is set if this is a "historical" report.  It is
+based on the value passed to the constructor.
+
+=back
+
+=head2 Report Data
+
+  my $dle = $report->get_dle_info($hostname, $disk [,$field] );
+
+This method returns the DLE information for the given C<$hostname> and C<disk>,
+or if C<$field> is given, returns that field of the DLE information.  See the
+DATA DESCRIPTION section for the format of this information.
+
+  my $info = $report->get_program_info($program [,$field] );
+
+This method returns the program information for the given C<$program>, or if
+C<$field> is given, returns that field of the DLE information.  See the DATA
+DESCRIPTION section for the format of this information.
 
 =head1 DATA DESCRIPTION
 
 
 =head1 DATA DESCRIPTION
 
-The data in the logfile is stored in the module at
-C<< $report->{data} >>.  Beyond that, there are a number of subdivisions
-that track both global and per-host status of the given Amanda run that
-the logfile represents.
+=head2 Top Level
+
+The data in the logfile is stored in the module at C<< $report->{data} >>.
+Beneath that, there are a number of subdivisions that track both global and
+per-host status of the given Amanda run that the logfile represents.  Note that
+these subdivisions are usually accessed via C<get_dle_info> and
+C<get_program_info>, as described above.
 
 
-=head2 $data->{programs}
+  $data->{programs}
 
 the C<programs> key of the data points to a hash of global program
 
 the C<programs> key of the data points to a hash of global program
-data, with one element per program.  A number of fields are common
-across all of the different programs.
+information, with one element per program.  See the Programs section, below.
+
+  $data->{boguses}
+
+The C<boguses> key refers to a list of arrayrefs of the form
+
+  [$prog, $type, $str]
+
+as returned directly by C<Amanda::Logfile::get_logline>.  These lines are not
+in a recognized trace log format.
+
+  $data->{disklist}
+
+The C<disklist> key points to a two-level hash of hostnames and
+disknames as present in the logfile.  It looks something like this:
+
+    $report->{data}{disklist} = {
+        "server.example.org" => {
+            "/home" => {...},
+            "/var"  => {...},
+        },
+        "workstation.example.org" => {
+            "/etc"     => {...},
+            "/var/www" => {...},
+        },
+    };
+
+Each C<{...}> in the above contains information about the corresponding DLE.  See DLEs, below.
+
+=head2 Programs
+
+Each program involved in a dump has a hash giving information about its
+performance during the run.  A number of fields are common across all of the
+different programs:
 
 =over
 
 
 =over
 
-=item C<start> - the numeric timestamp at which the process was
-started.
+=item C<start>
 
 
-=item C<time> - the length of time (in seconds) that the program ran.
+the numeric timestamp at which the process was started.
 
 
-=item C<notes> - a list which stores all notes reported to the logfile
+=item C<time>
+
+the length of time (in seconds) that the program ran.
+
+=item C<notes>
+
+a list which stores all notes reported to the logfile
 by the corresponding program.
 
 by the corresponding program.
 
-=item C<errors> - a list which stores all errors reported to the
+=item C<errors>
+
+a list which stores all errors reported to the
 logfile by the corresponding program.
 
 =back
 
 logfile by the corresponding program.
 
 =back
 
-In the below, assume
-
-  my $programs = $report->{data}{programs}
+Program-specific fields are described in the following sections.
 
 
-=head3 $programs->{planner}
+=head3 planner
 
 
-The planner logs very little DLE-specific information other than
-determining what will be backed up.  It has no special fields other
-than those given above.
+The planner logs very little information other than determining what will be
+backed up.  It has no special fields other than those given above.
 
 
-=head3 $programs->{driver}
+=head3 driver
 
 
-The driver has one unique field that the other program-specific
+The driver has one field that the other program-specific
 entries do not:
 
 =over
 entries do not:
 
 =over
@@ -157,35 +248,28 @@ entries do not:
 
 =back
 
 
 =back
 
-=head3 $programs->{amflush}
+=head3 amflush and amdump
 
 
-When amflush is present, it records what disklist entries need to be
-processed instead of the planner.  It also has no special fields.
+No special fields.
 
 
-=head3 $programs->{amdump}
-
-This program is a control program that spawns off dumper programs.  It
-has no special fields.
-
-=head3 $programs->{dumper} and $programs->{chunker}
+=head3 dumper and chunker
 
 Most of the chunker's output and the dumper's output can be tied to a
 
 Most of the chunker's output and the dumper's output can be tied to a
-particular DLE, so their C<programs> hashes are limited to C<notes>
-and C<errors>.
+particular DLE, so their C<programs> hashes are limited to C<notes> and
+C<errors>.
 
 
-=head3 $programs->{taper}
+=head3 taper
 
 
-The taper hash holds notes and errors for the per-instance runs of the
-taper program, but also has a unique field which tracks the tapes seen
-in the logfile:
+The taper hash holds notes and errors for the per-instance runs of the taper
+program, but also tracks the tapes seen in the logfile:
 
 =over
 
 =item C<tapes>
 
 
 =over
 
 =item C<tapes>
 
-The C<tapes> field is a hash reference keyed by the label of the tape.
+This field is a hash reference keyed by the label of the tape.
 each value of the key is another hash which stores date, size, and the
 each value of the key is another hash which stores date, size, and the
-number of files seen by this backup on the tape.  Here is an example:
+number of files seen by this backup on the tape.  For example:
 
     $report->{data}{programs}{taper}{tapes} = {
         FakeTape01 => {
 
     $report->{data}{programs}{taper}{tapes} = {
         FakeTape01 => {
@@ -202,151 +286,126 @@ number of files seen by this backup on the tape.  Here is an example:
 
 The C<tape_labels> field is a reference to a list which records the
 order that the tapes have been seen.  This list should be used as an
 
 The C<tape_labels> field is a reference to a list which records the
 order that the tapes have been seen.  This list should be used as an
-index for outputting the contents of C<tapes>.
+ordered index for C<tapes>.
 
 =back
 
 
 =back
 
-=head2 $data->{boguses}
+=head2 DLEs
 
 
-The C<boguses> key refers to a list of arrayrefs of the form
-
-  [$prog, $type, $str]
+In the below, C<$dle> is the hash representing one disklist entry.
 
 
-as returned directly by C<Amanda::Logfile::get_logline>.  These lines were not
-parseable because they were not in a recognized format of loglines.
+The C<estimate> key describes the estimate given by the planner.  For
+example:
 
 
-=head2 $data->{disklist}
+    $dle->{estimate} = {
+       level => 0,     # the level of the backup
+       sec   => 20,    # estimated time to back up (seconds)
+       nkb   => 2048,  # expected uncompressed size (kb)
+       ckb   => 1293,  # expected compressed size (kb)
+       kps   => 934.1, # speed of the backup (kb/sec)
+    };
 
 
-The C<disklist> key points to a two-level hash of hostnames and
-disknames as present in the logfile.  It looks something like this:
+Each dump of the DLE is represented in C<< $dle->{dumps} >>.  This is a hash,
+keyed by dump timestamp with a list of tries as the value for each dump.  Each
+try represents a specific attempt to finish writing this dump to a volume.  If
+an error occurs during the backup of a DLE and is retried, a second try is
+pushed to the tries list.  For example:
 
 
-    $report->{data}{disklist} = {
-        "server.example.org" => {
-            "/home" => {...},
-            "/var"  => {...},
-        },
-        "workstation.example.org" => {
-            "/etc"     => {...},
-            "/var/www" => {...},
-        },
+    $dle->{dumps} = {
+       '20100317142122' => [ $try1 ],
+       '20100318141930' => [ $try1, $try2 ],
     };
 
     };
 
-In the below, C<$dle> represents one disklist entry (C<{ ... }> in the
-above).  Each DLE has three major components: estimates, tries, and
-parts.
+=head3 Tries
 
 
-=head3 Estimates
+A try is a hash with at least one dumper, taper, and/or chunker DLE program as
+a key.  These entries contain the results from the associated program during
+try.
 
 
-The value of C<< $dle->{estimate} >> describes the estimate given by
-the planner.
+There are a number of common fields between all three elements:
 
 
-    $dle->{estimate} = {
-       level => "0",    # the level of the backup
-       sec   => "20",   # estimated time to back up (seconds)
-       nkb   => "2048", # expected uncompressed size (kb)
-       ckb   => "",     # expected compressed size (kb)
-       kps   => "",     # speed of the backup (kb/sec)
-    };
+=over
 
 
-=head3 Tries
+=item C<date>
 
 
-Tries are located at C<< $dle->{tries} >>.  This is a list of tries,
-each of which is a hash that represents a specific attempt to back up
-this DLE.  If an error occurs during the backup of a DLE and is
-retried, a second try is pushed to the tries list.
+a timestamp of when the program finished (if the program exited)
 
 
-A try is a hash with at least one dumper, taper, and/or chunker DLE
-program as a key.  These entries contain the exit conditions of that
-particular program for that particular try.
+=item C<status>
 
 
-There are a number of common fields between all three elements:
+the status of the dump at this program on this try ("success", "partial",
+"done", or "failed").  The planner adds an extra "skipped" status which is
+added when the planner decides to skip a DLE due to user configuration (e.g.,
+C<skipincr>).
 
 
-=over
+=item C<level>
 
 
-=item C<date> - a timestamp of when the program finished.
+the incremental level of the backup.
 
 
-=item C<status> - the exit status of the program on this try.
+=item C<sec>
 
 
-=item C<level> - the incremental level of the backup.
+the time in seconds for the program to finish.
 
 
-=item C<sec> - the time in seconds for the program to finish.
+=item C<kb>
 
 
-=item C<kb> - the size of the data dumped in kb.
+the size of the data dumped in kb.
 
 
-=item C<kps> - the rate at which the program was able to process data,
+=item C<kps>
+
+the rate at which the program was able to process data,
 in kb/sec.
 
 in kb/sec.
 
-=item C<error> - if the program fails, this field is set to record the
-error message.
+=item C<error>
+
+if the program fails, this field contains the error message
 
 =back
 
 
 =back
 
-The C<dumper> hash has an C<orig_kb> field, giving the size of the
-data dumped from the source, before any compression. If encountered,
-the C<dumper> hash may also contain a C<stranges> field, which is a
-list reference to all the messages of type C<L_STRANGE> encountered
-during the process.
+The C<dumper> hash has an C<orig_kb> field, giving the size of the data dumped
+from the source, before any compression. If encountered, the C<dumper> hash may
+also contain a C<stranges> field, which is a list of all the messages of type
+C<L_STRANGE> encountered during the process.
 
 
-The C<taper> hash contains all the exit status data given by the
-taper.  Because the taper has timestamped parts, the program itself
-does not have a C<date> field.  Taper has one unique field, C<parts>,
-which is a reference to a list of hash references which describe the
-parts that have been taped during execution.
+The C<taper> hash contains all the exit status data given by the taper.
+Because the same taper process handles multiple dumps, it does not have a
+C<date> field.  However, the taper does have an additional field, C<parts>,
+containing a list of parts written for this dump.
 
 =head3 Parts
 
 
 =head3 Parts
 
-Every taper process logs the parts it writes to tape in a list located
-at C<$taper->{parts}>.  Each item in the list is a hash reference with
-the following fields:
+Each item in the list of taper parts is a hash with the following
+fields:
 
 =over
 
 
 =over
 
-=item C<label> - the name of the tape that the part was written to.
+=item C<label>
 
 
-=item C<date> - the datestamp at which this part was written.
+the name of the tape that the part was written to.
 
 
-=item C<file> - the filename of the part.
+=item C<date>
 
 
-=item C<part> - the sequence number of the part for the DLE that the
-part is archiving.
+the datestamp at which this part was written.
 
 
-=item C<sec> - the length of time, in seconds, that the part took to
-be written.
+=item C<file>
 
 
-=item C<kb> - the total size of the part.
+the filename of the part.
 
 
-=item C<kps> - the speed at which the part was written.
+=item C<part>
 
 
-=back
-
-=head1 FLAGS
-
-During the reading of a logfile, the module will set and unset a
-number of flags to indicate the state of the backup.  These are used
-to indicate the type of backup or the conditions of success.
+the sequence number of the part for the DLE that the
+part is archiving.
 
 
-The following is a list of currently recognized flags:
+=item C<sec>
 
 
-=over
+the length of time, in seconds, that the part took to
+be written.
 
 
-=item C<got_finish> - This flag is true when the driver finished
-correctly.  It indicates that the dump run has finished and cleaned
-up.
+=item C<kb>
 
 
-=item C<degraded_mode> - This flag is set if the taper encounters an
-error that forces it into degraded mode.
+the total size of the part.
 
 
-=item C<amflush_run> - This flag is set if amflush is run instead of planner.
+=item C<kps>
 
 
-=item C<normal_run> - This flag is set when planner is run.  Its value
-should be opposite of C<amflush_run>.
-
-=item C<results_missing> - If this was a normal run, but some DLEs named by the
-planner do not have any results, then this flag is set.  Users should look for
-DLEs with empty C<tries> to enumerate the missing results.
-
-=item C<historical> - This flag is set if this is a "historical" report.  It is
-based on the value passed to the constructor.
+the speed at which the part was written.
 
 =back
 
 
 =back
 
@@ -398,6 +457,9 @@ sub read_file
       or die "cannot open '$logfname': $!";
 
     $self->{flags}{exit_status} = 0;
       or die "cannot open '$logfname': $!";
 
     $self->{flags}{exit_status} = 0;
+    $self->{flags}{results_missing} = 0;
+    $self->{flags}{dump_failed} = 0;
+    $self->{flags}{dump_strange} = 0;
 
     while ( my ( $type, $prog, $str ) = Amanda::Logfile::get_logline($logfh) ) {
         $self->read_line( $type, $prog, $str );
 
     while ( my ( $type, $prog, $str ) = Amanda::Logfile::get_logline($logfh) ) {
         $self->read_line( $type, $prog, $str );
@@ -407,17 +469,21 @@ sub read_file
 
     $self->{flags}{historical} = $self->{_historical};
     $self->{flags}{amflush_run} = 0;
 
     $self->{flags}{historical} = $self->{_historical};
     $self->{flags}{amflush_run} = 0;
-    if (
-        !$self->get_flag("normal_run")
-        && (   ( defined $self->get_program_info("amflush") )
-            && ( scalar %{ $self->get_program_info("amflush") } ) )
-      ) {
-       debug("detected an amflush run");
-        $self->{flags}{amflush_run} = 1;
+    $self->{flags}{amvault_run} = 0;
+    if (!$self->get_flag("normal_run")) {
+        if (   ( defined $self->get_program_info("amflush") )
+            && ( scalar %{ $self->get_program_info("amflush") } ) ) {
+           debug("detected an amflush run");
+           $self->{flags}{amflush_run} = 1;
+       } elsif (   ( defined $self->get_program_info("amvault") )
+                 && ( scalar %{ $self->get_program_info("amvault") } ) ) {
+           debug("detected an amvault run");
+           $self->{flags}{amvault_run} = 1;
+       }
     }
 
     }
 
-    # check for missing results
-    $self->check_missing() if $self->get_flag('normal_run');
+    # check for missing, fail and strange results
+    $self->check_missing_fail_strange() if $self->get_flag('normal_run');
 
     # clean up any temporary values in the data
     $self->cleanup();
 
     # clean up any temporary values in the data
     $self->cleanup();
@@ -467,6 +533,9 @@ sub read_line
     } elsif ( $prog == $P_AMFLUSH ) {
         return $self->_handle_amflush_line( $type, $str );
 
     } elsif ( $prog == $P_AMFLUSH ) {
         return $self->_handle_amflush_line( $type, $str );
 
+    } elsif ( $prog == $P_AMVAULT ) {
+        return $self->_handle_amvault_line( $type, $str );
+
     } elsif ( $prog == $P_AMDUMP ) {
         return $self->_handle_amdump_line( $type, $str );
 
     } elsif ( $prog == $P_AMDUMP ) {
         return $self->_handle_amdump_line( $type, $str );
 
@@ -556,7 +625,7 @@ sub get_tape
         push @$tape_labels, $label;
         $tapes->{$label} = {date => "",
                            kb => 0,
         push @$tape_labels, $label;
         $tapes->{$label} = {date => "",
                            kb => 0,
-                           files => "",
+                           files => 0,
                            dle => 0,
                            time => 0};
     }
                            dle => 0,
                            time => 0};
     }
@@ -598,6 +667,9 @@ sub _handle_planner_line
     } elsif ( $type == $L_DISK ) {
         return $self->_handle_disk_line( "planner", $str );
 
     } elsif ( $type == $L_DISK ) {
         return $self->_handle_disk_line( "planner", $str );
 
+    } elsif ( $type == $L_SUCCESS ) {
+        return $self->_handle_success_line( "planner", $str );
+
     } elsif ( $type == $L_ERROR ) {
         return $self->_handle_error_line( "planner", $str );
 
     } elsif ( $type == $L_ERROR ) {
         return $self->_handle_error_line( "planner", $str );
 
@@ -721,7 +793,7 @@ sub _handle_dumper_line
         $orig_kb =~ s{\]$}{};
 
         my $dle    = $disklist->{$hostname}->{$disk};
         $orig_kb =~ s{\]$}{};
 
         my $dle    = $disklist->{$hostname}->{$disk};
-        my $try    = $self->_get_try( $dle, "dumper" );
+        my $try    = $self->_get_try( $dle, "dumper", $self->{'run_timestamp'});
         my $dumper = $try->{dumper} ||= {};
        $dumper->{level} = $level;
        $dumper->{status} = 'strange';
         my $dumper = $try->{dumper} ||= {};
        $dumper->{level} = $level;
        $dumper->{status} = 'strange';
@@ -748,7 +820,7 @@ sub _handle_dumper_line
         $orig_kb =~ s{\]$}{};
 
         my $dle    = $disklist->{$hostname}->{$disk};
         $orig_kb =~ s{\]$}{};
 
         my $dle    = $disklist->{$hostname}->{$disk};
-        my $try    = $self->_get_try( $dle, "dumper" );
+        my $try    = $self->_get_try( $dle, "dumper", $timestamp );
         my $dumper = $try->{dumper} ||= {};
 
         $dumper->{date}      = $timestamp;
         my $dumper = $try->{dumper} ||= {};
 
         $dumper->{date}      = $timestamp;
@@ -787,7 +859,7 @@ sub _handle_chunker_line
     if ( $type == $L_INFO ) {
         return $self->_handle_info_line( "chunker", $str );
 
     if ( $type == $L_INFO ) {
         return $self->_handle_info_line( "chunker", $str );
 
-    } elsif ( $type == $L_SUCCESS || $L_PARTIAL ) {
+    } elsif ( $type == $L_SUCCESS || $type == $L_PARTIAL ) {
 
         my @info = Amanda::Util::split_quoted_strings($str);
         my ( $hostname, $disk, $timestamp, $level ) = @info[ 0 .. 3 ];
 
         my @info = Amanda::Util::split_quoted_strings($str);
         my ( $hostname, $disk, $timestamp, $level ) = @info[ 0 .. 3 ];
@@ -795,7 +867,7 @@ sub _handle_chunker_line
         $kps =~ s{\]$}{};
 
         my $dle     = $disklist->{$hostname}->{$disk};
         $kps =~ s{\]$}{};
 
         my $dle     = $disklist->{$hostname}->{$disk};
-        my $try     = $self->_get_try( $dle, "chunker" );
+        my $try     = $self->_get_try( $dle, "chunker", $timestamp );
         my $chunker = $try->{chunker} ||= {};
 
         $chunker->{date}  = $timestamp;
         my $chunker = $try->{chunker} ||= {};
 
         $chunker->{date}  = $timestamp;
@@ -861,15 +933,8 @@ sub _handle_taper_line
         $kps =~ s{\]$}{};
         $orig_kb =~ s{\]$}{} if defined($orig_kb);
 
         $kps =~ s{\]$}{};
         $orig_kb =~ s{\]$}{} if defined($orig_kb);
 
-       if (!$self->{'_current_tape'} || $label ne $self->{'_current_tape'}->{'label'}) {
-           warning("corrupted logfile - PART or PARTPARTIAL does not match previous START taper");
-       }
-
-       # count this as a filesystem if this is the first part
-        $self->{'_current_tape'}->{dle}++ if $currpart == 1;
-
         my $dle   = $disklist->{$hostname}{$disk};
         my $dle   = $disklist->{$hostname}{$disk};
-        my $try   = $self->_get_try($dle, "taper");
+        my $try   = $self->_get_try($dle, "taper", $timestamp);
         my $taper = $try->{taper} ||= {};
         my $parts = $taper->{parts} ||= [];
 
         my $taper = $try->{taper} ||= {};
         my $parts = $taper->{parts} ||= [];
 
@@ -888,6 +953,8 @@ sub _handle_taper_line
         push @$parts, $part;
 
         my $tape = $self->get_tape($label);
         push @$parts, $part;
 
         my $tape = $self->get_tape($label);
+       # count this as a filesystem if this is the first part
+        $tape->{dle}++ if $currpart == 1;
         $tape->{kb}   += $kb;
         $tape->{time} += $sec;
         $tape->{files}++;
         $tape->{kb}   += $kb;
         $tape->{time} += $sec;
         $tape->{files}++;
@@ -912,7 +979,7 @@ sub _handle_taper_line
         $orig_kb =~ s{\]$}{} if defined $orig_kb;
 
         my $dle   = $disklist->{$hostname}->{$disk};
         $orig_kb =~ s{\]$}{} if defined $orig_kb;
 
         my $dle   = $disklist->{$hostname}->{$disk};
-        my $try   = $self->_get_try($dle, "taper");
+        my $try   = $self->_get_try($dle, "taper", $timestamp);
         my $taper = $try->{taper} ||= {};
         my $parts = $taper->{parts};
 
         my $taper = $try->{taper} ||= {};
         my $parts = $taper->{parts};
 
@@ -938,9 +1005,14 @@ sub _handle_taper_line
 
         if ($str =~ m{^no-tape}) {
 
 
         if ($str =~ m{^no-tape}) {
 
+           my @info = Amanda::Util::split_quoted_strings($str);
+           my $failure_from = $info[1];
+           my $error = join " ", @info[ 2 .. $#info ];
+
             $self->{flags}{exit_status} |= STATUS_TAPE;
             $self->{flags}{degraded_mode} = 1;
             $self->{flags}{exit_status} |= STATUS_TAPE;
             $self->{flags}{degraded_mode} = 1;
-            $taper_p->{tape_error} = $str;
+           $taper_p->{failure_from} = $failure_from;
+            $taper_p->{tape_error} = $error;
 
         } else {
             $self->_handle_error_line("taper", $str);
 
         } else {
             $self->_handle_error_line("taper", $str);
@@ -981,6 +1053,35 @@ sub _handle_amflush_line
     }
 }
 
     }
 }
 
+sub _handle_amvault_line
+{
+    my $self = shift @_;
+    my ( $type, $str ) = @_;
+    my $data      = $self->{data};
+    my $disklist  = $data->{disklist};
+    my $programs  = $data->{programs};
+    my $amvault_p = $programs->{amvault} ||= {};
+
+    if ( $type == $L_START ) {
+        return $self->_handle_start_line( "amvault", $str );
+
+    } elsif ( $type == $L_INFO ) {
+        return $self->_handle_info_line( "amvault", $str );
+
+    } elsif ( $type == $L_ERROR ) {
+        return $self->_handle_error_line( "amvault", $str );
+
+    } elsif ( $type == $L_FATAL ) {
+        return $self->_handle_fatal_line( "amvault", $str );
+
+    } elsif ( $type == $L_DISK ) {
+        return $self->_handle_disk_line( "amvault", $str );
+
+    } else {
+        return $self->_handle_bogus_line( $P_AMFLUSH, $type, $str );
+    }
+}
+
 
 sub _handle_amdump_line
 {
 
 sub _handle_amdump_line
 {
@@ -1011,8 +1112,15 @@ sub _handle_fail_line
     my ($self, $program, $str) = @_;
 
     my @info = Amanda::Util::split_quoted_strings($str);
     my ($self, $program, $str) = @_;
 
     my @info = Amanda::Util::split_quoted_strings($str);
-    my ($hostname, $disk, $date, $level) = @info;
-    my $error = join " ", @info[ 4 .. $#info ];
+    my ($hostname, $disk, $timestamp, $level) = @info;
+    my $error;
+    my $failure_from;
+    if ($program eq 'taper') {
+       $failure_from = $info[4];
+       $error = join " ", @info[ 5 .. $#info ];
+    } else {
+       $error = join " ", @info[ 4 .. $#info ];
+    }
 
     #TODO: verify that this reaches the right try.  Also, DLE or
     #program?
 
     #TODO: verify that this reaches the right try.  Also, DLE or
     #program?
@@ -1023,12 +1131,13 @@ sub _handle_fail_line
         $program eq "driver") {
        $program_d = $dle->{$program} ||= {};
     } else {
         $program eq "driver") {
        $program_d = $dle->{$program} ||= {};
     } else {
-        my $try = $self->_get_try($dle, $program);
+        my $try = $self->_get_try($dle, $program, $timestamp);
         $program_d = $try->{$program} ||= {};
     }
 
     $program_d->{level}  = $level;
     $program_d->{status} = "fail";
         $program_d = $try->{$program} ||= {};
     }
 
     $program_d->{level}  = $level;
     $program_d->{status} = "fail";
+    $program_d->{failure_from}  = $failure_from;
     $program_d->{error}  = $error;
 
     my $errors = $self->get_program_info("program", "errors", []);
     $program_d->{error}  = $error;
 
     my $errors = $self->get_program_info("program", "errors", []);
@@ -1090,8 +1199,6 @@ sub _handle_start_line
     my $timestamp = $info[1];
     $program_p->{start} = $info[1];
 
     my $timestamp = $info[1];
     $program_p->{start} = $info[1];
 
-    # extend to 14 digits
-    $timestamp .= '0' x (14 - length($timestamp));
     if ($self->{'run_timestamp'} ne '00000000000000'
                and $self->{'run_timestamp'} ne $timestamp) {
        warning("not all timestamps in this file are the same; "
     if ($self->{'run_timestamp'} ne '00000000000000'
                and $self->{'run_timestamp'} ne $timestamp) {
        warning("not all timestamps in this file are the same; "
@@ -1124,8 +1231,27 @@ sub _handle_disk_line
 
         push @$dles, [ $hostname, $disk ];
         my $dle = $disklist->{$hostname}{$disk} = {};
 
         push @$dles, [ $hostname, $disk ];
         my $dle = $disklist->{$hostname}{$disk} = {};
-        $dle->{estimate} = undef;
-        $dle->{tries}    = [];
+        $dle->{'estimate'} = undef;
+        $dle->{'dumps'}    = {};
+    }
+    return;
+}
+
+sub _handle_success_line
+{
+    my $self = shift @_;
+    my ($program, $str) = @_;
+
+    my $data     = $self->{data};
+    my $disklist = $data->{disklist};
+    my $hosts    = $self->{cache}{hosts} ||= [];
+    my $dles     = $self->{cache}{dles}  ||= [];
+
+    my @info = Amanda::Util::split_quoted_strings($str);
+    my ($hostname, $disk, $timestamp, $level, $stat1, $stat2) = @info;
+
+    if ($stat1 =~ /skipped/) {
+        $disklist->{$hostname}{$disk}->{$program}->{'status'} = 'skipped';
     }
     return;
 }
     }
     return;
 }
@@ -1171,20 +1297,45 @@ sub _handle_bogus_line
     push @$boguses, [ $prog, $type, $str ];
 }
 
     push @$boguses, [ $prog, $type, $str ];
 }
 
-sub check_missing
+sub check_missing_fail_strange
 {
     my ($self) = @_;
     my @dles = $self->get_dles();
 
     foreach my $dle_entry (@dles) {
 {
     my ($self) = @_;
     my @dles = $self->get_dles();
 
     foreach my $dle_entry (@dles) {
-
-        my $tries = $self->get_dle_info(@$dle_entry, "tries");
-
-        if (!@$tries) {
-            $self->{flags}{results_missing} = 1;
-            $self->{flags}{exit_status} |= STATUS_MISSING;
-            last;
-        }
+        my $alldumps = $self->get_dle_info(@$dle_entry, 'dumps');
+       my $driver = $self->get_dle_info(@$dle_entry, 'driver');
+       my $planner = $self->get_dle_info(@$dle_entry, 'planner');
+
+       if ($planner && $planner->{'status'} eq 'fail') {
+           $self->{flags}{dump_failed} = 1;
+       } elsif ($planner && $planner->{'status'} eq 'skipped') {
+           # We don't want these to be counted as missing below
+       } elsif (!defined $alldumps->{$self->{'run_timestamp'}} and
+                !$driver and
+                !$planner) {
+           $self->{flags}{results_missing} = 1;
+           $self->{flags}{exit_status} |= STATUS_MISSING;
+       } else {
+           #get latest try
+           my $tries = $alldumps->{$self->{'run_timestamp'}};
+           my $try = @$tries[-1];
+
+           if (exists $try->{dumper} && $try->{dumper}->{status} eq 'fail') {
+               $self->{flags}{dump_failed} = 1;
+           } elsif ((defined($try->{'chunker'}) &&
+                $try->{'chunker'}->{status} eq 'success') ||
+               (defined($try->{'taper'}) &&
+                $try->{'taper'}->{status} eq 'done')) {
+               #chunker or taper success, use dumper status
+               if (exists $try->{dumper} && $try->{dumper}->{status} eq 'strange') {
+                   $self->{flags}{dump_strange} = 1;
+               }
+           } else {
+               #chunker or taper failed, the dump is not valid.
+               $self->{flags}{dump_failed} = 1;
+           }
+       }
     }
 }
 
     }
 }
 
@@ -1195,8 +1346,8 @@ sub check_missing
 sub _get_try
 {
     my $self = shift @_;
 sub _get_try
 {
     my $self = shift @_;
-    my ( $dle, $program ) = @_;
-    my $tries = $dle->{tries} ||= [];
+    my ( $dle, $program, $timestamp ) = @_;
+    my $tries = $dle->{'dumps'}{$timestamp} ||= [];
 
     if (
         !@$tries    # no tries
 
     if (
         !@$tries    # no tries