Imported Upstream version 3.3.3
[debian/amanda] / installcheck / Amanda_DB_Catalog.pl
1 # Copyright (c) 2008-2012 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
7 #
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11 # for more details.
12 #
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19
20 use Test::More tests => 85;
21 use File::Path;
22 use Data::Dumper;
23 use strict;
24 use warnings;
25
26 use lib "@amperldir@";
27 use Installcheck;
28 use Installcheck::Run;
29 use Installcheck::Catalogs;
30 use Amanda::Config qw( :init :getconf config_dir_relative );
31 use Amanda::DB::Catalog;
32 use Amanda::Cmdline;
33 use Amanda::Xfer qw( :constants );
34
35 # send xfer logging somewhere
36 Amanda::Debug::dbopen("installcheck");
37 Installcheck::log_test_output();
38
39 # set up and load a simple config
40 my $testconf = Installcheck::Run->setup();
41 $testconf->write();
42 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
43     or die("Could not load test config");
44
45 # test functions against an empty set of logfiles
46
47 is_deeply([ Amanda::DB::Catalog::get_write_timestamps() ], [],
48     "No write_timestamps in an empty catalog");
49
50 is_deeply(Amanda::DB::Catalog::get_latest_write_timestamp(), undef,
51     "No latest write_timestamp in an empty catalog");
52
53 is_deeply(Amanda::DB::Catalog::get_latest_write_timestamp(type => 'amvault'), undef,
54     "No latest write_timestamp in an empty catalog, even of a specific type");
55
56 is_deeply([ Amanda::DB::Catalog::get_parts() ], [],
57     "No parts in an empty catalog");
58
59 # and add some logfiles to query, and a corresponding tapelist, while also gathering
60 # a list of parts and dumps for comparison with the results from Amanda::DB::Catalog.
61 # also add some files to holding disk
62 my @dumpspecs;
63
64 # install the bigdb catalog
65 my $cat = Installcheck::Catalogs::load("bigdb");
66 $cat->install();
67 my %dumps = $cat->get_dumps();
68 my %parts = $cat->get_parts();
69
70 Amanda::DB::Catalog::_clear_cache();
71
72 sub partstr {
73     my ($part) = @_;
74     if (exists $part->{'holding_file'}) {
75         return "$part->{holding_file}: " .
76                "$part->{dump}->{hostname} $part->{dump}->{diskname} " .
77                "w$part->{dump}->{write_timestamp} d$part->{dump}->{dump_timestamp}";
78    } else {
79         return "$part->{label}:$part->{filenum}: " .
80                "$part->{dump}->{hostname} $part->{dump}->{diskname} $part->{dump}->{orig_kb} " .
81                "w$part->{dump}->{write_timestamp} d$part->{dump}->{dump_timestamp}";
82    }
83 }
84
85 # filter out recursive links from part->dump->parts, without changing
86 # the original objects.  Note that this removes the object-identity of
87 # dumps.
88 sub filter_parts {
89     my ($parts) = @_;
90
91     my @rv;
92     for my $p (@$parts) {
93         $p = do { my %t = %$p; \%t; }; # copy hash
94         $p->{'dump'} = do { my %t = %{$p->{'dump'}}; \%t; };
95         $p->{'dump'}->{'parts'} = undef;
96         push @rv, $p;
97     }
98
99     return \@rv;
100 }
101
102 sub got_parts {
103     my ($got, $exp, $msg, %params) = @_;
104
105     # give a warning if we accidentally select zero parts
106     if (@$exp == 0) {
107         diag("warning: zero parts expected: $msg")
108             unless $params{'zero_parts_expected'};
109     }
110
111     # filter recursive references to avoid confusing old is_deeply instances
112     if (!is_deeply(filter_parts($got), filter_parts($exp), $msg)) {
113         diag("got parts:");
114         my $i = 0;
115         for (@$got) {
116             diag(" [$i]  " . partstr($_));
117             $i++;
118         }
119
120         diag("expected parts:");
121         $i = 0;
122         for (@$exp) {
123             diag(" [$i]  " . partstr($_));
124             $i++;
125         }
126         return '';
127     }
128     return 1;
129 }
130
131 # get parts filtered by a regexp on the key
132 sub parts_named($) {
133     my ($expr) = @_; 
134     my @selected_keys = grep { $_ =~ $expr } keys %parts;
135     return map { $parts{$_} } @selected_keys;
136 }
137
138 # get parts filtered by an expression on the dumpfile itself
139 sub parts_matching(&) {
140     my ($block) = @_; 
141     return grep { &$block } values %parts;
142 }
143
144 # put @_ in a canonical order
145 sub sortparts {
146     map {
147         # convert bigints to strings and on to integers so is_deeply doesn't get confused
148         $_->{'dump'}->{'level'} = "$_->{dump}->{level}" + 0;
149         $_->{'dump'}->{'bytes'} = 0;
150         $_->{'dump'}->{'bytes'} = "$_->{dump}->{bytes}" + 0 if defined $_->{dump}->{bytes};
151         $_->{'dump'}->{'kb'} = "$_->{dump}->{kb}" + 0;
152         $_->{'dump'}->{'orig_kb'} = "$_->{dump}->{orig_kb}" + 0;
153         if (!defined $_->{filenum}) {
154             $_->{'filenum'} = 0;
155         } else {
156             $_->{'filenum'} = "$_->{filenum}" + 0;
157         }
158         $_->{'kb'} = "$_->{kb}" + 0;
159         $_->{'orig_kb'} = "$_->{orig_kb}" + 0;
160         $_->{'partnum'} = "$_->{partnum}" + 0;
161         $_;
162     } sort {
163         if (exists $a->{'holding_file'} and exists $b->{'holding_file'}) {
164             return $a->{'holding_file'} cmp $b->{'holding_file'};
165         } elsif (not exists $a->{'holding_file'} and not exists $b->{'holding_file'}) {
166             return ($a->{'label'} cmp $b->{'label'})
167                 || ($a->{'filenum'} <=> $b->{'filenum'});
168         } else {
169             return (exists $a->{'holding_file'})? 1 : -1;
170         }
171     }
172     @_;
173 }
174
175 sub dumpstr {
176     my ($dump) = @_;
177     return "$dump->{hostname} $dump->{diskname} " .
178            "$dump->{dump_timestamp} $dump->{level}";
179 }
180
181 # filter out recursive links from dump->parts->dump, without changing
182 # the original objects.
183 sub filter_dumps {
184     my ($dumps) = @_;
185
186     my @rv;
187     for my $d (@$dumps) {
188         $d = do { my %t = %$d; \%t; }; # copy hash
189         my @dparts = map {
190                 return undef unless defined $_;
191                 my $p = do { my %t = %$_; \%t }; # copy part
192                 $p->{'dump'} = undef;
193                 $p;
194             } @{$d->{'parts'}};
195         $d->{'parts'} = [ @dparts ];
196         push @rv, $d;
197     }
198
199     return \@rv;
200 }
201
202 sub got_dumps {
203     my ($got, $exp, $msg, %params) = @_;
204
205     # give a warning if we accidentally select zero dumps
206     if (@$exp == 0) {
207         diag("warning: zero dumps expected: $msg")
208             unless $params{'zero_dumps_expected'};
209     }
210
211     # filter recursive references to avoid confusing old is_deeply instances
212     if (!is_deeply(filter_dumps($got), filter_dumps($exp), $msg)) {
213         diag("got dumps:");
214         for (@$got) {
215             diag("  " . dumpstr($_));
216         }
217         diag("expected dumps:");
218         for (@$exp) {
219             diag("  " . dumpstr($_));
220         }
221         return '';
222     }
223     return 1;
224 }
225
226 # get dumps filtered by a regexp on the key
227 sub dumps_named($) {
228     my ($expr) = @_; 
229     my @selected_keys = grep { $_ =~ $expr } keys %dumps;
230     return map { $dumps{$_} } @selected_keys;
231 }
232
233 # get dumps filtered by an expression on the dumpfile itself
234 sub dumps_matching(&) {
235     my ($block) = @_; 
236     return grep { &$block } values %dumps;
237 }
238
239 # put @_ in a canonical order
240 sub sortdumps {
241     map {
242         # convert bigints to strings and on to integers so is_deeply doesn't get confused
243         $_->{'level'} = "$_->{level}" + 0;
244         $_->{'bytes'} = 0;
245         $_->{'bytes'} = "$_->{bytes}" + 0 if defined $_->{bytes};
246         $_->{'kb'} = "$_->{kb}" + 0;
247         $_->{'orig_kb'} = 0;
248         $_->{'orig_kb'} = "$_->{orig_kb}" + 0 if defined $_->{orig_kb};
249         $_->{'nparts'} = "$_->{nparts}" + 0;
250         $_;
251     } sort {
252         $a->{'write_timestamp'} cmp $b->{'write_timestamp'}
253             or $a->{'hostname'} cmp $b->{'hostname'}
254             or $a->{'diskname'} cmp $b->{'diskname'}
255             or $a->{'level'} <=> $b->{'level'}
256     }
257     @_;
258 }
259
260 ##
261 # Test the timestamps
262
263 is_deeply([ Amanda::DB::Catalog::get_write_timestamps(), ],
264     [ '20080111000000', '20080222222222', '20080313133333',
265       '20080414144444', '20080515155555', '20080616166666',
266       '20100722000000' ],
267     "get_write_timestamps returns all logfile datestamps in proper order, with zero-padding");
268
269 is(Amanda::DB::Catalog::get_latest_write_timestamp(), '20100722000000',
270     "get_latest_write_timestamp correctly returns the latest write timestamp");
271
272 is(Amanda::DB::Catalog::get_latest_write_timestamp(type => 'amdump'), '20100722000000',
273     "get_latest_write_timestamp correctly returns the latest write timestamp of type amdump");
274
275 is(Amanda::DB::Catalog::get_latest_write_timestamp(type => 'amflush'), '20080111000000',
276     "get_latest_write_timestamp correctly returns the latest write timestamp of type amflush");
277
278 is(Amanda::DB::Catalog::get_latest_write_timestamp(types => [qw(amvault amflush)]),
279     '20080222222222',
280     "get_latest_write_timestamp correctly returns the latest write timestamp of a set of ts's");
281
282 is(Amanda::DB::Catalog::get_run_type('20080222222222'), "amvault",
283     "get_run_type detects amvault");
284
285 is(Amanda::DB::Catalog::get_run_type('20080111'), "amflush",
286     "get_run_type detects amflush (short ts)");
287
288 is(Amanda::DB::Catalog::get_run_type('20080111000000'), "amflush",
289     "get_run_type detects amflush (long ts)");
290
291 ##
292 # test get_parts and sort_parts
293
294 got_parts([ sortparts Amanda::DB::Catalog::get_parts() ],
295     [ sortparts parts_named qr/.*/ ],
296     "get_parts returns all parts when given no parameters");
297 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111000000') ],
298     [ sortparts parts_named qr/somebox_lib_20080111/ ],
299     "get_parts parameter write_timestamp");
300 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111') ],
301     [ sortparts parts_named qr/somebox_lib_20080111/ ],
302     "get_parts accepts a short write_timestamp and zero-pads it");
303 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamps => ['20080111000000','20080222222222']) ],
304     [ sortparts parts_named qr/(20080111|20080222222222_p\d*)$/ ],
305     "get_parts parameter write_timestamps");
306 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080111', holding => 1) ],
307     [ ],
308     "get_parts parameter write_timestamp + holding => 1 returns nothing",
309     zero_parts_expected => 1);
310
311 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp => '20080111000000') ],
312     [ sortparts parts_named qr/somebox_lib_20080111/ ],
313     "get_parts parameter dump_timestamp");
314 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp => '20080111') ],
315     [ sortparts parts_named qr/somebox_lib_20080111/ ],
316     "get_parts accepts a short dump_timestamp and zero-pads it");
317 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamps => ['20080111000000','20080222222222']) ],
318     [ sortparts parts_named qr/(20080111|20080222222222_p\d*)$/ ],
319     "get_parts parameter dump_timestamps");
320 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dump_timestamp_match => '200801-2') ],
321     [ sortparts parts_named qr/20080[12]/ ],
322     "get_parts parameter dump_timestamp_match");
323
324 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => 'otherbox') ],
325     [ sortparts parts_named qr/^otherbox_/ ],
326     "get_parts parameter hostname");
327 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => ['otherbox','somebox']) ],
328     [ sortparts parts_named qr/^(otherbox_|somebox_)/ ],
329     "get_parts parameter hostnames");
330 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname_match => '*box') ],
331     [ sortparts parts_named qr/box/ ],
332     "get_parts parameter hostname_match");
333
334 got_parts([ sortparts Amanda::DB::Catalog::get_parts(diskname => '/lib') ],
335     [ sortparts parts_named qr/_lib_/ ],
336     "get_parts parameter diskname");
337 got_parts([ sortparts Amanda::DB::Catalog::get_parts(disknames => ['/lib','/usr/bin']) ],
338     [ sortparts parts_named qr/(_lib_|_usr_bin_)/ ],
339     "get_parts parameter disknames");
340 got_parts([ sortparts Amanda::DB::Catalog::get_parts(diskname_match => '/usr') ],
341     [ sortparts parts_named qr/_usr_/ ],
342     "get_parts parameter diskname_match");
343
344 got_parts([ sortparts Amanda::DB::Catalog::get_parts(label => 'Conf-001') ],
345     [ sortparts parts_matching { defined $_->{'label'} and $_->{'label'} eq 'Conf-001' } ],
346     "get_parts parameter label");
347 got_parts([ sortparts Amanda::DB::Catalog::get_parts(labels => ['Conf-002','Conf-003']) ],
348     [ sortparts parts_matching { defined $_->{'label'} and ($_->{'label'} eq 'Conf-002' or $_->{'label'} eq 'Conf-003') } ],
349     "get_parts parameter labels");
350
351 got_parts([ sortparts Amanda::DB::Catalog::get_parts(level => 0) ],
352     [ sortparts parts_matching { $_->{'dump'}->{'level'} == 0 } ],
353     "get_parts parameter level");
354 got_parts([ sortparts Amanda::DB::Catalog::get_parts(levels => [ 1 ]) ],
355     [ sortparts parts_matching { $_->{'dump'}->{'level'} == 1 } ],
356     "get_parts parameter levels");
357
358 got_parts([ sortparts Amanda::DB::Catalog::get_parts(status => "OK") ],
359     [ sortparts parts_matching { $_->{'status'} eq "OK" } ],
360     "get_parts parameter status = OK");
361
362 got_parts([ sortparts Amanda::DB::Catalog::get_parts(status => "PARTIAL") ],
363     [ sortparts parts_matching { $_->{'status'} eq "PARTIAL" } ],
364     "get_parts parameter status = PARTIAL");
365
366 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox") ],
367     [ sortparts parts_named qr/^oldbox_/ ],
368     "get_parts finds a holding-disk dump");
369
370 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox", holding => 0) ],
371     [ ],
372     "get_parts ignores a holding-disk dump if holding is false",
373     zero_parts_expected => 1);
374 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => "oldbox", holding => 1) ],
375     [ sortparts parts_named qr/^oldbox_/ ],
376     "get_parts supplies a holding-disk dump if holding is true");
377 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ]) ],
378     [ sortparts (parts_named qr/^oldbox_.*_holding/, parts_named qr/^somebox_/) ],
379     "get_parts returns both holding and on-media dumps");
380 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ],
381                                                      holding => 1) ],
382     [ sortparts parts_named qr/^oldbox_.*_holding/ ],
383     "get_parts ignores an on-media dump if holding is true");
384
385 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostnames => [ "oldbox", "somebox" ],
386                                                      holding => 0) ],
387     [ sortparts parts_named qr/^somebox_/ ],
388     "get_parts ignores an holding dump if holding is false");
389
390 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
391 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
392     [ sortparts parts_named qr/_lib_/ ],
393     "get_parts parameter dumpspecs with one dumpspec");
394
395 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib", "somebox"], 0);
396 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
397     [ sortparts parts_matching { $_->{'dump'}->{'diskname'} eq '/lib'
398                               or $_->{'dump'}->{'hostname'} eq 'somebox' } ],
399     "get_parts parameter dumpspecs with two dumpspecs");
400
401 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
402 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ]) ],
403     [ sortparts parts_matching { $_->{'dump'}->{'hostname'} eq 'otherbox'
404                               or $_->{'dump'}->{'hostname'} eq 'somebox' } ],
405     "get_parts parameter dumpspecs with two non-overlapping dumpspecs");
406
407 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
408 got_parts([ sortparts Amanda::DB::Catalog::get_parts(dumpspecs => [ @dumpspecs ], holding => 1), ],
409     [ sortparts parts_matching { $_->{'dump'}->{'hostname'} eq 'otherbox'
410                              and exists $_->{'holding_file'} } ],
411     "get_parts parameter dumpspecs with two non-overlapping dumpspecs, but holding files only");
412
413 ## more complex, multi-parameter queries
414
415 got_parts([ sortparts Amanda::DB::Catalog::get_parts(hostname => 'somebox',
416                                                      diskname_match => '/lib') ],
417     [ sortparts parts_named qr/^somebox_lib_/ ],
418     "get_parts parameters hostname and diskname_match");
419
420 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080313133333',
421                                                      dump_timestamp => '20080311131133') ],
422     [ sortparts parts_matching { $_->{'dump'}->{'dump_timestamp'} eq '20080311131133' 
423                     and $_->{'dump'}->{'write_timestamp'} eq '20080313133333' } ],
424     "get_parts parameters write_timestamp and dump_timestamp");
425
426 got_parts([ sortparts Amanda::DB::Catalog::get_parts(write_timestamp => '20080414144444',
427                                                      status => 'OK') ],
428     [ ], # there were no OK dumps on that date
429     "get_parts parameters write_timestamp status",
430     zero_parts_expected => 1);
431
432 ## test part sorting
433
434 got_parts([ Amanda::DB::Catalog::sort_parts(['write_timestamp'],
435                 @parts{'somebox_lib_20080222222222_p1','somebox_lib_20080111'}) ],
436               [ @parts{'somebox_lib_20080111','somebox_lib_20080222222222_p1'} ],
437     "sort by write_timestamps");
438 got_parts([ Amanda::DB::Catalog::sort_parts(['-write_timestamp'],
439                 @parts{'somebox_lib_20080111','somebox_lib_20080222222222_p1'}) ],
440               [ @parts{'somebox_lib_20080222222222_p1','somebox_lib_20080111'} ],
441     "sort by write_timestamps, reverse");
442
443 got_parts([ Amanda::DB::Catalog::sort_parts(['hostname', '-diskname', 'write_timestamp'],
444                 @parts{
445                     'somebox_lib_20080222222222_p1',
446                     'somebox_usr_bin_20080313133333',
447                     'somebox_lib_20080313133333_p4',
448                     'otherbox_lib_20080313133333',
449                     'somebox_lib_20080111',
450                     }) ],
451               [ @parts{
452                     'otherbox_lib_20080313133333',
453                     'somebox_usr_bin_20080313133333',
454                     'somebox_lib_20080111',
455                     'somebox_lib_20080222222222_p1',
456                     'somebox_lib_20080313133333_p4',
457                     } ],
458     "multi-key sort");
459
460 got_parts([ Amanda::DB::Catalog::sort_parts(['filenum'],
461                 @parts{
462                     'somebox_lib_20080313133333_p9',
463                     'somebox_lib_20080313133333_p10',
464                     'somebox_lib_20080313133333_p1',
465                     }) ],
466               [ @parts{
467                     'somebox_lib_20080313133333_p1',
468                     'somebox_lib_20080313133333_p9',
469                     'somebox_lib_20080313133333_p10',
470                     } ],
471                 "filenum is sorted numerically, not lexically");
472
473 got_parts([ Amanda::DB::Catalog::sort_parts(['-partnum'],
474                 @parts{
475                     'somebox_lib_20080313133333_p9',
476                     'somebox_lib_20080313133333_p10',
477                     'somebox_lib_20080313133333_p1',
478                     }) ],
479               [ @parts{
480                     'somebox_lib_20080313133333_p10',
481                     'somebox_lib_20080313133333_p9',
482                     'somebox_lib_20080313133333_p1',
483                     } ],
484                 "partnum is sorted numerically (and in reverse!), not lexically");
485
486 got_parts([ Amanda::DB::Catalog::sort_parts(['nparts'],
487                 @parts{
488                     'somebox_lib_20080313133333_p9', # nparts=10
489                     'somebox_lib_20080222222222_p2', # nparts=2
490                     }) ],
491               [ @parts{
492                     'somebox_lib_20080222222222_p2', # nparts=2
493                     'somebox_lib_20080313133333_p9', # nparts=10
494                     } ],
495                 "nparts is sorted numerically, not lexically");
496
497 got_parts([ Amanda::DB::Catalog::sort_parts(['label'],
498                 @parts{
499                     'somebox_lib_20080313133333_p9', # Conf-003
500                     'somebox_lib_20080222222222_p2', # Conf-002
501                     }) ],
502               [ @parts{
503                     'somebox_lib_20080222222222_p2', # Conf-002
504                     'somebox_lib_20080313133333_p9', # Conf-003
505                     } ],
506                 "labels sort correctly");
507
508 ### test dump selecting
509
510 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps() ],
511     [ sortdumps dumps_named qr/.*/ ],
512     "get_dumps returns all dumps when given no parameters");
513
514 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111000000') ],
515     [ sortdumps dumps_named qr/somebox_lib_20080111/ ],
516     "get_dumps parameter write_timestamp");
517
518 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111') ],
519     [ sortdumps dumps_named qr/somebox_lib_20080111/ ],
520     "get_dumps accepts a short write_timestamp and zero-pads it");
521
522 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamps => ['20080111000000','20080222222222']) ],
523     [ sortdumps dumps_named qr/(20080111|20080222222222)$/ ],
524     "get_dumps parameter write_timestamps");
525
526 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'otherbox') ],
527     [ sortdumps dumps_named qr/^otherbox/ ],
528     "get_dumps parameter hostname");
529
530 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'oldbox') ],
531     [ sortdumps dumps_named qr/^oldbox_.*_holding/ ],
532     "get_dumps parameter hostname, holding");
533
534 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostnames => ['notthere', 'otherbox']) ],
535     [ sortdumps dumps_named qr/^otherbox/ ],
536     "get_dumps parameter hostnames");
537
538 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(hostname_match => 'other*') ],
539     [ sortdumps dumps_named qr/^otherbox/ ],
540     "get_dumps parameter hostname_match");
541
542 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(diskname => '/lib') ],
543     [ sortdumps dumps_named qr/^[^_]*_lib_/ ],
544     "get_dumps parameter diskname");
545
546 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(disknames => ['/lib', '/usr/bin']) ],
547     [ sortdumps dumps_named qr/^[^_]*_(usr_bin|lib)_/ ],
548     "get_dumps parameter disknames");
549
550 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(diskname_match => 'bin') ],
551     [ sortdumps dumps_named qr/.*_bin_/ ],
552     "get_dumps parameter diskname_match");
553
554 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp => '20080414144444') ],
555     [ sortdumps dumps_matching { $_->{'dump_timestamp'} eq '20080414144444' } ],
556     "get_dumps parameter dump_timestamp");
557
558 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(
559                         dump_timestamps => ['20080414144444', '20080311131133']) ],
560     [ sortdumps dumps_matching { $_->{'dump_timestamp'} eq '20080414144444' 
561                               or $_->{'dump_timestamp'} eq '20080311131133' } ],
562     "get_dumps parameter dump_timestamps");
563
564 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp_match => '200804-7') ],
565     [ sortdumps dumps_matching { $_->{'dump_timestamp'} =~ /^20080[4567]/ } ],
566     "get_dumps parameter dump_timestamp_match");
567
568 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(level => 0) ],
569     [ sortdumps dumps_matching { $_->{'level'} == 0 } ],
570     "get_dumps parameter level");
571
572 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(levels => [ 1 ]) ],
573     [ sortdumps dumps_matching { $_->{'level'} == 1 } ],
574     "get_dumps parameter levels");
575
576 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "OK") ],
577     [ sortdumps dumps_matching { $_->{'status'} eq "OK" } ],
578     "get_dumps parameter status = OK");
579
580 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "PARTIAL") ],
581     [ sortdumps dumps_matching { $_->{'status'} eq "PARTIAL" } ],
582     "get_dumps parameter status = PARTIAL");
583
584 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(status => "FAIL") ],
585     [ sortdumps dumps_matching { $_->{'status'} eq "FAIL" } ],
586     "get_dumps parameter status = FAIL");
587
588 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
589 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
590     [ sortdumps dumps_named qr/_lib_/ ],
591     "get_dumps parameter dumpspecs with one dumpspec");
592
593 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib"], 0);
594 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ], holding => 1) ],
595     [ sortdumps dumps_named qr/_lib_.*_holding/ ],
596     "get_dumps parameter dumpspecs with one dumpspec");
597
598 @dumpspecs = Amanda::Cmdline::parse_dumpspecs([".*", "/lib", "somebox"], 0);
599 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
600     [ sortdumps dumps_matching { $_->{'diskname'} eq '/lib'
601                               or $_->{'hostname'} eq 'somebox' } ],
602     "get_dumps parameter dumpspecs with two dumpspecs");
603
604 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["otherbox", "*", "somebox"], 0);
605 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
606     [ sortdumps dumps_matching { $_->{'hostname'} eq 'otherbox'
607                               or $_->{'hostname'} eq 'somebox' } ],
608     "get_dumps parameter dumpspecs with two non-overlapping dumpspecs");
609
610 @dumpspecs = Amanda::Cmdline::parse_dumpspecs(["does-not-exist"], 0);
611 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
612     [ ],
613     "get_dumps parameter dumpspecs with a dumpspec that matches nothing",
614     zero_dumps_expected => 1);
615
616 @dumpspecs = Amanda::Cmdline::dumpspec_t->new(undef, undef, undef, undef, '20080222222222');
617 got_dumps([ sortdumps Amanda::DB::Catalog::get_dumps(dumpspecs => [ @dumpspecs ]) ],
618     [ sortdumps dumps_matching { $_->{'write_timestamp'} eq '20080222222222' }],
619     "get_dumps parameter dumpspecs with write_timestamp");
620
621 ## test dump sorting
622
623 got_dumps([ Amanda::DB::Catalog::sort_dumps(['write_timestamp'],
624                 @dumps{'somebox_lib_20080222222222','somebox_lib_20080111'}) ],
625               [ @dumps{'somebox_lib_20080111','somebox_lib_20080222222222'} ],
626     "sort dumps by write_timestamps");
627 got_dumps([ Amanda::DB::Catalog::sort_dumps(['-write_timestamp'],
628                 @dumps{'somebox_lib_20080111','somebox_lib_20080222222222'}) ],
629               [ @dumps{'somebox_lib_20080222222222','somebox_lib_20080111'} ],
630     "sort dumps by write_timestamps, reverse");
631
632 got_dumps([ Amanda::DB::Catalog::sort_dumps(['hostname', '-diskname', 'write_timestamp'],
633                 @dumps{
634                     'somebox_lib_20080222222222',
635                     'somebox_usr_bin_20080313133333',
636                     'somebox_lib_20080313133333',
637                     'otherbox_lib_20080313133333',
638                     'somebox_lib_20080111',
639                     }) ],
640               [ @dumps{
641                     'otherbox_lib_20080313133333',
642                     'somebox_usr_bin_20080313133333',
643                     'somebox_lib_20080111',
644                     'somebox_lib_20080222222222',
645                     'somebox_lib_20080313133333',
646                     } ],
647                 "multi-key dump sort");
648
649 got_dumps([ Amanda::DB::Catalog::sort_dumps(['nparts'],
650                 @dumps{
651                     'somebox_lib_20080313133333', # nparts=10
652                     'somebox_lib_20080222222222', # nparts=2
653                     }) ],
654               [ @dumps{
655                     'somebox_lib_20080222222222', # nparts=2
656                     'somebox_lib_20080313133333', # nparts=10
657                     } ],
658                 "dumps' nparts is sorted numerically, not lexically");
659
660 got_dumps([ Amanda::DB::Catalog::sort_dumps(['-level'],
661                 @dumps{
662                     'somebox_lib_20080313133333', # level=0
663                     'somebox_usr_bin_20080313133333', # level=1
664                     }) ],
665               [ @dumps{
666                     'somebox_usr_bin_20080313133333', # level=1
667                     'somebox_lib_20080313133333', # level=0
668                     } ],
669                 "sort dumps by level, reversed");
670
671 got_dumps([ Amanda::DB::Catalog::sort_dumps(['dump_timestamp'],
672                 @dumps{
673                     'somebox_lib_20080313133333', # dts=20080313133333
674                     'otherbox_usr_bin_20080313133333_1', # dts=20080311131133
675                     }) ],
676               [ @dumps{
677                     'otherbox_usr_bin_20080313133333_1', # dts=20080311131133
678                     'somebox_lib_20080313133333', # dts=20080313133333
679                     } ],
680                 "sort dumps by write_timestamp");
681
682
683 # install the multi-taper catalog
684 $cat = Installcheck::Catalogs::load("multi-taper");
685 $cat->install();
686 %dumps = $cat->get_dumps();
687 %parts = $cat->get_parts();
688
689 Amanda::DB::Catalog::_clear_cache();
690
691 got_parts([ sortparts Amanda::DB::Catalog::get_parts() ],
692         [ sortparts parts_named qr/.*/ ],
693         "get_parts returns all parts when given no parameters");
694