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