Imported Upstream version 3.1.0
[debian/amanda] / installcheck / Amanda_DB_Catalog.pl
1 # Copyright (c) 2008, 2010 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10 # for more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 #
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
18
19 use Test::More tests => 76;
20 use File::Path;
21 use Data::Dumper;
22 use strict;
23 use warnings;
24
25 use lib "@amperldir@";
26 use Installcheck;
27 use Installcheck::Run;
28 use Amanda::Config qw( :init :getconf config_dir_relative );
29 use Amanda::DB::Catalog;
30 use Amanda::Cmdline;
31 use Amanda::Xfer qw( :constants );
32
33 # send xfer logging somewhere
34 Amanda::Debug::dbopen("installcheck");
35 Installcheck::log_test_output();
36
37 # set up and load a simple config
38 my $testconf = Installcheck::Run->setup();
39 $testconf->write();
40 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
41     or die("Could not load test config");
42
43 # test functions against an empty set of logfiles
44
45 is_deeply([ Amanda::DB::Catalog::get_write_timestamps() ], [],
46     "No write_timestamps in an empty catalog");
47
48 is_deeply(Amanda::DB::Catalog::get_latest_write_timestamp(), undef,
49     "No latest write_timestamp in an empty catalog");
50
51 is_deeply([ Amanda::DB::Catalog::get_parts() ], [],
52     "No parts in an empty catalog");
53
54 # and add some logfiles to query, and a corresponding tapelist, while also gathering
55 # a list of parts and dumps for comparison with the results from Amanda::DB::Catalog.
56 # also add some files to holding disk
57 my $logdir = config_dir_relative(getconf($CNF_LOGDIR));
58 my $tapelist_fn = config_dir_relative(getconf($CNF_TAPELIST));
59 my $holdingdir = "$Installcheck::TMP/holding";
60 my $output;
61 my $write_timestamp;
62 my (%parts, %dumps, $last_dump);
63 my @dumpspecs;
64
65 sub make_holding_file {
66     my ($dump) = @_;
67
68     my $dir = "$holdingdir/$dump->{dump_timestamp}";
69     my $safe_disk = $dump->{'diskname'};
70     $safe_disk =~ tr{/}{_};
71     my $filename = "$dir/$dump->{hostname}.$safe_disk";
72     mkpath($dir);
73
74     # (note that multi-chunk holding files are not used at this point)
75     my $hdr = Amanda::Header->new();
76     $hdr->{'type'} = $Amanda::Header::F_DUMPFILE;
77     $hdr->{'datestamp'} = $dump->{'dump_timestamp'};
78     $hdr->{'dumplevel'} = $dump->{'level'};
79     $hdr->{'name'} = $dump->{'hostname'};
80     $hdr->{'disk'} = $dump->{'diskname'};
81     $hdr->{'program'} = "INSTALLCHECK";
82     $hdr->{'is_partial'} = ($dump->{'status'} ne 'OK');
83
84     open(my $fh, ">", $filename) or die("opening '$filename': $!");
85     print $fh $hdr->to_string(32768,32768);
86
87     # transfer some data to that file
88     my $xfer = Amanda::Xfer->new([
89         Amanda::Xfer::Source::Pattern->new(1024*$dump->{'kb'}, "+-+-+-+-"),
90         Amanda::Xfer::Dest::Fd->new($fh),
91     ]);
92
93     $xfer->start(sub {
94         my ($src, $msg, $xfer) = @_;
95         if ($msg->{type} == $XMSG_ERROR) {
96             die $msg->{elt} . " failed: " . $msg->{message};
97         } elsif ($msg->{'type'} == $XMSG_DONE) {
98             $src->remove();
99             Amanda::MainLoop::quit();
100         }
101     });
102     Amanda::MainLoop::run();
103     close($fh);
104
105     return $filename;
106 }
107
108 open (my $tapelist, ">", $tapelist_fn);
109 while (<DATA>) {
110     # skip comments
111     next if (/^#/ or /^\S*$/);
112
113     # add to tapelist
114     if (/^:tapelist (\d+) (\S+)\s*$/) {
115         print $tapelist "$1 $2 reuse\n";
116         next;
117     }
118     
119     # new logfile
120     if (/^::: (.*)/) {
121         open $output, ">", "$logdir/$1" or die("Could not open $1 for writing: $!");
122         next;
123     }
124
125     # write_timestamp
126     if (/^:timestamp (\d+)/) {
127         $write_timestamp = $1;
128         next;
129     }
130
131     # new dump
132     if (/^:dump (\S+) (\S+) (\S+) (\S+) (\d+) (\S+) (\S+) (\d+) (\S+) (\d+) (\d+)/) {
133         $last_dump = $dumps{$1} = {
134             'dump_timestamp' => $2,     'hostname' => $3,           'diskname' => $4,
135             'level' => $5+0,            'status' => $6,             'message' => $7,
136             'nparts' => $8,             'sec' => $9+0.0,            'kb' => $10,
137             'orig_kb' => $11,
138             'write_timestamp' => $write_timestamp,
139         };
140         $last_dump->{'message'} = ''
141             if $last_dump->{'message'} eq '""';
142         next;
143     } elsif (/^:dump (\S+) (\S+) (\S+) (\S+) (\d+) (\S+) (\S+) (\d+) (\S+) (\d+)/) {
144         $last_dump = $dumps{$1} = {
145             'dump_timestamp' => $2,     'hostname' => $3,           'diskname' => $4,
146             'level' => $5+0,            'status' => $6,             'message' => $7,
147             'nparts' => $8,             'sec' => $9+0.0,            'kb' => $10,
148             'orig_kb' => 0,
149             'write_timestamp' => $write_timestamp,
150         };
151         $last_dump->{'message'} = ''
152             if $last_dump->{'message'} eq '""';
153         next;
154     }
155
156     # new holding-disk file
157     if (/^:holding (\S+) (\S+) (\S+) (\S+) (\d+) (\S+) (\d+)/) {
158         $last_dump = $dumps{$1} = {
159             'dump_timestamp' => $2,     'hostname' => $3,           'diskname' => $4,
160             'level' => $5+0,            'status' => $6,             'kb' => $7,
161             'orig_kb' => 0,
162             'write_timestamp' => '00000000000000',
163             'message' => '',
164             'nparts' => 1,
165             'sec' => 0.0,
166         };
167         $parts{$1} = {
168             holding_file => make_holding_file($last_dump),
169             dump => $last_dump,
170             status => $last_dump->{'status'},
171             sec => 0.0,
172             kb => $last_dump->{'kb'},
173             orig_kb => 0,
174             partnum => 1,
175         };
176         $last_dump->{'parts'} = [ undef, $parts{$1}, ];
177         next;
178     }
179
180     # new part
181     if (/^:part (\S+) (\S+) (\S+) (\d+) (\d+) (\S+) (\S+) (\d+) (\d+)/) {
182         $parts{$1} = {
183             'dump' => $dumps{$2},       'label' => $3,              'filenum' => $4,
184             'partnum' => $5,            'status' => $6,             'sec' => $7+0.0,
185             'kb' => $8,                 'orig_kb' => $9 
186         };
187         $last_dump->{'parts'}->[$parts{$1}->{'partnum'}] = $parts{$1};
188         next;
189     } elsif (/^:part (\S+) (\S+) (\S+) (\d+) (\d+) (\S+) (\S+) (\d+)/) {
190         $parts{$1} = {
191             'dump' => $dumps{$2},       'label' => $3,              'filenum' => $4,
192             'partnum' => $5,            'status' => $6,             'sec' => $7+0.0,
193             'kb' => $8,                 'orig_kb' => 0
194         };
195         $last_dump->{'parts'}->[$parts{$1}->{'partnum'}] = $parts{$1};
196         next;
197     }
198
199     die("syntax error") if (/^:/);
200
201     print $output $_;
202 }
203 close($output);
204 close($tapelist);
205 Amanda::DB::Catalog::_clear_cache();
206
207 ##
208 # Test the timestamps
209
210 is_deeply([ Amanda::DB::Catalog::get_write_timestamps(), ],
211     [ '20080111000000', '20080222222222', '20080313133333', '20080414144444', '20080515155555', '20080616166666' ],
212     "get_write_timestamps returns all logfile datestamps in proper order, with zero-padding");
213
214 is(Amanda::DB::Catalog::get_latest_write_timestamp(), '20080616166666',
215     "get_latest_write_timestamp correctly returns the latest write timestamp");
216
217 ##
218 # test get_parts and sort_parts
219
220 sub partstr {
221     my ($part) = @_;
222     if (exists $part->{'holding_file'}) {
223         return "$part->{holding_file}: " .
224                "$part->{dump}->{hostname} $part->{dump}->{diskname}";
225    } else {
226         return "$part->{label}:$part->{filenum}: " .
227                "$part->{dump}->{hostname} $part->{dump}->{diskname} $part->{dump}->{orig_kb}";
228    }
229 }
230
231 # filter out recursive links from part->dump->parts, without changing
232 # the original objects.  Note that this removes the object-identity of
233 # dumps.
234 sub filter_parts {
235     my ($parts) = @_;
236
237     my @rv;
238     for my $p (@$parts) {
239         $p = do { my %t = %$p; \%t; }; # copy hash
240         $p->{'dump'} = do { my %t = %{$p->{'dump'}}; \%t; };
241         $p->{'dump'}->{'parts'} = undef;
242         push @rv, $p;
243     }
244
245     return \@rv;
246 }
247
248 sub got_parts {
249     my ($got, $exp, $msg, %params) = @_;
250
251     # give a warning if we accidentally select zero parts
252     if (@$exp == 0) {
253         diag("warning: zero parts expected: $msg")
254             unless $params{'zero_parts_expected'};
255     }
256
257     # filter recursive references to avoid confusing old is_deeply instances
258     if (!is_deeply(filter_parts($got), filter_parts($exp), $msg)) {
259         diag("got parts:");
260         for (@$got) {
261             diag("  " . partstr($_));
262         }
263         diag("expected parts:");
264         for (@$exp) {
265             diag("  " . partstr($_));
266         }
267         return '';
268     }
269     return 1;
270 }
271
272 # get parts filtered by a regexp on the key
273 sub parts_named($) {
274     my ($expr) = @_; 
275     my @selected_keys = grep { $_ =~ $expr } keys %parts;
276     return map { $parts{$_} } @selected_keys;
277 }
278
279 # get parts filtered by an expression on the dumpfile itself
280 sub parts_matching(&) {
281     my ($block) = @_; 
282     return grep { &$block } values %parts;
283 }
284
285 # put @_ in a canonical order
286 sub sortparts {
287     map {
288         # convert bigints to strings and on to integers so is_deeply doesn't get confused
289         $_->{'dump'}->{'level'} = "$_->{dump}->{level}" + 0;
290         $_->{'dump'}->{'kb'} = "$_->{dump}->{kb}" + 0;
291         $_->{'dump'}->{'orig_kb'} = "$_->{dump}->{orig_kb}" + 0;
292         if (!defined $_->{filenum}) {
293             $_->{'filenum'} = 0;
294         } else {
295             $_->{'filenum'} = "$_->{filenum}" + 0;
296         }
297         $_->{'kb'} = "$_->{kb}" + 0;
298         $_->{'orig_kb'} = "$_->{orig_kb}" + 0;
299         $_->{'partnum'} = "$_->{partnum}" + 0;
300         $_;
301     } sort {
302         if (exists $a->{'holding_file'} and exists $b->{'holding_file'}) {
303             return $a->{'holding_file'} cmp $b->{'holding_file'};
304         } elsif (not exists $a->{'holding_file'} and not exists $b->{'holding_file'}) {
305             return ($a->{'label'} cmp $b->{'label'})
306                 || ($a->{'filenum'} <=> $b->{'filenum'});
307         } else {
308             return (exists $a->{'holding_file'})? 1 : -1;
309         }
310     }
311     @_;
312 }
313
314 sub dumpstr {
315     my ($dump) = @_;
316     return "$dump->{hostname} $dump->{diskname} " .
317            "$dump->{dump_timestamp} $dump->{level}";
318 }
319
320 # filter out recursive links from dump->parts->dump, without changing
321 # the original objects.
322 sub filter_dumps {
323     my ($dumps) = @_;
324
325     my @rv;
326     for my $d (@$dumps) {
327         $d = do { my %t = %$d; \%t; }; # copy hash
328         my @dparts = map {
329                 return undef unless defined $_;
330                 my $p = do { my %t = %$_; \%t }; # copy part
331                 $p->{'dump'} = undef;
332                 $p;
333             } @{$d->{'parts'}};
334         $d->{'parts'} = [ @dparts ];
335         push @rv, $d;
336     }
337
338     return \@rv;
339 }
340
341 sub got_dumps {
342     my ($got, $exp, $msg, %params) = @_;
343
344     # give a warning if we accidentally select zero dumps
345     if (@$exp == 0) {
346         diag("warning: zero dumps expected: $msg")
347             unless $params{'zero_dumps_expected'};
348     }
349
350     # filter recursive references to avoid confusing old is_deeply instances
351     if (!is_deeply(filter_dumps($got), filter_dumps($exp), $msg)) {
352         diag("got dumps:");
353         for (@$got) {
354             diag("  " . dumpstr($_));
355         }
356         diag("expected dumps:");
357         for (@$exp) {
358             diag("  " . dumpstr($_));
359         }
360         return '';
361     }
362     return 1;
363 }
364
365 # get dumps filtered by a regexp on the key
366 sub dumps_named($) {
367     my ($expr) = @_; 
368     my @selected_keys = grep { $_ =~ $expr } keys %dumps;
369     return map { $dumps{$_} } @selected_keys;
370 }
371
372 # get dumps filtered by an expression on the dumpfile itself
373 sub dumps_matching(&) {
374     my ($block) = @_; 
375     return grep { &$block } values %dumps;
376 }
377
378 # put @_ in a canonical order
379 sub sortdumps {
380     map {
381         # convert bigints to strings and on to integers so is_deeply doesn't get confused
382         $_->{'level'} = "$_->{level}" + 0;
383         $_->{'kb'} = "$_->{kb}" + 0;
384         $_->{'orig_kb'} = "$_->{orig_kb}" + 0;
385         $_->{'nparts'} = "$_->{nparts}" + 0;
386         $_;
387     } sort {
388         $a->{'write_timestamp'} cmp $b->{'write_timestamp'}
389             or $a->{'hostname'} cmp $b->{'hostname'}
390             or $a->{'diskname'} cmp $b->{'diskname'}
391             or $a->{'level'} <=> $b->{'level'}
392     }
393     @_;
394 }
395
396 ### test part selecting
397
398 got_parts([ sortparts Amanda::DB::Catalog::get_parts() ],
399     [ sortparts parts_named qr/.*/ ],
400     "get_parts returns all parts when given no parameters");
401 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111000000') ],
402     [ sortparts parts_named qr/somebox_lib_20080111/ ],
403     "get_parts parameter write_timestamp");
404 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111') ],
405     [ sortparts parts_named qr/somebox_lib_20080111/ ],
406     "get_parts accepts a short write_timestamp and zero-pads it");
407 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamps => ['20080111000000','20080222222222']) ],
408     [ sortparts parts_named qr/(20080111|20080222222222_p\d*)$/ ],
409     "get_parts parameter write_timestamps");
410 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111', holding => 1) ],
411     [ ],
412     "get_parts parameter write_timestamp + holding => 1 returns nothing",
413     zero_parts_expected => 1);
414
415 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp => '20080111000000') ],
416     [ sortparts parts_named qr/somebox_lib_20080111/ ],
417     "get_parts parameter dump_timestamp");
418 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp => '20080111') ],
419     [ sortparts parts_named qr/somebox_lib_20080111/ ],
420     "get_parts accepts a short dump_timestamp and zero-pads it");
421 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamps => ['20080111000000','20080222222222']) ],
422     [ sortparts parts_named qr/(20080111|20080222222222_p\d*)$/ ],
423     "get_parts parameter dump_timestamps");
424 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp_match => '200801-2') ],
425     [ sortparts parts_named qr/20080[12]/ ],
426     "get_parts parameter dump_timestamp_match");
427
428 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => 'otherbox') ],
429     [ sortparts parts_named qr/^otherbox_/ ],
430     "get_parts parameter hostname");
431 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => ['otherbox','somebox']) ],
432     [ sortparts parts_named qr/^(otherbox_|somebox_)/ ],
433     "get_parts parameter hostnames");
434 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname_match => '*box') ],
435     [ sortparts parts_named qr/box/ ],
436     "get_parts parameter hostname_match");
437
438 got_parts([ sortparts Amanda::DB::Catalog::get_parts(diskname => '/lib') ],
439     [ sortparts parts_named qr/_lib_/ ],
440     "get_parts parameter diskname");
441 got_parts([ sortparts Amanda::DB::Catalog::get_parts(disknames => ['/lib','/usr/bin']) ],
442     [ sortparts parts_named qr/(_lib_|_usr_bin_)/ ],
443     "get_parts parameter disknames");
444 got_parts([ sortparts Amanda::DB::Catalog::get_parts(diskname_match => '/usr') ],
445     [ sortparts parts_named qr/_usr_/ ],
446     "get_parts parameter diskname_match");
447
448 got_parts([ sortparts Amanda::DB::Catalog::get_parts(label => 'Conf-001') ],
449     [ sortparts parts_matching { defined $_->{'label'} and $_->{'label'} eq 'Conf-001' } ],
450     "get_parts parameter label");
451 got_parts([ sortparts Amanda::DB::Catalog::get_parts(labels => ['Conf-002','Conf-003']) ],
452     [ sortparts parts_matching { defined $_->{'label'} and ($_->{'label'} eq 'Conf-002' or $_->{'label'} eq 'Conf-003') } ],
453     "get_parts parameter labels");
454
455 got_parts([ sortparts Amanda::DB::Catalog::get_parts(level => 0) ],
456     [ sortparts parts_matching { $_->{'dump'}->{'level'} == 0 } ],
457     "get_parts parameter level");
458 got_parts([ sortparts Amanda::DB::Catalog::get_parts(levels => [ 1 ]) ],
459     [ sortparts parts_matching { $_->{'dump'}->{'level'} == 1 } ],
460     "get_parts parameter levels");
461
462 got_parts([ sortparts Amanda::DB::Catalog::get_parts(status => "OK") ],
463     [ sortparts parts_matching { $_->{'status'} eq "OK" } ],
464     "get_parts parameter status = OK");
465
466 got_parts([ sortparts Amanda::DB::Catalog::get_parts(status => "PARTIAL") ],
467     [ sortparts parts_matching { $_->{'status'} eq "PARTIAL" } ],
468     "get_parts parameter status = PARTIAL");
469
470 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox") ],
471     [ sortparts parts_named qr/^oldbox_/ ],
472     "get_parts finds a holding-disk dump");
473
474 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox", holding => 0) ],
475     [ ],
476     "get_parts ignores a holding-disk dump if holding is false",
477     zero_parts_expected => 1);
478 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox", holding => 1) ],
479     [ sortparts parts_named qr/^oldbox_/ ],
480     "get_parts supplies a holding-disk dump if holding is true");
481 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ]) ],
482     [ sortparts (parts_named qr/^oldbox_.*_holding/, parts_named qr/^somebox_/) ],
483     "get_parts returns both holding and on-media dumps");
484 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ],
485                                                      holding => 1) ],
486     [ sortparts parts_named qr/^oldbox_.*_holding/ ],
487     "get_parts ignores an on-media dump if holding is true");
488
489 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ],
490                                                      holding => 0) ],
491     [ sortparts parts_named qr/^somebox_/ ],
492     "get_parts ignores an holding dump if holding is false");
493
494 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
495 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
496     [ sortparts parts_named qr/_lib_/ ],
497     "get_parts parameter dumpspecs with one dumpspec");
498
499 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib", "somebox"], 0);
500 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
501     [ sortparts parts_matching { $_->{'dump'}->{'diskname'} eq '/lib'
502                               or $_->{'dump'}->{'hostname'} eq 'somebox' } ],
503     "get_parts parameter dumpspecs with two dumpspecs");
504
505 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
506 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
507     [ sortparts parts_matching { $_->{'dump'}->{'hostname'} eq 'otherbox'
508                               or $_->{'dump'}->{'hostname'} eq 'somebox' } ],
509     "get_parts parameter dumpspecs with two non-overlapping dumpspecs");
510
511 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
512 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ], holding => 1), ],
513     [ sortparts parts_matching { $_->{'dump'}->{'hostname'} eq 'otherbox'
514                              and exists $_->{'holding_file'} } ],
515     "get_parts parameter dumpspecs with two non-overlapping dumpspecs, but holding files only");
516
517 ## more complex, multi-parameter queries
518
519 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => 'somebox',
520                                                      diskname_match => '/lib') ],
521     [ sortparts parts_named qr/^somebox_lib_/ ],
522     "get_parts parameters hostname and diskname_match");
523
524 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080313133333',
525                                                      dump_timestamp => '20080311131133') ],
526     [ sortparts parts_matching { $_->{'dump'}->{'dump_timestamp'} eq '20080311131133' 
527                     and $_->{'dump'}->{'write_timestamp'} eq '20080313133333' } ],
528     "get_parts parameters write_timestamp and dump_timestamp");
529
530 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080414144444',
531                                                      status => 'OK') ],
532     [ ], # there were no OK dumps on that date
533     "get_parts parameters write_timestamp status",
534     zero_parts_expected => 1);
535
536 ## test part sorting
537
538 got_parts([ Amanda::DB::Catalog::sort_parts(['write_timestamp'],
539                 @parts{'somebox_lib_20080222222222_p1','somebox_lib_20080111'}) ],
540               [ @parts{'somebox_lib_20080111','somebox_lib_20080222222222_p1'} ],
541     "sort by write_timestamps");
542 got_parts([ Amanda::DB::Catalog::sort_parts(['-write_timestamp'],
543                 @parts{'somebox_lib_20080111','somebox_lib_20080222222222_p1'}) ],
544               [ @parts{'somebox_lib_20080222222222_p1','somebox_lib_20080111'} ],
545     "sort by write_timestamps, reverse");
546
547 got_parts([ Amanda::DB::Catalog::sort_parts(['hostname', '-diskname', 'write_timestamp'],
548                 @parts{
549                     'somebox_lib_20080222222222_p1',
550                     'somebox_usr_bin_20080313133333',
551                     'somebox_lib_20080313133333_p4',
552                     'otherbox_lib_20080313133333',
553                     'somebox_lib_20080111',
554                     }) ],
555               [ @parts{
556                     'otherbox_lib_20080313133333',
557                     'somebox_usr_bin_20080313133333',
558                     'somebox_lib_20080111',
559                     'somebox_lib_20080222222222_p1',
560                     'somebox_lib_20080313133333_p4',
561                     } ],
562     "multi-key sort");
563
564 got_parts([ Amanda::DB::Catalog::sort_parts(['filenum'],
565                 @parts{
566                     'somebox_lib_20080313133333_p9',
567                     'somebox_lib_20080313133333_p10',
568                     'somebox_lib_20080313133333_p1',
569                     }) ],
570               [ @parts{
571                     'somebox_lib_20080313133333_p1',
572                     'somebox_lib_20080313133333_p9',
573                     'somebox_lib_20080313133333_p10',
574                     } ],
575                 "filenum is sorted numerically, not lexically");
576
577 got_parts([ Amanda::DB::Catalog::sort_parts(['-partnum'],
578                 @parts{
579                     'somebox_lib_20080313133333_p9',
580                     'somebox_lib_20080313133333_p10',
581                     'somebox_lib_20080313133333_p1',
582                     }) ],
583               [ @parts{
584                     'somebox_lib_20080313133333_p10',
585                     'somebox_lib_20080313133333_p9',
586                     'somebox_lib_20080313133333_p1',
587                     } ],
588                 "partnum is sorted numerically (and in reverse!), not lexically");
589
590 got_parts([ Amanda::DB::Catalog::sort_parts(['nparts'],
591                 @parts{
592                     'somebox_lib_20080313133333_p9', # nparts=10
593                     'somebox_lib_20080222222222_p2', # nparts=2
594                     }) ],
595               [ @parts{
596                     'somebox_lib_20080222222222_p2', # nparts=2
597                     'somebox_lib_20080313133333_p9', # nparts=10
598                     } ],
599                 "nparts is sorted numerically, not lexically");
600
601 got_parts([ Amanda::DB::Catalog::sort_parts(['label'],
602                 @parts{
603                     'somebox_lib_20080313133333_p9', # Conf-003
604                     'somebox_lib_20080222222222_p2', # Conf-002
605                     }) ],
606               [ @parts{
607                     'somebox_lib_20080222222222_p2', # Conf-002
608                     'somebox_lib_20080313133333_p9', # Conf-003
609                     } ],
610                 "labels sort correctly");
611
612 ### test dump selecting
613
614 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps() ],
615     [ sortdumps dumps_named qr/.*/ ],
616     "get_dumps returns all dumps when given no parameters");
617
618 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111000000') ],
619     [ sortdumps dumps_named qr/somebox_lib_20080111/ ],
620     "get_dumps parameter write_timestamp");
621
622 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111') ],
623     [ sortdumps dumps_named qr/somebox_lib_20080111/ ],
624     "get_dumps accepts a short write_timestamp and zero-pads it");
625
626 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamps => ['20080111000000','20080222222222']) ],
627     [ sortdumps dumps_named qr/(20080111|20080222222222)$/ ],
628     "get_dumps parameter write_timestamps");
629
630 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'otherbox') ],
631     [ sortdumps dumps_named qr/^otherbox/ ],
632     "get_dumps parameter hostname");
633
634 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'oldbox') ],
635     [ sortdumps dumps_named qr/^oldbox_.*_holding/ ],
636     "get_dumps parameter hostname, holding");
637
638 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostnames => ['notthere', 'otherbox']) ],
639     [ sortdumps dumps_named qr/^otherbox/ ],
640     "get_dumps parameter hostnames");
641
642 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname_match => 'other*') ],
643     [ sortdumps dumps_named qr/^otherbox/ ],
644     "get_dumps parameter hostname_match");
645
646 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(diskname => '/lib') ],
647     [ sortdumps dumps_named qr/^[^_]*_lib_/ ],
648     "get_dumps parameter diskname");
649
650 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(disknames => ['/lib', '/usr/bin']) ],
651     [ sortdumps dumps_named qr/^[^_]*_(usr_bin|lib)_/ ],
652     "get_dumps parameter disknames");
653
654 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(diskname_match => 'bin') ],
655     [ sortdumps dumps_named qr/.*_bin_/ ],
656     "get_dumps parameter diskname_match");
657
658 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp => '20080414144444') ],
659     [ sortdumps dumps_matching { $_->{'dump_timestamp'} eq '20080414144444' } ],
660     "get_dumps parameter dump_timestamp");
661
662 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(
663                         dump_timestamps => ['20080414144444', '20080311131133']) ],
664     [ sortdumps dumps_matching { $_->{'dump_timestamp'} eq '20080414144444' 
665                               or $_->{'dump_timestamp'} eq '20080311131133' } ],
666     "get_dumps parameter dump_timestamps");
667
668 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp_match => '200804-7') ],
669     [ sortdumps dumps_matching { $_->{'dump_timestamp'} =~ /^20080[4567]/ } ],
670     "get_dumps parameter dump_timestamp_match");
671
672 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(level => 0) ],
673     [ sortdumps dumps_matching { $_->{'level'} == 0 } ],
674     "get_dumps parameter level");
675
676 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(levels => [ 1 ]) ],
677     [ sortdumps dumps_matching { $_->{'level'} == 1 } ],
678     "get_dumps parameter levels");
679
680 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "OK") ],
681     [ sortdumps dumps_matching { $_->{'status'} eq "OK" } ],
682     "get_dumps parameter status = OK");
683
684 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "PARTIAL") ],
685     [ sortdumps dumps_matching { $_->{'status'} eq "PARTIAL" } ],
686     "get_dumps parameter status = PARTIAL");
687
688 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "FAIL") ],
689     [ sortdumps dumps_matching { $_->{'status'} eq "FAIL" } ],
690     "get_dumps parameter status = FAIL");
691
692 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
693 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
694     [ sortdumps dumps_named qr/_lib_/ ],
695     "get_dumps parameter dumpspecs with one dumpspec");
696
697 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
698 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ], holding => 1) ],
699     [ sortdumps dumps_named qr/_lib_.*_holding/ ],
700     "get_dumps parameter dumpspecs with one dumpspec");
701
702 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib", "somebox"], 0);
703 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
704     [ sortdumps dumps_matching { $_->{'diskname'} eq '/lib'
705                               or $_->{'hostname'} eq 'somebox' } ],
706     "get_dumps parameter dumpspecs with two dumpspecs");
707
708 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
709 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
710     [ sortdumps dumps_matching { $_->{'hostname'} eq 'otherbox'
711                               or $_->{'hostname'} eq 'somebox' } ],
712     "get_dumps parameter dumpspecs with two non-overlapping dumpspecs");
713
714 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["does-not-exist"], 0);
715 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
716     [ ],
717     "get_dumps parameter dumpspecs with a dumpspec that matches nothing",
718     zero_dumps_expected => 1);
719
720 ## test dump sorting
721
722 got_dumps([ Amanda::DB::Catalog::sort_dumps(['write_timestamp'],
723                 @dumps{'somebox_lib_20080222222222','somebox_lib_20080111'}) ],
724               [ @dumps{'somebox_lib_20080111','somebox_lib_20080222222222'} ],
725     "sort dumps by write_timestamps");
726 got_dumps([ Amanda::DB::Catalog::sort_dumps(['-write_timestamp'],
727                 @dumps{'somebox_lib_20080111','somebox_lib_20080222222222'}) ],
728               [ @dumps{'somebox_lib_20080222222222','somebox_lib_20080111'} ],
729     "sort dumps by write_timestamps, reverse");
730
731 got_dumps([ Amanda::DB::Catalog::sort_dumps(['hostname', '-diskname', 'write_timestamp'],
732                 @dumps{
733                     'somebox_lib_20080222222222',
734                     'somebox_usr_bin_20080313133333',
735                     'somebox_lib_20080313133333',
736                     'otherbox_lib_20080313133333',
737                     'somebox_lib_20080111',
738                     }) ],
739               [ @dumps{
740                     'otherbox_lib_20080313133333',
741                     'somebox_usr_bin_20080313133333',
742                     'somebox_lib_20080111',
743                     'somebox_lib_20080222222222',
744                     'somebox_lib_20080313133333',
745                     } ],
746                 "multi-key dump sort");
747
748 got_dumps([ Amanda::DB::Catalog::sort_dumps(['nparts'],
749                 @dumps{
750                     'somebox_lib_20080313133333', # nparts=10
751                     'somebox_lib_20080222222222', # nparts=2
752                     }) ],
753               [ @dumps{
754                     'somebox_lib_20080222222222', # nparts=2
755                     'somebox_lib_20080313133333', # nparts=10
756                     } ],
757                 "dumps' nparts is sorted numerically, not lexically");
758
759 got_dumps([ Amanda::DB::Catalog::sort_dumps(['-level'],
760                 @dumps{
761                     'somebox_lib_20080313133333', # level=0
762                     'somebox_usr_bin_20080313133333', # level=1
763                     }) ],
764               [ @dumps{
765                     'somebox_usr_bin_20080313133333', # level=1
766                     'somebox_lib_20080313133333', # level=0
767                     } ],
768                 "sort dumps by level, reversed");
769
770 got_dumps([ Amanda::DB::Catalog::sort_dumps(['dump_timestamp'],
771                 @dumps{
772                     'somebox_lib_20080313133333', # dts=20080313133333
773                     'otherbox_usr_bin_20080313133333', # dts=20080311131133
774                     }) ],
775               [ @dumps{
776                     'otherbox_usr_bin_20080313133333', # dts=20080311131133
777                     'somebox_lib_20080313133333', # dts=20080313133333
778                     } ],
779                 "sort dumps by write_timestamp");
780
781 __DATA__
782 # a short-datestamp logfile with only a single, single-part file in it
783 ::: log.20080111.0
784 :tapelist 20080111 Conf-001
785 :timestamp 20080111000000
786 DISK planner somebox /lib
787 START planner date 20080111
788 START driver date 20080111
789 STATS driver hostname somebox
790 STATS driver startup time 0.051
791 FINISH planner date 20080111 time 82.721
792 START taper datestamp 20080111 label Conf-001 tape 1
793 SUCCESS dumper somebox /lib 20080111 0 [sec 0.209 kb 1970 kps 9382.2 orig-kb 1970]
794 SUCCESS chunker somebox /lib 20080111 0 [sec 0.305 kb 420 kps 1478.7]
795 STATS driver estimate somebox /lib 20080111 0 [sec 1 nkb 2002 ckb 480 kps 385]
796 :dump somebox_lib_20080111 20080111000000 somebox /lib 0 OK "" 1 4.813543 419
797 :part somebox_lib_20080111 somebox_lib_20080111 Conf-001 1 1 OK 4.813543 419
798 PART taper Conf-001 1 somebox /lib 20080111 1/1 0 [sec 4.813543 kb 419 kps 87.133307]
799 DONE taper somebox /lib 20080111 1 0 [sec 4.813543 kb 419 kps 87.133307]
800 FINISH driver date 20080111 time 2167.581
801
802 # a long-datestamp logfile, also fairly simple
803 ::: log.20080222222222.0
804 :tapelist 20080222222222 Conf-002
805 :timestamp 20080222222222
806 DISK planner somebox /lib
807 START planner date 20080222222222
808 START driver date 20080222222222
809 STATS driver hostname somebox
810 STATS driver startup time 0.051
811 FINISH planner date 20080222222222 time 0.102
812 SUCCESS dumper somebox /lib 20080222222222 0 [sec 0.012 kb 100 kps 8115.6 orig-kb 100]
813 SUCCESS chunker somebox /lib 20080222222222 0 [sec 5.075 kb 100 kps 26.0]
814 STATS driver estimate somebox /lib 20080222222222 0 [sec 0 nkb 132 ckb 160 kps 1024]
815 START taper datestamp 20080222222222 label Conf-002 tape 1
816 :dump somebox_lib_20080222222222 20080222222222 somebox /lib 0 OK "" 2 0.001161 172
817 :part somebox_lib_20080222222222_p1 somebox_lib_20080222222222 Conf-002 1 1 OK 0.000733 100
818 PART taper Conf-002 1 somebox /lib 20080222222222 1/2 0 [sec 0.000733 kb 100 kps 136425.648022]
819 :part somebox_lib_20080222222222_p2 somebox_lib_20080222222222 Conf-002 2 2 OK 0.000428 72
820 PART taper Conf-002 2 somebox /lib 20080222222222 2/2 0 [sec 0.000428 kb 72 kps 136425.648022]
821 DONE taper somebox /lib 20080222222222 2 0 [sec 0.001161 kb 172 kps 136425.648022]
822 FINISH driver date 20080222222222 time 6.206
823
824 # a logfile with several dumps in it, one of which comes in many parts, and one of which is
825 # from a previous run
826 ::: log.20080313133333.0
827 :tapelist 20080313133333 Conf-003
828 :timestamp 20080313133333
829 DISK planner somebox /usr/bin
830 DISK planner somebox /lib
831 DISK planner otherbox /lib
832 DISK planner otherbox /usr/bin
833 START planner date 20080313133333
834 START driver date 20080313133333
835 STATS driver hostname somebox
836 STATS driver startup time 0.059
837 INFO planner Full dump of somebox:/lib promoted from 2 days ahead.
838 FINISH planner date 20080313133333 time 0.286
839 SUCCESS dumper somebox /usr/bin 20080313133333 1 [sec 0.001 kb 20 kps 10352.0 orig-kb 20]
840 SUCCESS chunker somebox /usr/bin 20080313133333 1 [sec 1.023 kb 20 kps 50.8]
841 STATS driver estimate somebox /usr/bin 20080313133333 1 [sec 0 nkb 52 ckb 64 kps 1024]
842 START taper datestamp 20080313133333 label Conf-003 tape 1
843 :dump somebox_usr_bin_20080313133333 20080313133333 somebox /usr/bin 1 OK "" 1 0.000370 20
844 :part somebox_usr_bin_20080313133333 somebox_usr_bin_20080313133333 Conf-003 1 1 OK 0.000370 20
845 PART taper Conf-003 1 somebox /usr/bin 20080313133333 1/1 1 [sec 0.000370 kb 20 kps 54054.054054]
846 DONE taper somebox /usr/bin 20080313133333 1 1 [sec 0.000370 kb 20 kps 54054.054054]
847 # a multi-part dump
848 SUCCESS dumper somebox /lib 20080313133333 0 [sec 0.189 kb 3156 kps 50253.1 orig-kb 3156]
849 SUCCESS chunker somebox /lib 20080313133333 0 [sec 5.250 kb 3156 kps 1815.5]
850 STATS driver estimate somebox /lib 20080313133333 0 [sec 1 nkb 3156 ckb 3156 kps 9500]
851 :dump somebox_lib_20080313133333 20080313133333 somebox /lib 0 OK "" 10 0.051436 3156
852 :part somebox_lib_20080313133333_p1 somebox_lib_20080313133333 Conf-003 2 1 OK 0.005621 1024
853 PART taper Conf-003 2 somebox /lib 20080313133333 1/10 0 [sec 0.005621 kb 1024 kps 182173.990393]
854 :part somebox_lib_20080313133333_p2 somebox_lib_20080313133333 Conf-003 3 2 OK 0.006527 1024
855 PART taper Conf-003 3 somebox /lib 20080313133333 2/10 0 [sec 0.006527 kb 1024 kps 156886.777999]
856 :part somebox_lib_20080313133333_p3 somebox_lib_20080313133333 Conf-003 4 3 OK 0.005854 1024
857 PART taper Conf-003 4 somebox /lib 20080313133333 3/10 0 [sec 0.005854 kb 1024 kps 174923.129484]
858 :part somebox_lib_20080313133333_p4 somebox_lib_20080313133333 Conf-003 5 4 OK 0.007344 1024
859 PART taper Conf-003 5 somebox /lib 20080313133333 4/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
860 :part somebox_lib_20080313133333_p5 somebox_lib_20080313133333 Conf-003 6 5 OK 0.007344 1024
861 PART taper Conf-003 6 somebox /lib 20080313133333 5/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
862 :part somebox_lib_20080313133333_p6 somebox_lib_20080313133333 Conf-003 7 6 OK 0.007344 1024
863 PART taper Conf-003 7 somebox /lib 20080313133333 6/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
864 :part somebox_lib_20080313133333_p7 somebox_lib_20080313133333 Conf-003 8 7 OK 0.007344 1024
865 PART taper Conf-003 8 somebox /lib 20080313133333 7/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
866 :part somebox_lib_20080313133333_p8 somebox_lib_20080313133333 Conf-003 9 8 OK 0.007344 1024
867 PART taper Conf-003 9 somebox /lib 20080313133333 8/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
868 :part somebox_lib_20080313133333_p9 somebox_lib_20080313133333 Conf-003 10 9 OK 0.007344 1024
869 PART taper Conf-003 10 somebox /lib 20080313133333 9/10 0 [sec 0.007344 kb 1024 kps 147993.746743]
870 :part somebox_lib_20080313133333_p10 somebox_lib_20080313133333 Conf-003 11 10 OK 0.001919 284
871 PART taper Conf-003 11 somebox /lib 20080313133333 10/10 0 [sec 0.001919 kb 284 kps 147993.746743]
872 DONE taper somebox /lib 20080313133333 10 0 [sec 0.051436 kb 3156 kps 184695.543977]
873 SUCCESS dumper otherbox /lib 20080313133333 0 [sec 0.001 kb 190 kps 10352.0 orig-kb 20]
874 SUCCESS chunker otherbox /lib 20080313133333 0 [sec 1.023 kb 190 kps 50.8]
875 STATS driver estimate otherbox /lib 20080313133333 0 [sec 0 nkb 190 ckb 190 kps 1024]
876 # this dump is from a previous run, with an older dump_timestamp
877 :dump otherbox_usr_bin_20080313133333 20080311131133 otherbox /usr/bin 0 OK "" 1 0.002733 240
878 :part otherbox_usr_bin_20080313133333 otherbox_usr_bin_20080313133333 Conf-003 12 1 OK 0.002733 240
879 PART taper Conf-003 12 otherbox /usr/bin 20080311131133 1/1 0 [sec 0.002733 kb 240 kps 136425.648022]
880 DONE taper otherbox /usr/bin 20080311131133 1 0 [sec 0.002733 kb 240 kps 136425.648022]
881 :dump otherbox_lib_20080313133333 20080313133333 otherbox /lib 0 OK "" 1 0.001733 190
882 :part otherbox_lib_20080313133333 otherbox_lib_20080313133333 Conf-003 13 1 OK 0.001733 190
883 PART taper Conf-003 13 otherbox /lib 20080313133333 1/1 0 [sec 0.001733 kb 190 kps 136425.648022]
884 DONE taper otherbox /lib 20080313133333 1 0 [sec 0.001733 kb 190 kps 136425.648022]
885 FINISH driver date 20080313133333 time 24.777
886
887 # A logfile with some partial parts (PARTPARTIAL) in it
888 ::: log.20080414144444.0
889 :tapelist 20080414144444 Conf-004
890 :tapelist 20080414144444 Conf-005
891 :timestamp 20080414144444
892 DISK planner otherbox /lib
893 START planner date 20080414144444
894 START driver date 20080414144444
895 STATS driver hostname otherbox
896 STATS driver startup time 0.075
897 INFO taper Will write new label `Conf-004' to new (previously non-amanda) tape
898 FINISH planner date 20080414144444 time 2.139
899 SUCCESS dumper otherbox /lib 20080414144444 1 [sec 0.003 kb 60 kps 16304.3 orig-kb 60]
900 SUCCESS chunker otherbox /lib 20080414144444 1 [sec 1.038 kb 60 kps 88.5]
901 STATS driver estimate otherbox /lib 20080414144444 1 [sec 0 nkb 92 ckb 96 kps 1024]
902 START taper datestamp 20080414144444 label Conf-004 tape 1
903 :dump otherbox_lib_20080414144444 20080414144444 otherbox /lib 1 PARTIAL full-up 0 0.000540 32
904 :part otherbox_lib_20080414144444_try1 otherbox_lib_20080414144444 Conf-004 1 1 PARTIAL 0.000707 32
905 PARTPARTIAL taper Conf-004 1 otherbox /lib 20080414144444 1/1 1 [sec 0.000707 kb 32 kps 45261.669024] ""
906 INFO taper Will request retry of failed split part.
907 INFO taper Will write new label `Conf-005' to new (previously non-amanda) tape
908 START taper datestamp 20080414144444 label Conf-005 tape 2
909 :part otherbox_lib_20080414144444_try2 otherbox_lib_20080414144444 Conf-005 1 1 PARTIAL 0.000540 32
910 PARTPARTIAL taper Conf-005 1 otherbox /lib 20080414144444 1/1 1 [sec 0.000540 kb 32 kps 59259.259259] ""
911 INFO taper Will request retry of failed split part.
912 WARNING driver Out of tapes; going into degraded mode.
913 PARTIAL taper otherbox /lib 20080414144444 1 1 [sec 0.000540 kb 32 kps 59259.259259] "full-up"
914 # a completely failed dump
915 :dump otherbox_boot_20080414144444 20080414144444 otherbox /boot 0 FAIL no-space 0 0.0 0
916 FAIL taper otherbox /boot 20080414144444 0 "no-space"
917 FINISH driver date 20080414144444 time 6.959
918
919 # holding-disk
920 :holding otherbox_lib_20080414144444_holding 20080414144444 otherbox /lib 1 OK 256
921 :holding oldbox_opt_20080414144444_holding 20080414144444 oldbox /opt 0 OK 1298
922
923 # A logfile with orig-kb in taper line
924 ::: log.20080515155555.0
925 :tapelist 20080515155555 Conf-006
926 :timestamp 20080515155555
927 DISK planner somebox /usr/bin
928 DISK planner somebox /lib
929 DISK planner otherbox /lib
930 DISK planner otherbox /usr/bin
931 START planner date 20080515155555
932 START driver date 20080515155555
933 STATS driver hostname somebox
934 STATS driver startup time 0.059
935 INFO planner Full dump of somebox:/lib promoted from 2 days ahead.
936 FINISH planner date 20080515155555 time 0.286
937 SUCCESS dumper somebox /usr/bin 20080515155555 1 [sec 0.001 kb 20 kps 10352.0 orig-kb 20]
938 SUCCESS chunker somebox /usr/bin 20080515155555 1 [sec 1.023 kb 20 kps 50.8]
939 STATS driver estimate somebox /usr/bin 20080515155555 1 [sec 0 nkb 52 ckb 64 kps 1024]
940 START taper datestamp 20080515155555 label Conf-006 tape 1
941 :dump somebox_usr_bin_20080515155555 20080515155555 somebox /usr/bin 1 OK "" 1 0.000370 20 20
942 :part somebox_usr_bin_20080515155555 somebox_usr_bin_20080515155555 Conf-006 1 1 OK 0.000370 20 20
943 PART taper Conf-006 1 somebox /usr/bin 20080515155555 1/1 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
944 DONE taper somebox /usr/bin 20080515155555 1 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
945 # a multi-part dump
946 SUCCESS dumper somebox /lib 20080515155555 0 [sec 0.189 kb 3156 kps 50253.1 orig-kb 3156]
947 SUCCESS chunker somebox /lib 20080515155555 0 [sec 5.250 kb 3156 kps 1815.5]
948 STATS driver estimate somebox /lib 20080515155555 0 [sec 1 nkb 3156 ckb 3156 kps 9500]
949 :dump somebox_lib_20080515155555 20080515155555 somebox /lib 0 OK "" 10 0.051436 3156 3156
950 :part somebox_lib_20080515155555_p1 somebox_lib_20080515155555 Conf-006 2 1 OK 0.005621 1024 3156
951 PART taper Conf-006 2 somebox /lib 20080515155555 1/10 0 [sec 0.005621 kb 1024 kps 182173.990393 orig-kb 3156]
952 :part somebox_lib_20080515155555_p2 somebox_lib_20080515155555 Conf-006 3 2 OK 0.006527 1024 3156
953 PART taper Conf-006 3 somebox /lib 20080515155555 2/10 0 [sec 0.006527 kb 1024 kps 156886.777999 orig-kb 3156]
954 :part somebox_lib_20080515155555_p3 somebox_lib_20080515155555 Conf-006 4 3 OK 0.005854 1024 3156
955 PART taper Conf-006 4 somebox /lib 20080515155555 3/10 0 [sec 0.005854 kb 1024 kps 174923.129484 orig-kb 3156]
956 :part somebox_lib_20080515155555_p4 somebox_lib_20080515155555 Conf-006 5 4 OK 0.007344 1024 3156
957 PART taper Conf-006 5 somebox /lib 20080515155555 4/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
958 :part somebox_lib_20080515155555_p5 somebox_lib_20080515155555 Conf-006 6 5 OK 0.007344 1024 3156
959 PART taper Conf-006 6 somebox /lib 20080515155555 5/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
960 :part somebox_lib_20080515155555_p6 somebox_lib_20080515155555 Conf-006 7 6 OK 0.007344 1024 3156
961 PART taper Conf-006 7 somebox /lib 20080515155555 6/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
962 :part somebox_lib_20080515155555_p7 somebox_lib_20080515155555 Conf-006 8 7 OK 0.007344 1024 3156
963 PART taper Conf-006 8 somebox /lib 20080515155555 7/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
964 :part somebox_lib_20080515155555_p8 somebox_lib_20080515155555 Conf-006 9 8 OK 0.007344 1024 3156
965 PART taper Conf-006 9 somebox /lib 20080515155555 8/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
966 :part somebox_lib_20080515155555_p9 somebox_lib_20080515155555 Conf-006 10 9 OK 0.007344 1024 3156
967 PART taper Conf-006 10 somebox /lib 20080515155555 9/10 0 [sec 0.007344 kb 1024 kps 147993.746743 orig-kb 3156]
968 :part somebox_lib_20080515155555_p10 somebox_lib_20080515155555 Conf-006 11 10 OK 0.001919 284 3156
969 PART taper Conf-006 11 somebox /lib 20080515155555 10/10 0 [sec 0.001919 kb 284 kps 147993.746743 orig-kb 3156]
970 DONE taper somebox /lib 20080515155555 10 0 [sec 0.051436 kb 3156 kps 184695.543977 orig-kb 3156]
971 SUCCESS dumper otherbox /lib 20080515155555 0 [sec 0.001 kb 190 kps 10352.0 orig-kb 20]
972 SUCCESS chunker otherbox /lib 20080515155555 0 [sec 1.023 kb 190 kps 50.8]
973 STATS driver estimate otherbox /lib 20080515155555 0 [sec 0 nkb 190 ckb 190 kps 1024]
974 # this dump is from a previous run, with an older dump_timestamp
975 :dump otherbox_usr_bin_20080515155555 20080511151155 otherbox /usr/bin 0 OK "" 1 0.002733 240 240
976 :part otherbox_usr_bin_20080515155555 otherbox_usr_bin_20080515155555 Conf-006 12 1 OK 0.002733 240 240
977 PART taper Conf-006 12 otherbox /usr/bin 20080511151155 1/1 0 [sec 0.002733 kb 240 kps 136425.648022 orig-kb 240]
978 DONE taper otherbox /usr/bin 20080511151155 1 0 [sec 0.002733 kb 240 kps 136425.648022 orig-kb 240]
979 :dump otherbox_lib_20080515155555 20080511151555 otherbox /lib 0 OK "" 1 0.001733 190 190
980 :part otherbox_lib_20080515155555 otherbox_lib_20080515155555 Conf-006 13 1 OK 0.001733 190 190
981 PART taper Conf-006 13 otherbox /lib 20080511151555 1/1 0 [sec 0.001733 kb 190 kps 136425.648022 orig-kb 190]
982 DONE taper otherbox /lib 20080515155555 1 0 [sec 0.001733 kb 190 kps 136425.648022 orig-kb 190]
983 # this dump is a direct to tape
984 :dump otherbox_direct_20080515155555 20080515155555 otherbox /direct 0 OK "" 1 0.001 190 350
985 :part otherbox_direct_20080515155555 otherbox_direct_20080515155555 Conf-006 14 1 OK 0.001 190 350
986 SUCCESS dumper otherbox /direct 20080515155555 0 [sec 0.001 kb 190 kps 10352.0 orig-kb 350]
987 PART taper Conf-006 14 otherbox /direct 20080515155555 1/1 0 [sec 0.001 kb 190 kps 10352.0]
988 DONE taper otherbox /direct 20080515155555 1 0 [sec 0.001 kb 190 kps 10352.0 orig-kb 350]
989 FINISH driver date 20080515155555 time 24.777
990
991 # A logfile with removed tape-kb in taper line
992 ::: log.20080616166666.0
993 :tapelist 20080616166666 Conf-007
994 # this tape is removed
995 #:tapelist 20080616166666 Conf-007
996 :timestamp 20080616166666
997 DISK planner somebox /usr/bin
998 DISK planner somebox /lib
999 DISK planner otherbox /lib
1000 START planner date 20080616166666
1001 START driver date 20080616166666
1002 STATS driver hostname somebox
1003 STATS driver startup time 0.059
1004 FINISH planner date 20080616166666 time 0.286
1005 SUCCESS dumper somebox /usr/bin 20080616166666 1 [sec 0.001 kb 20 kps 10352.0 orig-kb 20]
1006 SUCCESS chunker somebox /usr/bin 20080616166666 1 [sec 1.023 kb 20 kps 50.8]
1007 STATS driver estimate somebox /usr/bin 20080616166666 1 [sec 0 nkb 52 ckb 64 kps 1024]
1008 START taper datestamp 20080616166666 label Conf-007 tape 1
1009 :dump somebox_usr_bin_20080616166666 20080616166666 somebox /usr/bin 1 OK "" 1 0.000370 20 20
1010 :part somebox_usr_bin_20080616166666 somebox_usr_bin_20080616166666 Conf-007 1 1 OK 0.000370 20 20
1011 PART taper Conf-007 1 somebox /usr/bin 20080616166666 1/1 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
1012 DONE taper somebox /usr/bin 20080616166666 1 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
1013 SUCCESS dumper somebox /lib 20080616166666 1 [sec 0.001 kb 20 kps 10352.0 orig-kb 20]
1014 SUCCESS chunker somebox /lib 20080616166666 1 [sec 1.023 kb 20 kps 50.8]
1015 :dump somebox_lib_20080616166666 20080616166666 somebox /lib 1 FAIL "" 1 0.000370 20 20
1016 :part somebox_lib_20080616166666 somebox_lib_20080616166666 Conf-007 2 1 OK 0.000370 20 20
1017 PART taper Conf-007 2 somebox /lib 20080616166666 1/2 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
1018 STATS driver estimate somebox /lib 20080616166666 1 [sec 0 nkb 52 ckb 64 kps 1024]
1019 START taper datestamp 20080616166666 label Conf-008 tape 1
1020 # this tape is removed
1021 #:part somebox_lib_20080616166666 somebox_lib_20080616166666 Conf-008 1 1 OK 0.000370 20 20
1022 PART taper Conf-008 1 somebox /lib 20080616166666 2/2 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]
1023 DONE taper somebox /lib 20080616166666 1 1 [sec 0.000370 kb 20 kps 54054.054054 orig-kb 20]