1 # Copyright (c) 2008 Zmanda Inc. All Rights Reserved.
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.
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
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
16 # Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19 # TODO: check large values of 'kb'
21 use Test::More tests => 35;
25 use lib "@amperldir@";
26 use Installcheck::Config;
27 use Amanda::Config qw( :init :getconf config_dir_relative );
28 use Amanda::DB::Catalog;
30 # set up and load a simple config
31 my $testconf = Installcheck::Config->new();
33 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
34 or die("Could not load test config");
36 # test functions against an empty set of logfiles
38 is_deeply([ Amanda::DB::Catalog::get_write_timestamps() ], [],
39 "No write_timestamps in an empty catalog");
41 is_deeply(Amanda::DB::Catalog::get_latest_write_timestamp(), undef,
42 "No latest write_timestamp in an empty catalog");
44 is_deeply([ Amanda::DB::Catalog::get_dumps() ], [],
45 "No dumpfiles in an empty catalog");
47 # and add some logfiles to query, and a corresponding tapelist, while also gathering
48 # a list of dumpfiles for comparison with the results from Amanda::DB::Catalog
49 my $logdir = config_dir_relative(getconf($CNF_LOGDIR));
50 my $tapelist_fn = config_dir_relative(getconf($CNF_TAPELIST));
54 open (my $tapelist, ">", $tapelist_fn);
57 next if (/^#/ or /^\S*$/);
60 if (/^:tapelist (\d+) (\S+)\s*$/) {
61 print $tapelist "$1 $2 reuse\n";
67 open $output, ">", "$logdir/$1" or die("Could not open $1 for writing: $!");
72 if (/^:timestamp (\d+)/) {
73 $write_timestamp = $1;
78 if (/^:dumpfile (\S+) (\S+) (\S+) (\S+) (\d+) (\S+) (\d+) (\d+) (\d+) (\S+) (\S+) (\d+)/) {
80 'dump_timestamp' => $2, 'hostname' => $3, 'diskname' => $4,
81 'level' => $5+0, 'label' => $6, 'filenum' => $7+0,
82 'partnum' => $8+0, 'nparts' => $9+0, 'status' => $10,
83 'sec' => $11+0.0, 'kb' => $12+0,
84 'write_timestamp' => $write_timestamp,
89 die("syntax error") if (/^:/);
95 Amanda::DB::Catalog::_clear_cache();
100 is_deeply([ Amanda::DB::Catalog::get_write_timestamps(), ],
101 [ '20080111000000', '20080222222222', '20080313133333', '20080414144444' ],
102 "get_write_timestamps returns all logfile datestamps in proper order, with zero-padding");
104 is(Amanda::DB::Catalog::get_latest_write_timestamp(), '20080414144444',
105 "get_latest_write_timestamp correctly returns the latest write timestamp");
108 # test get_dumps and sort_dumps
110 # get dumps filtered by a regexp on the key
113 my @selected_keys = grep { $_ =~ $expr } keys %dumpfiles;
114 return map { $dumpfiles{$_} } @selected_keys;
117 # get dumps filtered by an expression on the dumpfile itself
120 return grep { &$block } values %dumpfiles;
123 # put @_ in a canonical order
126 # convert bigints to strings so is_deeply doesn't get confused
127 $_->{'level'} = "$_->{level}";
128 $_->{'filenum'} = "$_->{filenum}";
129 $_->{'kb'} = "$_->{kb}";
132 $a->{'label'} cmp $b->{'label'}
133 or $a->{'filenum'} cmp $b->{'filenum'}
138 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps() ],
139 [ sortdumps dump_names qr/.*/ ],
140 "get_dumps returns all dumps when given no parameters");
142 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111000000') ],
143 [ sortdumps dump_names qr/somebox_lib_20080111/ ],
144 "get_dumps parameter write_timestamp");
145 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080111') ],
146 [ sortdumps dump_names qr/somebox_lib_20080111/ ],
147 "get_dumps accepts a short write_timestamp and zero-pads it");
148 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamps => ['20080111000000','20080222222222']) ],
149 [ sortdumps dump_names qr/(20080111|20080222222222)$/ ],
150 "get_dumps parameter write_timestamps");
152 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp => '20080111000000') ],
153 [ sortdumps dump_names qr/somebox_lib_20080111/ ],
154 "get_dumps parameter dump_timestamp");
155 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp => '20080111') ],
156 [ sortdumps dump_names qr/somebox_lib_20080111/ ],
157 "get_dumps accepts a short dump_timestamp and zero-pads it");
158 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamps => ['20080111000000','20080222222222']) ],
159 [ sortdumps dump_names qr/(20080111|20080222222222)$/ ],
160 "get_dumps parameter dump_timestamps");
161 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(dump_timestamp_match => '200801-2') ],
162 [ sortdumps dump_names qr/20080[12]/ ],
163 "get_dumps parameter dump_timestamp_match");
165 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'otherbox') ],
166 [ sortdumps dump_names qr/^otherbox_/ ],
167 "get_dumps parameter hostname");
168 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostnames => ['otherbox','somebox']) ],
169 [ sortdumps dump_names qr/^(otherbox_|somebox_)/ ],
170 "get_dumps parameter hostnames");
171 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostname_match => '*box') ],
172 [ sortdumps dump_names qr/box/ ],
173 "get_dumps parameter hostname_match");
175 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(diskname => '/lib') ],
176 [ sortdumps dump_names qr/_lib_/ ],
177 "get_dumps parameter diskname");
178 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(disknames => ['/lib','/usr/bin']) ],
179 [ sortdumps dump_names qr/(_lib_|_usr_bin_)/ ],
180 "get_dumps parameter disknames");
181 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(diskname_match => '/usr') ],
182 [ sortdumps dump_names qr/_usr_/ ],
183 "get_dumps parameter diskname_match");
185 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(label => 'Conf-001') ],
186 [ sortdumps dumps { $_->{'label'} eq 'Conf-001' } ],
187 "get_dumps parameter label");
188 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(labels => ['Conf-002','Conf-003']) ],
189 [ sortdumps dumps { $_->{'label'} eq 'Conf-002' or $_->{'label'} eq 'Conf-003' } ],
190 "get_dumps parameter labels");
192 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(level => 0) ],
193 [ sortdumps dumps { $_->{'level'} == 0 } ],
194 "get_dumps parameter level");
195 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(levels => [ 1 ]) ],
196 [ sortdumps dumps { $_->{'level'} == 1 } ],
197 "get_dumps parameter levels");
199 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(status => "OK") ],
200 [ sortdumps dumps { $_->{'status'} eq "OK" } ],
201 "get_dumps parameter status = OK");
203 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(status => "PARTIAL") ],
204 [ sortdumps dumps { $_->{'status'} eq "PARTIAL" } ],
205 "get_dumps parameter status = PARTIAL");
207 ## more complex, multi-parameter queries
209 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'somebox',
210 diskname_match => '/lib') ],
211 [ sortdumps dump_names qr/^somebox_lib_/ ],
212 "get_dumps parameters hostname and diskname_match");
214 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080313133333',
215 dump_timestamp => '20080311131133') ],
216 [ sortdumps dumps { $_->{'dump_timestamp'} eq '20080311131133'
217 and $_->{'write_timestamp'} eq '20080313133333' } ],
218 "get_dumps parameters write_timestamp and dump_timestamp");
220 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(write_timestamp => '20080414144444',
222 [ ], # there were no OK dumps on that date
223 "get_dumps parameters write_timestamp status");
226 # (this is not exhaustive, as that would make the tests more complex than the
229 is_deeply([ Amanda::DB::Catalog::sort_dumps(['write_timestamp'],
230 @dumpfiles{'somebox_lib_20080222222222','somebox_lib_20080111'}) ],
231 [ @dumpfiles{'somebox_lib_20080111','somebox_lib_20080222222222'} ],
232 "sort by write_timestamps");
233 is_deeply([ Amanda::DB::Catalog::sort_dumps(['-write_timestamp'],
234 @dumpfiles{'somebox_lib_20080111','somebox_lib_20080222222222'}) ],
235 [ @dumpfiles{'somebox_lib_20080222222222','somebox_lib_20080111'} ],
236 "sort by write_timestamps, reverse");
238 is_deeply([ Amanda::DB::Catalog::sort_dumps(['hostname', '-diskname', 'write_timestamp'],
240 'somebox_lib_20080222222222',
241 'somebox_usr_bin_20080313133333',
242 'somebox_lib_20080313133333_p4',
243 'otherbox_lib_20080313133333',
244 'somebox_lib_20080111',
247 'otherbox_lib_20080313133333',
248 'somebox_usr_bin_20080313133333',
249 'somebox_lib_20080111',
250 'somebox_lib_20080222222222',
251 'somebox_lib_20080313133333_p4',
257 # one to an existing logfile, same tape
258 Amanda::DB::Catalog::add_dump({
259 'write_timestamp' => '20080111',
260 'dump_timestamp' => '20080707070707',
261 'hostname' => 'newbox',
262 'diskname' => '/newdisk',
264 'label' => 'Conf-001',
273 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'newbox') ],
275 'write_timestamp' => '20080111000000',
276 'dump_timestamp' => '20080707070707',
277 'hostname' => 'newbox',
278 'diskname' => '/newdisk',
280 'label' => 'Conf-001',
288 "successfully re-read an added dump in an existing logfile");
290 # and again, to test the last-logfile cache in Amanda::DB::Catalog
291 Amanda::DB::Catalog::add_dump({
292 'write_timestamp' => '20080111',
293 'dump_timestamp' => '20080707070707',
294 'hostname' => 'newbox',
295 'diskname' => '/newdisk2',
297 'label' => 'Conf-001',
306 is(scalar Amanda::DB::Catalog::get_dumps(hostname => 'newbox'), 2,
307 "adding another dump to that logfile and re-reading gives 2 dumps");
309 # and another in a new file, as well as a tapelist entry
310 Amanda::DB::Catalog::add_dump({
311 'write_timestamp' => '20080707070707',
312 'dump_timestamp' => '20080707070707',
313 'hostname' => 'newlog',
314 'diskname' => '/newdisk',
316 'label' => 'Conf-009',
325 is_deeply([ sortdumps Amanda::DB::Catalog::get_dumps(hostname => 'newlog') ],
327 'write_timestamp' => '20080707070707',
328 'dump_timestamp' => '20080707070707',
329 'hostname' => 'newlog',
330 'diskname' => '/newdisk',
332 'label' => 'Conf-009',
340 "successfully re-read an added dump in a new logfile");
342 # and add a multipart dump to that same logfile
343 for (my $i = 1; $i <= 5; $i++) {
344 Amanda::DB::Catalog::add_dump({
345 'write_timestamp' => '20080707070707',
346 'dump_timestamp' => '20080707070707',
347 'hostname' => 'newlog',
348 'diskname' => '/bigdisk',
350 'label' => 'Conf-009',
360 is(scalar Amanda::DB::Catalog::get_dumps(diskname => '/bigdisk'), 5,
361 "multi-part dump added and re-read successfully");
364 # a short-datestamp logfile with only a single, single-part file in it
366 :tapelist 20080111 Conf-001
367 :timestamp 20080111000000
368 DISK planner somebox /lib
369 START planner date 20080111
370 START driver date 20080111
371 STATS driver hostname somebox
372 STATS driver startup time 0.051
373 FINISH planner date 20080111 time 82.721
374 START taper datestamp 20080111 label Conf-001 tape 1
375 SUCCESS dumper somebox /lib 20080111 0 [sec 0.209 kb 1970 kps 9382.2 orig-kb 1970]
376 SUCCESS chunker somebox /lib 20080111 0 [sec 0.305 kb 420 kps 1478.7]
377 STATS driver estimate somebox /lib 20080111 0 [sec 1 nkb 2002 ckb 480 kps 385]
378 :dumpfile somebox_lib_20080111 20080111000000 somebox /lib 0 Conf-001 1 1 1 OK 4.813543 419
379 PART taper Conf-001 1 somebox /lib 20080111 1/1 0 [sec 4.813543 kb 419 kps 87.133307]
380 DONE taper somebox /lib 20080111 1 0 [sec 4.813543 kb 419 kps 87.133307]
381 FINISH driver date 20080111 time 2167.581
383 # a long-datestamp logfile, also fairly simple
384 ::: log.20080222222222.0
385 :tapelist 20080222222222 Conf-002
386 :timestamp 20080222222222
387 DISK planner somebox /lib
388 START planner date 20080222222222
389 START driver date 20080222222222
390 STATS driver hostname somebox
391 STATS driver startup time 0.051
392 FINISH planner date 20080222222222 time 0.102
393 SUCCESS dumper somebox /lib 20080222222222 0 [sec 0.012 kb 100 kps 8115.6 orig-kb 100]
394 SUCCESS chunker somebox /lib 20080222222222 0 [sec 5.075 kb 100 kps 26.0]
395 STATS driver estimate somebox /lib 20080222222222 0 [sec 0 nkb 132 ckb 160 kps 1024]
396 START taper datestamp 20080222222222 label Conf-002 tape 1
397 :dumpfile somebox_lib_20080222222222 20080222222222 somebox /lib 0 Conf-002 1 1 1 OK 0.000733 100
398 PART taper Conf-002 1 somebox /lib 20080222222222 1/1 0 [sec 0.000733 kb 100 kps 136425.648022]
399 DONE taper somebox /lib 20080222222222 1 0 [sec 0.000733 kb 100 kps 136425.648022]
400 FINISH driver date 20080222222222 time 6.206
402 # a logfile with several dumps in it, one of which comes in many parts, and one of which is
403 # from a previous run
404 ::: log.20080313133333.0
405 :tapelist 20080313133333 Conf-003
406 :timestamp 20080313133333
407 DISK planner somebox /usr/bin
408 DISK planner somebox /lib
409 DISK planner otherbox /lib
410 DISK planner otherbox /usr/bin
411 START planner date 20080313133333
412 START driver date 20080313133333
413 STATS driver hostname somebox
414 STATS driver startup time 0.059
415 INFO planner Full dump of somebox:/lib promoted from 2 days ahead.
416 FINISH planner date 20080313133333 time 0.286
417 SUCCESS dumper somebox /usr/bin 20080313133333 1 [sec 0.001 kb 20 kps 10352.0 orig-kb 20]
418 SUCCESS chunker somebox /usr/bin 20080313133333 1 [sec 1.023 kb 20 kps 50.8]
419 STATS driver estimate somebox /usr/bin 20080313133333 1 [sec 0 nkb 52 ckb 64 kps 1024]
420 START taper datestamp 20080313133333 label Conf-003 tape 1
421 :dumpfile somebox_usr_bin_20080313133333 20080313133333 somebox /usr/bin 1 Conf-003 1 1 1 OK 0.000370 20
422 PART taper Conf-003 1 somebox /usr/bin 20080313133333 1/1 1 [sec 0.000370 kb 20 kps 54054.054054]
423 DONE taper somebox /usr/bin 20080313133333 1 1 [sec 0.000370 kb 20 kps 54054.054054]
425 SUCCESS dumper somebox /lib 20080313133333 0 [sec 0.189 kb 3156 kps 50253.1 orig-kb 3156]
426 SUCCESS chunker somebox /lib 20080313133333 0 [sec 5.250 kb 3156 kps 1815.5]
427 STATS driver estimate somebox /lib 20080313133333 0 [sec 1 nkb 3156 ckb 3156 kps 9500]
428 :dumpfile somebox_lib_20080313133333_p1 20080313133333 somebox /lib 0 Conf-003 2 1 4 OK 0.005621 1024
429 PART taper Conf-003 2 somebox /lib 20080313133333 1/4 0 [sec 0.005621 kb 1024 kps 182173.990393]
430 :dumpfile somebox_lib_20080313133333_p2 20080313133333 somebox /lib 0 Conf-003 3 2 4 OK 0.006527 1024
431 PART taper Conf-003 3 somebox /lib 20080313133333 2/4 0 [sec 0.006527 kb 1024 kps 156886.777999]
432 :dumpfile somebox_lib_20080313133333_p3 20080313133333 somebox /lib 0 Conf-003 4 3 4 OK 0.005854 1024
433 PART taper Conf-003 4 somebox /lib 20080313133333 3/4 0 [sec 0.005854 kb 1024 kps 174923.129484]
434 :dumpfile somebox_lib_20080313133333_p4 20080313133333 somebox /lib 0 Conf-003 5 4 4 OK 0.001919 284
435 PART taper Conf-003 5 somebox /lib 20080313133333 4/4 0 [sec 0.001919 kb 284 kps 147993.746743]
436 DONE taper somebox /lib 20080313133333 10 0 [sec 0.051436 kb 3156 kps 184695.543977]
437 SUCCESS dumper otherbox /lib 20080313133333 0 [sec 0.001 kb 190 kps 10352.0 orig-kb 20]
438 SUCCESS chunker otherbox /lib 20080313133333 0 [sec 1.023 kb 190 kps 50.8]
439 STATS driver estimate otherbox /lib 20080313133333 0 [sec 0 nkb 190 ckb 190 kps 1024]
440 # this dump is from a previous run, with an older dump_timestamp
441 :dumpfile otherbox_usr_bin_20080313133333 20080311131133 otherbox /usr/bin 0 Conf-003 6 1 1 OK 0.002733 240
442 PART taper Conf-003 6 otherbox /usr/bin 20080311131133 1/1 0 [sec 0.002733 kb 240 kps 136425.648022]
443 :dumpfile otherbox_lib_20080313133333 20080313133333 otherbox /lib 0 Conf-003 7 1 1 OK 0.001733 190
444 PART taper Conf-003 7 otherbox /lib 20080313133333 1/1 0 [sec 0.001733 kb 190 kps 136425.648022]
445 DONE taper otherbox /lib 20080313133333 1 0 [sec 0.001733 kb 190 kps 136425.648022]
446 FINISH driver date 20080313133333 time 24.777
448 # A logfile with some partial parts (PARTPARTIAL) in it
449 ::: log.20080414144444.0
450 :tapelist 20080414144444 Conf-004
451 :tapelist 20080414144444 Conf-005
452 :timestamp 20080414144444
453 DISK planner otherbox /lib
454 START planner date 20080414144444
455 START driver date 20080414144444
456 STATS driver hostname otherbox
457 STATS driver startup time 0.075
458 INFO taper Will write new label `Conf-004' to new (previously non-amanda) tape
459 FINISH planner date 20080414144444 time 2.139
460 SUCCESS dumper otherbox /lib 20080414144444 1 [sec 0.003 kb 60 kps 16304.3 orig-kb 60]
461 SUCCESS chunker otherbox /lib 20080414144444 1 [sec 1.038 kb 60 kps 88.5]
462 STATS driver estimate otherbox /lib 20080414144444 1 [sec 0 nkb 92 ckb 96 kps 1024]
463 START taper datestamp 20080414144444 label Conf-004 tape 1
464 :dumpfile otherbox_lib_20080414144444_try1 20080414144444 otherbox /lib 1 Conf-004 1 1 1 PARTIAL 0.000707 32
465 PARTPARTIAL taper Conf-004 1 otherbox /lib 20080414144444 1/1 1 [sec 0.000707 kb 32 kps 45261.669024] ""
466 INFO taper Will request retry of failed split part.
467 INFO taper Will write new label `Conf-005' to new (previously non-amanda) tape
468 START taper datestamp 20080414144444 label Conf-005 tape 2
469 :dumpfile otherbox_lib_20080414144444_try2 20080414144444 otherbox /lib 1 Conf-005 1 1 1 PARTIAL 0.000540 32
470 PARTPARTIAL taper Conf-005 1 otherbox /lib 20080414144444 1/1 1 [sec 0.000540 kb 32 kps 59259.259259] ""
471 INFO taper Will request retry of failed split part.
472 WARNING driver Out of tapes; going into degraded mode.
473 PARTIAL taper otherbox /lib 20080414144444 1 1 [sec 0.000540 kb 32 kps 59259.259259] ""
474 FINISH driver date 20080414144444 time 6.959