1 # Copyright (c) 2008-2012 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. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19 use Test::More tests => 609;
20 use File::Path qw( mkpath rmtree );
26 use lib "@amperldir@";
28 use Installcheck::Mock;
29 use Installcheck::Config;
31 use Amanda::Device qw( :constants );
32 use Amanda::Config qw( :getconf :init );
33 use Amanda::Xfer qw( :constants );
34 use Amanda::Header qw( :constants );
36 use Amanda::Constants;
43 my ($vtape1, $vtape2);
44 my ($input_filename, $output_filename) =
45 ( "$Installcheck::TMP/input.tmp", "$Installcheck::TMP/output.tmp" );
46 my $taperoot = "$Installcheck::TMP/Amanda_Device_test_tapes";
49 # we'll need some vtapes..
53 my $mytape = "$taperoot/$num";
54 if (-d $mytape) { rmtree($mytape); }
55 mkpath("$mytape/data");
60 # make up a fake dumpfile_t to write with
61 my $dumpfile = Amanda::Header->new();
62 $dumpfile->{type} = $Amanda::Header::F_DUMPFILE;
63 $dumpfile->{datestamp} = "20070102030405";
64 $dumpfile->{dumplevel} = 0;
65 $dumpfile->{compressed} = 1;
66 $dumpfile->{name} = "localhost";
67 $dumpfile->{disk} = "/home";
68 $dumpfile->{program} = "INSTALLCHECK";
70 my $write_file_count = 5;
72 my ($seed, $length, $filenum) = @_;
74 $dumpfile->{'datestamp'} = "2000010101010$filenum";
76 ok($dev->start_file($dumpfile),
77 "start file $filenum")
78 or diag($dev->error_or_status());
80 is($dev->file(), $filenum,
81 "Device has correct filenum");
83 croak ("selected file size $length is *way* too big")
84 unless ($length < 1024*1024*10);
85 ok(Amanda::Device::write_random_to_device($seed, $length, $dev),
88 if(ok($dev->in_file(),
90 ok($dev->finish_file(),
92 or diag($dev->error_or_status());
94 pass("not in file, so not calling finish_file");
98 my $verify_file_count = 4;
100 my ($seed, $length, $filenum) = @_;
102 ok(my $read_dumpfile = $dev->seek_file($filenum),
103 "seek to file $filenum")
104 or diag($dev->error_or_status());
105 is($dev->file(), $filenum,
106 "device is really at file $filenum");
107 ok(header_for($read_dumpfile, $filenum),
109 or diag($dev->error_or_status());
110 ok(Amanda::Device::verify_random_from_device($seed, $length, $dev),
111 "verified file contents");
115 my ($hdr, $filenum) = @_;
116 return ($hdr and $hdr->{'datestamp'} eq "2000010101010$filenum");
121 my @common_properties = (
127 'medium_access_type',
134 sub properties_include {
135 my ($got, $should_include, $msg) = @_;
136 my %got = map { $_->{'name'}, 1 } @$got;
137 my @missing = grep { !defined($got{$_}) } @$should_include;
140 diag(" Expected properties: " . join(", ", @$should_include));
141 diag(" Got properties: " . join(", ", @$got));
142 diag(" Missing properties: " . join(", ", @missing));
151 $testconf = Installcheck::Config->new();
153 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
154 or die("Could not load configuration");
156 # put the debug messages somewhere
157 Amanda::Debug::dbopen("installcheck");
158 Installcheck::log_test_output();
161 ## Test errors a little bit
163 $dev = Amanda::Device->new("foobar:");
164 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
165 "creation of a bogus 'foobar:' device fails");
167 $dev = Amanda::Device->new("rait:{{");
168 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
169 "creation of a bogus 'rait:{{' device fails");
171 $dev = Amanda::Device->new("rait:{a,b");
172 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
173 "creation of a bogus 'rait:{a,b' device fails");
176 ## first, test out the 'null' device.
180 $dev = Amanda::Device->new($dev_name);
181 is($dev->status(), $DEVICE_STATUS_SUCCESS,
182 "create null device")
183 or diag $dev->error_or_status();
184 ok($dev->start($ACCESS_WRITE, "NULL1", "19780615010203"),
185 "start null device in write mode")
186 or diag $dev->error_or_status();
189 properties_include([ $dev->property_list() ], [ @common_properties ],
190 "necessary properties listed on null device");
191 is($dev->property_get("canonical_name"), "null:",
192 "property_get(canonical_name) on null device");
193 is($dev->property_get("caNONical-name"), "null:",
194 "property_get(caNONical-name) on null device (case, dash-insensitivity)");
195 is_deeply([ $dev->property_get("canonical_name") ],
196 [ "null:", $PROPERTY_SURETY_GOOD, $PROPERTY_SOURCE_DEFAULT ],
197 "extended property_get returns correct surety/source");
198 for my $prop ($dev->property_list()) {
199 next unless $prop->{'name'} eq 'canonical_name';
200 is($prop->{'description'},
201 "The most reliable device name to use to refer to this device.",
202 "property info for canonical name is correct");
204 ok(!$dev->property_get("full_deletion"),
205 "property_get(full_deletion) on null device");
206 is($dev->property_get("comment"), undef,
207 "no comment by default");
208 ok($dev->property_set("comment", "well, that was silly"),
209 "set comment property");
210 is($dev->property_get("comment"), "well, that was silly",
211 "comment correctly stored");
213 # and write a file to it
214 write_file(0xabcde, 1024*256, 1);
216 # (don't finish the device, testing the finalize method's cleanup)
219 ## Now some full device tests
223 $vtape1 = mkvtape(1);
224 $dev_name = "file:$vtape1";
226 $dev = Amanda::Device->new($dev_name);
227 is($dev->status(), $DEVICE_STATUS_SUCCESS,
228 "$dev_name: create successful")
229 or diag($dev->error_or_status());
231 properties_include([ $dev->property_list() ],
232 [ @common_properties, 'max_volume_usage' ],
233 "necessary properties listed on vfs device");
235 # play with properties a little bit
236 ok($dev->property_set("comment", 16),
237 "set an string property to an integer");
239 ok($dev->property_set("comment", 16.0),
240 "set an string property to a float");
242 ok($dev->property_set("comment", "hi mom"),
243 "set an string property to a string");
245 ok($dev->property_set("comment", "32768"),
246 "set an integer property to a simple string");
248 ok($dev->property_set("comment", "32k"),
249 "set an integer property to a string with a unit");
251 ok($dev->property_set("block_size", 32768),
252 "set an integer property to an integer");
254 ok(!($dev->property_set("invalid-property-name", 32768)),
255 "set an invalid-property-name");
258 ok($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED,
259 "initially unlabeled")
260 or diag($dev->error_or_status());
262 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
263 "start in write mode")
264 or diag($dev->error_or_status());
266 ok(!($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED),
267 "not unlabeled anymore")
268 or diag($dev->error_or_status());
270 for (my $i = 1; $i <= 3; $i++) {
271 write_file(0x2FACE, $dev->block_size()*10+17, $i);
275 "finish device after write")
276 or diag($dev->error_or_status());
279 ok(!($dev->status()),
280 "no error, at all, from read_label")
281 or diag($dev->error_or_status());
283 # append one more copy, to test ACCESS_APPEND
285 ok($dev->start($ACCESS_APPEND, undef, undef),
286 "start in append mode")
287 or diag($dev->error_or_status());
289 write_file(0xD0ED0E, $dev->block_size()*4, 4);
292 "finish device after append")
293 or diag($dev->error_or_status());
295 # try reading the third file back, creating a new device
296 # object first, and skipping the read-label step.
299 $dev = Amanda::Device->new($dev_name);
300 is($dev->status(), $DEVICE_STATUS_SUCCESS,
301 "$dev_name: re-create successful")
302 or diag($dev->error_or_status());
304 ok($dev->start($ACCESS_READ, undef, undef),
305 "start in read mode")
306 or diag($dev->error_or_status());
308 verify_file(0x2FACE, $dev->block_size()*10+17, 3);
311 # try two seek_file's in a row
312 my $hdr = $dev->seek_file(3);
313 is($hdr? $hdr->{'type'} : -1, $Amanda::Header::F_DUMPFILE, "seek_file the first time");
314 $hdr = $dev->seek_file(3);
315 is($hdr? $hdr->{'type'} : -1, $Amanda::Header::F_DUMPFILE, "seek_file the second time");
319 "finish device after read")
320 or diag($dev->error_or_status());
325 or diag($dev->error_or_status());
328 "erase device (again)")
329 or diag($dev->error_or_status());
332 "finish device after erase")
333 or diag($dev->error_or_status());
335 # test monitor_free_space property (testing the monitoring would require a
336 # dedicated partition for the tests - it's not worth it)
338 ok($dev->property_get("monitor_free_space"),
339 "monitor_free_space property is set by default");
341 ok($dev->property_set("monitor_free_space", 0),
342 "monitor_free_space property can be set to false");
344 ok(!$dev->property_get("monitor_free_space"),
345 "monitor_free_space property value 'sticks'");
347 # test the LEOM functionality
350 $dev = Amanda::Device->new($dev_name);
351 is($dev->status(), $DEVICE_STATUS_SUCCESS,
352 "$dev_name: re-create successful")
353 or diag($dev->error_or_status());
354 ok($dev->property_set("MAX_VOLUME_USAGE", "512k"),
355 "set MAX_VOLUME_USAGE to test LEOM");
356 ok($dev->property_set("LEOM", 1),
358 ok($dev->property_set("ENFORCE_MAX_VOLUME_USAGE", 0),
359 "set ENFORCE_MAX_VOLUME_USAGE");
361 ok($dev->start($ACCESS_WRITE, 'TESTCONF23', undef),
362 "start in write mode")
363 or diag($dev->error_or_status());
365 ok($dev->start_file($dumpfile),
367 or diag($dev->error_or_status());
369 ok(Amanda::Device::write_random_to_device(0xCAFE, 440*1024, $dev),
370 "write random data into the early-warning zone");
373 "device does not indicates LEOM after writing when ENFORCE_MAX_VOLUME_USAGE is FALSE");
375 ok($dev->finish_file(),
376 "..but a finish_file is allowed to complete")
377 or diag($dev->error_or_status());
380 "finish device after LEOM test")
381 or diag($dev->error_or_status());
384 $dev = Amanda::Device->new($dev_name);
385 is($dev->status(), $DEVICE_STATUS_SUCCESS,
386 "$dev_name: re-create successful")
387 or diag($dev->error_or_status());
388 ok($dev->property_set("MAX_VOLUME_USAGE", "512k"),
389 "set MAX_VOLUME_USAGE to test LEOM");
390 ok($dev->property_set("LEOM", 1),
392 ok($dev->property_set("ENFORCE_MAX_VOLUME_USAGE", 1),
393 "set ENFORCE_MAX_VOLUME_USAGE");
395 ok($dev->start($ACCESS_WRITE, 'TESTCONF23', undef),
396 "start in write mode")
397 or diag($dev->error_or_status());
399 ok($dev->start_file($dumpfile),
401 or diag($dev->error_or_status());
404 "device does not indicate LEOM before writing");
406 ok(Amanda::Device::write_random_to_device(0xCAFE, 440*1024, $dev),
407 "write random data into the early-warning zone");
410 "device indicates LEOM after writing");
412 ok($dev->finish_file(),
413 "..but a finish_file is allowed to complete")
414 or diag($dev->error_or_status());
417 "finish device after LEOM test")
418 or diag($dev->error_or_status());
421 $dev = Amanda::Device->new($dev_name);
422 is($dev->status(), $DEVICE_STATUS_SUCCESS,
423 "$dev_name: re-create successful")
424 or diag($dev->error_or_status());
425 ok($dev->property_set("MAX_VOLUME_USAGE", "512k"),
426 "set MAX_VOLUME_USAGE to test LEOM");
427 ok($dev->property_set("LEOM", 1),
430 ok($dev->start($ACCESS_WRITE, 'TESTCONF23', undef),
431 "start in write mode")
432 or diag($dev->error_or_status());
434 ok($dev->start_file($dumpfile),
436 or diag($dev->error_or_status());
439 "device does not indicate LEOM before writing");
441 ok(Amanda::Device::write_random_to_device(0xCAFE, 440*1024, $dev),
442 "write random data into the early-warning zone");
445 "device indicates LEOM after writing as default value of ENFORCE_MAX_VOLUME_USAGE is true for vfs device");
447 ok($dev->finish_file(),
448 "..but a finish_file is allowed to complete")
449 or diag($dev->error_or_status());
452 "finish device after LEOM test")
453 or diag($dev->error_or_status());
456 $dev = Amanda::Device->new($dev_name);
457 is($dev->status(), $DEVICE_STATUS_SUCCESS,
458 "$dev_name: re-create successful")
459 or diag($dev->error_or_status());
460 ok($dev->property_set("MAX_VOLUME_USAGE", "160k"),
461 "set MAX_VOLUME_USAGE to test LEOM while writing the first header");
462 ok($dev->property_set("LEOM", 1),
465 ok($dev->start($ACCESS_WRITE, 'TESTCONF23', undef),
466 "start in write mode")
467 or diag($dev->error_or_status());
469 ok($dev->start_file($dumpfile),
471 or diag($dev->error_or_status());
474 "device indicates LEOM after writing first header");
476 ok($dev->finish_file(),
477 "..but a finish_file is allowed to complete")
478 or diag($dev->error_or_status());
481 "finish device after LEOM test")
482 or diag($dev->error_or_status());
485 ## Test a RAIT device of two vfs devices.
487 ($vtape1, $vtape2) = (mkvtape(1), mkvtape(2));
488 $dev_name = "rait:file:{$vtape1,$vtape2}";
490 $dev = Amanda::Device->new($dev_name);
491 is($dev->status(), $DEVICE_STATUS_SUCCESS,
492 "$dev_name: create successful")
493 or diag($dev->error_or_status());
495 ok($dev->configure(1), "configure device");
497 properties_include([ $dev->property_list() ], [ @common_properties ],
498 "necessary properties listed on rait device");
500 is($dev->property_get("block_size"), 32768, # (RAIT default)
501 "rait device calculates a default block size correctly");
503 ok($dev->property_set("block_size", 32768*16),
504 "rait device accepts an explicit block size");
506 is($dev->property_get("block_size"), 32768*16,
507 "..and remembers it");
509 ok($dev->property_set("max_volume_usage", 32768*1000),
510 "rait device accepts property MAX_VOLUME_USAGE");
512 is($dev->property_get("max_volume_usage"), 32768*1000,
513 "..and remembers it");
516 ok($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED,
517 "initially unlabeled")
518 or diag($dev->error_or_status());
520 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
521 "start in write mode")
522 or diag($dev->error_or_status());
524 ok(!($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED),
525 "not unlabeled anymore")
526 or diag($dev->error_or_status());
528 for (my $i = 1; $i <= 3; $i++) {
529 write_file(0x2FACE, $dev->block_size()*10+17, $i);
533 "finish device after write")
534 or diag($dev->error_or_status());
537 ok(!($dev->status()),
538 "no error, at all, from read_label")
539 or diag($dev->error_or_status());
541 # append one more copy, to test ACCESS_APPEND
543 ok($dev->start($ACCESS_APPEND, undef, undef),
544 "start in append mode")
545 or diag($dev->error_or_status());
547 write_file(0xD0ED0E, $dev->block_size()*4, 4);
550 "finish device after append")
551 or diag($dev->error_or_status());
553 # try reading the third file back, creating a new device
554 # object first, and skipping the read-label step.
557 $dev = Amanda::Device->new($dev_name);
558 is($dev->status(), $DEVICE_STATUS_SUCCESS,
559 "$dev_name: re-create successful")
560 or diag($dev->error_or_status());
562 ok($dev->start($ACCESS_READ, undef, undef),
563 "start in read mode")
564 or diag($dev->error_or_status());
566 verify_file(0x2FACE, $dev->block_size()*10+17, 3);
569 "finish device after read")
570 or diag($dev->error_or_status());
572 ok($dev->start($ACCESS_READ, undef, undef),
573 "start in read mode after missing volume")
574 or diag($dev->error_or_status());
576 # corrupt the device somehow and hope it keeps working
577 rmtree("$taperoot/1");
579 verify_file(0x2FACE, $dev->block_size()*10+17, 3);
580 verify_file(0xD0ED0E, $dev->block_size()*4, 4);
581 verify_file(0x2FACE, $dev->block_size()*10+17, 2);
584 "finish device read after missing volume")
585 or diag($dev->error_or_status());
587 ok(!($dev->start($ACCESS_WRITE, "TESTCONF29", undef)),
588 "start in write mode fails with missing volume")
589 or diag($dev->error_or_status());
593 $dev_name = "rait:{file:$vtape2,MISSING}";
594 $dev = Amanda::Device->new($dev_name);
596 ok($dev->start($ACCESS_READ, undef, undef),
597 "start in read mode with MISSING")
598 or diag($dev->error_or_status());
600 verify_file(0x2FACE, $dev->block_size()*10+17, 3);
601 verify_file(0xD0ED0E, $dev->block_size()*4, 4);
602 verify_file(0x2FACE, $dev->block_size()*10+17, 2);
605 "finish device read with MISSING")
606 or diag($dev->error_or_status());
608 ok(!($dev->start($ACCESS_WRITE, "TESTCONF29", undef)),
609 "start in write mode fails with MISSING")
610 or diag($dev->error_or_status());
614 $dev = Amanda::Device->new_rait_from_children(
615 Amanda::Device->new("file:$vtape2"), undef);
617 ok(!($dev->start($ACCESS_WRITE, "TESTCONF29", undef)),
618 "start a RAIT device in write mode fails, when created with 'undef'")
619 or diag($dev->error_or_status());
621 # Make two devices with different labels, should get a
622 # message accordingly.
623 ($vtape1, $vtape2) = (mkvtape(1), mkvtape(2));
626 for $dev_name ("file:$vtape1", "file:$vtape2") {
627 my $dev = Amanda::Device->new($dev_name);
628 is($dev->status(), $DEVICE_STATUS_SUCCESS,
629 "$dev_name: Open successful")
630 or diag($dev->error_or_status());
631 ok($dev->start($ACCESS_WRITE, "TESTCONF$n", undef),
632 "wrote label 'TESTCONF$n'");
633 ok($dev->finish(), "finished device");
637 $dev = Amanda::Device->new_rait_from_children(
638 Amanda::Device->new("file:$vtape1"),
639 Amanda::Device->new("file:$vtape2"));
640 is($dev->status(), $DEVICE_STATUS_SUCCESS,
641 "new_rait_from_children: Open successful")
642 or diag($dev->error_or_status());
645 ok($dev->status() & $DEVICE_STATUS_VOLUME_ERROR,
646 "Label mismatch error handled correctly")
647 or diag($dev->error_or_status());
649 # Use some config to set a block size on a child device
650 ($vtape1, $vtape2) = (mkvtape(1), mkvtape(2));
651 $dev_name = "rait:{file:$vtape1,mytape2}";
653 $testconf = Installcheck::Config->new();
654 $testconf->add_device("mytape2", [
655 "tapedev" => "\"file:$vtape2\"",
656 "device_property" => "\"BLOCK_SIZE\" \"64k\""
659 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
660 or die("Could not load configuration");
662 $dev = Amanda::Device->new($dev_name);
663 is($dev->status(), $DEVICE_STATUS_SUCCESS,
664 "$dev_name: create successful")
665 or diag($dev->error_or_status());
667 ok($dev->configure(1), "configure device");
669 is($dev->property_get("block_size"), 65536,
670 "rait device calculates a block size from its children correctly");
672 # Test an S3 device if the proper environment variables are set
673 my $S3_SECRET_KEY = $ENV{'INSTALLCHECK_S3_SECRET_KEY'};
674 my $S3_ACCESS_KEY = $ENV{'INSTALLCHECK_S3_ACCESS_KEY'};
675 my $DEVPAY_SECRET_KEY = $ENV{'INSTALLCHECK_DEVPAY_SECRET_KEY'};
676 my $DEVPAY_ACCESS_KEY = $ENV{'INSTALLCHECK_DEVPAY_ACCESS_KEY'};
677 my $DEVPAY_USER_TOKEN = $ENV{'INSTALLCHECK_DEVPAY_USER_TOKEN'};
679 my $run_s3_tests = defined $S3_SECRET_KEY && defined $S3_ACCESS_KEY;
680 my $run_devpay_tests = defined $DEVPAY_SECRET_KEY &&
681 defined $DEVPAY_ACCESS_KEY && $DEVPAY_USER_TOKEN;
683 my $s3_make_device_count = 7;
684 sub s3_make_device($$) {
685 my ($dev_name, $kind) = @_;
686 $dev = Amanda::Device->new($dev_name);
687 is($dev->status(), $DEVICE_STATUS_SUCCESS,
688 "$dev_name: create successful")
689 or diag($dev->error_or_status());
691 my @s3_props = ( 's3_access_key', 's3_secret_key' );
692 push @s3_props, 's3_user_token' if ($kind eq "devpay");
693 properties_include([ $dev->property_list() ], [ @common_properties, @s3_props ],
694 "necessary properties listed on s3 device");
696 ok($dev->property_set('BLOCK_SIZE', 32768*2),
698 or diag($dev->error_or_status());
700 # might as well save a few cents while testing this property..
701 ok($dev->property_set('S3_STORAGE_CLASS', 'REDUCED_REDUNDANCY'),
703 or diag($dev->error_or_status());
706 # use regular S3 credentials
707 ok($dev->property_set('S3_ACCESS_KEY', $S3_ACCESS_KEY),
709 or diag($dev->error_or_status());
711 ok($dev->property_set('S3_SECRET_KEY', $S3_SECRET_KEY),
713 or diag($dev->error_or_status());
715 pass("(placeholder)");
716 } elsif ($kind eq "devpay") {
717 # use devpay credentials
718 ok($dev->property_set('S3_ACCESS_KEY', $DEVPAY_ACCESS_KEY),
719 "set devpay access key")
720 or diag($dev->error_or_status());
722 ok($dev->property_set('S3_SECRET_KEY', $DEVPAY_SECRET_KEY),
723 "set devpay secret key")
724 or diag($dev->error_or_status());
726 ok($dev->property_set('S3_USER_TOKEN', $DEVPAY_USER_TOKEN),
727 "set devpay user token")
728 or diag($dev->error_or_status());
730 croak("didn't recognize the device kind, so no credentials were set");
738 skip "define \$INSTALLCHECK_S3_{SECRET,ACCESS}_KEY to run S3 tests",
740 1 * $verify_file_count +
741 7 * $write_file_count +
742 13 * $s3_make_device_count
743 unless $run_s3_tests;
746 $dev = Amanda::Device->new($dev_name);
747 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
748 "creating $dev_name fails miserably");
750 $dev_name = "s3:foo";
751 $dev = Amanda::Device->new($dev_name);
753 ok($dev->property_get("full_deletion"),
754 "property_get(full_deletion) on s3 device");
756 ok($dev->property_get("leom"),
757 "property_get(leom) on s3 device");
759 # test parsing of boolean values
760 # (s3 is the only device driver that has a writable boolean property at the
764 {'val' => '1', 'true' => 1},
765 {'val' => '0', 'true' => 0},
766 {'val' => 't', 'true' => 1},
767 {'val' => 'true', 'true' => 1},
768 {'val' => 'f', 'true' => 0},
769 {'val' => 'false', 'true' => 0},
770 {'val' => 'y', 'true' => 1},
771 {'val' => 'yes', 'true' => 1},
772 {'val' => 'n', 'true' => 0},
773 {'val' => 'no', 'true' => 0},
774 {'val' => 'on', 'true' => 1},
775 {'val' => 'off', 'true' => 0},
776 {'val' => 'oFf', 'true' => 0},
779 foreach my $v (@verbose_vals) {
780 $dev_name = "s3:foo";
781 $dev = Amanda::Device->new($dev_name);
783 $testconf = Installcheck::Config->new();
784 $testconf->add_param("device_property", "\"verbose\" \"$v->{'val'}\"");
786 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
787 or die("Could not load configuration");
789 ok($dev->configure(1),
790 "configured device with verbose set to $v->{'val'}")
791 or diag($dev->error_or_status());
793 my $get_val = $dev->property_get('verbose');
794 # see if truth-iness matches
795 my $expec = $v->{'true'}? "true" : "false";
796 is(!!$dev->property_get('verbose'), !!$v->{'true'},
797 "device_property 'VERBOSE' '$v->{'val'}' => property_get(verbose) returning $expec");
800 # test unparsable property
801 $dev_name = "s3:foo";
802 $dev = Amanda::Device->new($dev_name);
804 $testconf = Installcheck::Config->new();
805 $testconf->add_param("device_property", "\"verbose\" \"foo\"");
807 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF') == $CFGERR_OK
808 or die("Could not load configuration");
810 ok(!$dev->configure(1),
811 "failed to configure device with verbose set to foo");
813 like($dev->error_or_status(), qr/'verbose'/,
814 "error message mentions property name");
816 like($dev->error_or_status(), qr/'foo'/,
817 "error message mentions property value");
819 like($dev->error_or_status(), qr/gboolean/,
820 "error message mentions property type");
822 my $hostname = hostname();
823 $hostname =~ s/\./-/g;
824 $base_name = "$S3_ACCESS_KEY-installcheck-$hostname";
825 $dev_name = "s3:$base_name-s3";
826 $dev = s3_make_device($dev_name, "s3");
828 my $status = $dev->status();
829 # this test appears very liberal, but catches the case where setup_handle fails without
830 # giving false positives
831 ok(($status == $DEVICE_STATUS_SUCCESS) || (($status & $DEVICE_STATUS_VOLUME_UNLABELED) != 0),
832 "status is either OK or possibly unlabeled")
833 or diag($dev->error_or_status());
835 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
836 "start in write mode")
837 or diag($dev->error_or_status());
839 ok(!($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED),
841 or diag($dev->error_or_status());
843 for (my $i = 1; $i <= 3; $i++) {
844 write_file(0x2FACE, $dev->block_size()*10, $i);
848 "finish device after write")
849 or diag($dev->error_or_status());
852 ok(!($dev->status()),
853 "no error, at all, from read_label")
854 or diag($dev->error_or_status());
856 # append one more copy, to test ACCESS_APPEND
858 ok($dev->start($ACCESS_APPEND, undef, undef),
859 "start in append mode")
860 or diag($dev->error_or_status());
862 write_file(0xD0ED0E, $dev->block_size()*10, 4);
865 "finish device after append")
866 or diag($dev->error_or_status());
868 # try reading the third file back
870 ok($dev->start($ACCESS_READ, undef, undef),
871 "start in read mode")
872 or diag($dev->error_or_status());
874 verify_file(0x2FACE, $dev->block_size()*10, 3);
876 # test EOT indications on reading
877 my $hdr = $dev->seek_file(4);
878 is($hdr->{'type'}, $Amanda::Header::F_DUMPFILE,
879 "file 4 has correct type F_DUMPFILE");
881 $hdr = $dev->seek_file(5);
882 is($hdr->{'type'}, $Amanda::Header::F_TAPEEND,
883 "file 5 has correct type F_TAPEEND");
885 $hdr = $dev->seek_file(6);
886 is($hdr, undef, "seek_file returns undef for file 6");
889 "finish device after read")
890 or diag($dev->error_or_status()); # (note: we don't use write_max_size here,
891 # as the maximum for S3 is very large)
895 or diag($dev->error_or_status());
898 "erase device (again)")
899 or diag($dev->error_or_status());
902 "finish device after erase")
903 or diag($dev->error_or_status());
906 $status = $dev->status();
907 ok($status & $DEVICE_STATUS_VOLUME_UNLABELED,
908 "status is unlabeled after an erase")
909 or diag($dev->error_or_status());
911 $dev = s3_make_device($dev_name, "s3");
914 "erase device right after creation")
915 or diag($dev->error_or_status());
917 $dev = s3_make_device($dev_name, "s3");
919 # set MAX_VOLUME_USAGE, LEOM=true, ENFORCE_MAX_VOLUME_USAGE=false
920 ok($dev->property_set('MAX_VOLUME_USAGE', "512k"),
921 "set MAX_VOLUME_USAGE to test LEOM");
923 ok($dev->property_set("LEOM", 1),
926 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
927 "start in write mode")
928 or diag($dev->error_or_status());
930 write_file(0x2FACE, 440*1024, 1);
933 "device does not indicate LEOM after writing as property ENFORCE_MAX_VOLUME_USAGE not set and its default value is false");
936 "finish device after LEOM test")
937 or diag($dev->error_or_status());
941 or diag($dev->error_or_status());
943 $dev = s3_make_device($dev_name, "s3");
945 # set MAX_VOLUME_USAGE, LEOM=true, ENFORCE_MAX_VOLUME_USAGE=true
946 ok($dev->property_set('MAX_VOLUME_USAGE', "512k"),
947 "set MAX_VOLUME_USAGE to test LEOM");
949 ok($dev->property_set('ENFORCE_MAX_VOLUME_USAGE', 1 ),
950 "set ENFORCE_MAX_VOLUME_USAGE");
952 ok($dev->property_set("LEOM", 1),
955 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
956 "start in write mode")
957 or diag($dev->error_or_status());
959 write_file(0x2FACE, 440*1024, 1);
962 "device indicates LEOM after writing, when property ENFORCE_MAX_VOLUME_USAGE set to true");
965 "finish device after LEOM test")
966 or diag($dev->error_or_status());
970 or diag($dev->error_or_status());
972 $dev = s3_make_device($dev_name, "s3");
974 # set MAX_VOLUME_USAGE, LEOM=true, ENFORCE_MAX_VOLUME_USAGE=false
975 ok($dev->property_set('MAX_VOLUME_USAGE', "512k"),
976 "set MAX_VOLUME_USAGE to test LEOM");
978 ok($dev->property_set('ENFORCE_MAX_VOLUME_USAGE', 0 ),
979 "set ENFORCE_MAX_VOLUME_USAGE");
981 ok($dev->property_set("LEOM", 1),
984 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
985 "start in write mode")
986 or diag($dev->error_or_status());
988 write_file(0x2FACE, 440*1024, 1);
991 "device does not indicate LEOM after writing, when property ENFORCE_MAX_VOLUME_USAGE set to false");
994 "finish device after LEOM test")
995 or diag($dev->error_or_status());
999 or diag($dev->error_or_status());
1001 # try with empty user token
1002 $dev_name = lc("s3:$base_name-s3");
1003 $dev = s3_make_device($dev_name, "s3");
1004 ok($dev->property_set('S3_USER_TOKEN', ''),
1005 "set devpay user token")
1006 or diag($dev->error_or_status());
1009 $status = $dev->status();
1010 ok(($status == $DEVICE_STATUS_SUCCESS) || (($status & $DEVICE_STATUS_VOLUME_UNLABELED) != 0),
1011 "status is either OK or possibly unlabeled")
1012 or diag($dev->error_or_status());
1018 or diag($dev->error_or_status());
1020 # try a eu-constrained bucket
1021 $dev_name = lc("s3:$base_name-s3-eu");
1022 $dev = s3_make_device($dev_name, "s3");
1023 ok($dev->property_set('S3_BUCKET_LOCATION', 'EU'),
1024 "set S3 bucket location to 'EU'")
1025 or diag($dev->error_or_status());
1027 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
1028 "start in write mode")
1029 or diag($dev->error_or_status());
1031 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1033 or diag($dev->error_or_status());
1039 or diag($dev->error_or_status());
1041 # try a wildcard-constrained bucket
1042 $dev_name = lc("s3:$base_name-s3-wild");
1043 $dev = s3_make_device($dev_name, "s3");
1044 ok($dev->property_set('S3_BUCKET_LOCATION', '*'),
1045 "set S3 bucket location to ''")
1046 or diag($dev->error_or_status());
1048 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
1049 "start in write mode")
1050 or diag($dev->error_or_status());
1052 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1054 or diag($dev->error_or_status());
1058 # test again with invalid ca_info
1059 $dev = s3_make_device($dev_name, "s3");
1061 skip "SSL not supported; can't check SSL_CA_INFO", 2
1062 unless $dev->property_get('S3_SSL');
1064 ok($dev->property_set('SSL_CA_INFO', '/dev/null'),
1065 "set invalid SSL/TLS CA certificate")
1066 or diag($dev->error_or_status());
1068 ok(!$dev->start($ACCESS_WRITE, "TESTCONF13", undef),
1069 "start in write mode")
1070 or diag($dev->error_or_status());
1072 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
1074 or diag($dev->error_or_status());
1079 # test again with our own CA bundle
1080 $dev = s3_make_device($dev_name, "s3");
1082 skip "SSL not supported; can't check SSL_CA_INFO", 4
1083 unless $dev->property_get('S3_SSL');
1084 ok($dev->property_set('SSL_CA_INFO', "$srcdir/data/aws-bundle.crt"),
1085 "set our own SSL/TLS CA certificate bundle")
1086 or diag($dev->error_or_status());
1090 or diag($dev->error_or_status());
1092 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
1093 "start in write mode")
1094 or diag($dev->error_or_status());
1096 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1098 or diag($dev->error_or_status());
1105 or diag($dev->error_or_status());
1107 # bucket names incompatible with location constraint
1108 $dev_name = "s3:-$base_name-s3-eu";
1109 $dev = s3_make_device($dev_name, "s3");
1111 ok($dev->property_set('S3_BUCKET_LOCATION', ''),
1112 "should be able to set an empty S3 bucket location with an incompatible name")
1113 or diag($dev->error_or_status());
1115 $dev_name = "s3:$base_name-s3.eu";
1116 $dev = s3_make_device($dev_name, "s3");
1118 ok($dev->property_set('S3_BUCKET_LOCATION', ''),
1119 "should be able to set an empty S3 bucket location with an incompatible name")
1120 or diag($dev->error_or_status());
1122 $dev_name = "s3:-$base_name-s3-eu";
1123 $dev = s3_make_device($dev_name, "s3");
1125 ok(!$dev->property_set('S3_BUCKET_LOCATION', 'EU'),
1126 "should not be able to set S3 bucket location with an incompatible name")
1127 or diag($dev->error_or_status());
1129 $dev_name = lc("s3:$base_name-s3-eu");
1130 $dev = s3_make_device($dev_name, "s3");
1131 ok($dev->property_set('S3_BUCKET_LOCATION', 'XYZ'),
1132 "should be able to set S3 bucket location with a compatible name")
1133 or diag($dev->error_or_status());
1135 $status = $dev->status();
1136 ok(($status == $DEVICE_STATUS_DEVICE_ERROR),
1137 "status is DEVICE_STATUS_DEVICE_ERROR")
1138 or diag($dev->error_or_status());
1139 my $error_msg = $dev->error_or_status();
1140 ok(($dev->error_or_status() == "While creating new S3 bucket: The specified location-constraint is not valid (Unknown) (HTTP 400)"),
1141 "invalid location-constraint")
1142 or diag("bad error: " . $dev->error_or_status());
1147 # in this case, most of our code has already been exercised
1148 # just make sure that authentication works as a basic sanity check
1149 skip "skipping abbreviated devpay tests", $s3_make_device_count + 1
1150 unless $run_devpay_tests;
1151 $dev_name = "s3:$base_name-devpay";
1152 $dev = s3_make_device($dev_name, "devpay");
1154 my $status = $dev->status();
1155 # this test appears very liberal, but catches the case where setup_handle fails without
1156 # giving false positives
1157 ok(($status == 0) || (($status & $DEVICE_STATUS_VOLUME_UNLABELED) != 0),
1158 "status is either OK or possibly unlabeled")
1159 or diag($dev->error_or_status());
1162 # Test a tape device if the proper environment variables are set
1163 my $TAPE_DEVICE = $ENV{'INSTALLCHECK_TAPE_DEVICE'};
1164 my $run_tape_tests = defined $TAPE_DEVICE;
1166 skip "define \$INSTALLCHECK_TAPE_DEVICE to run tape tests",
1168 7 * $verify_file_count +
1169 5 * $write_file_count
1170 unless $run_tape_tests;
1172 $dev_name = "tape:$TAPE_DEVICE";
1173 $dev = Amanda::Device->new($dev_name);
1174 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1175 "$dev_name: create successful")
1176 or diag($dev->error_or_status());
1178 my $status = $dev->read_label();
1179 ok(($status == $DEVICE_STATUS_SUCCESS) || (($status & $DEVICE_STATUS_VOLUME_UNLABELED) != 0),
1180 "status is either OK or possibly unlabeled")
1181 or diag($dev->error_or_status());
1183 ok($dev->start($ACCESS_WRITE, "TESTCONF13", undef),
1184 "start in write mode")
1185 or diag($dev->error_or_status());
1187 ok(!($dev->status() & $DEVICE_STATUS_VOLUME_UNLABELED),
1188 "not unlabeled anymore")
1189 or diag($dev->error_or_status());
1191 for (my $i = 1; $i <= 4; $i++) {
1192 write_file(0x2FACE+$i, $dev->block_size()*10+17, $i);
1196 "finish device after write")
1197 or diag($dev->error_or_status());
1200 ok(!($dev->status()),
1201 "no error, at all, from read_label")
1202 or diag($dev->error_or_status());
1204 is($dev->volume_label(), "TESTCONF13",
1205 "read_label reads the correct label")
1206 or diag($dev->error_or_status());
1208 # append one more copy, to test ACCESS_APPEND
1210 # if final_filemarks is 1, then the tape device will use F_NOOP,
1211 # inserting an extra file, and we'll be appending at file number 6.
1212 my $append_fileno = ($dev->property_get("FINAL_FILEMARKS") == 2)? 5:6;
1215 skip "APPEND not supported", $write_file_count + 2
1216 unless $dev->property_get("APPENDABLE");
1218 ok($dev->start($ACCESS_APPEND, undef, undef),
1219 "start in append mode")
1220 or diag($dev->error_or_status());
1222 write_file(0xD0ED0E, $dev->block_size()*4, $append_fileno);
1225 "finish device after append")
1226 or diag($dev->error_or_status());
1229 # try reading the second and third files back, creating a new
1230 # device object first, and skipping the read-label step.
1233 $dev = Amanda::Device->new($dev_name);
1234 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1235 "$dev_name: re-create successful")
1236 or diag($dev->error_or_status());
1238 # use a big read_block_size, checking that it's also settable
1239 # via read_buffer_size
1240 ok($dev->property_set("read_buffer_size", 256*1024),
1241 "can set read_buffer_size");
1242 is($dev->property_get("read_block_size"), 256*1024,
1243 "and its value is reflected in read_block_size");
1244 ok($dev->property_set("read_block_size", 32*1024),
1245 "can set read_block_size");
1247 ok($dev->start($ACCESS_READ, undef, undef),
1248 "start in read mode")
1249 or diag($dev->error_or_status());
1251 # now verify those files in a particular order to trigger all of the
1252 # seeking edge cases
1254 verify_file(0x2FACE+1, $dev->block_size()*10+17, 1);
1255 verify_file(0x2FACE+2, $dev->block_size()*10+17, 2);
1256 verify_file(0x2FACE+4, $dev->block_size()*10+17, 4);
1257 verify_file(0x2FACE+3, $dev->block_size()*10+17, 3);
1258 verify_file(0x2FACE+1, $dev->block_size()*10+17, 1);
1260 # try re-seeking to the same file
1261 ok(header_for($dev->seek_file(2), 2), "seek to file 2 the first time");
1262 verify_file(0x2FACE+2, $dev->block_size()*10+17, 2);
1263 ok(header_for($dev->seek_file(2), 2), "seek to file 2 the third time");
1265 # and seek through the same pattern *without* reading to EOF
1266 ok(header_for($dev->seek_file(1), 1), "seek to file 1");
1267 ok(header_for($dev->seek_file(2), 2), "seek to file 2");
1268 ok(header_for($dev->seek_file(4), 4), "seek to file 4");
1269 ok(header_for($dev->seek_file(3), 3), "seek to file 3");
1270 ok(header_for($dev->seek_file(1), 1), "seek to file 1");
1273 skip "APPEND not supported", $verify_file_count
1274 unless $dev->property_get("APPENDABLE");
1275 verify_file(0xD0ED0E, $dev->block_size()*4, $append_fileno);
1279 "finish device after read")
1280 or diag($dev->error_or_status());
1282 # tickle a regression in improperly closing fd's
1284 "finish device again after read")
1285 or diag($dev->error_or_status());
1287 ok($dev->read_label() == $DEVICE_STATUS_SUCCESS,
1288 "read_label after second finish (used to fail)")
1289 or diag($dev->error_or_status());
1291 # finally, run the device with FSF and BSF set to "no", to test the
1292 # fallback schemes for this condition
1295 $dev = Amanda::Device->new($dev_name);
1296 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1297 "$dev_name: re-create successful")
1298 or diag($dev->error_or_status());
1299 $dev->property_set("fsf", "no");
1300 $dev->property_set("bsf", "no");
1302 ok($dev->start($ACCESS_READ, undef, undef),
1303 "start in read mode")
1304 or diag($dev->error_or_status());
1306 ok(header_for($dev->seek_file(1), 1), "seek to file 1");
1307 ok(header_for($dev->seek_file(4), 4), "seek to file 4");
1308 ok(header_for($dev->seek_file(2), 2), "seek to file 2");
1311 "finish device after read")
1312 or diag($dev->error_or_status());
1316 skip "not built with ndmp and server", 94 unless
1317 Amanda::Util::built_with_component("ndmp") and
1318 Amanda::Util::built_with_component("server");
1321 my $testconf = Installcheck::Config->new();
1324 my $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
1325 if ($cfg_result != $CFGERR_OK) {
1326 my ($level, @errors) = Amanda::Config::config_errors();
1327 die(join "\n", @errors);
1330 my $ndmp = Installcheck::Mock::NdmpServer->new();
1331 my $ndmp_port = $ndmp->{'port'};
1332 my $drive = $ndmp->{'drive'};
1333 pass("started ndmjob in daemon mode");
1335 # set up a header for use below
1336 my $hdr = Amanda::Header->new();
1337 $hdr->{type} = $Amanda::Header::F_DUMPFILE;
1338 $hdr->{datestamp} = "20070102030405";
1339 $hdr->{dumplevel} = 0;
1340 $hdr->{compressed} = 1;
1341 $hdr->{name} = "localhost";
1342 $hdr->{disk} = "/home";
1343 $hdr->{program} = "INSTALLCHECK";
1345 $dev = Amanda::Device->new("ndmp:127.0.0.1:9i1\@foo");
1346 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
1347 "creation of an ndmp device fails with invalid port");
1349 $dev = Amanda::Device->new("ndmp:127.0.0.1:90000\@foo");
1350 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
1351 "creation of an ndmp device fails with too-large port");
1353 $dev = Amanda::Device->new("ndmp:127.0.0.1:$ndmp_port");
1354 isnt($dev->status(), $DEVICE_STATUS_SUCCESS,
1355 "creation of an ndmp device fails without ..\@device_name");
1357 $dev = Amanda::Device->new("ndmp:127.0.0.1:$ndmp_port\@$drive");
1358 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1359 "creation of an ndmp device succeeds with correct syntax");
1361 ok($dev->property_set("ndmp_username", "foo"),
1362 "set ndmp_username property");
1363 is($dev->property_get("ndmp_username"), "foo",
1364 "..and get the value back");
1365 ok($dev->property_set("ndmp_password", "bar"),
1366 "set ndmp_password property");
1367 is($dev->property_get("ndmp_password"), "bar",
1368 "..and get the value back");
1370 ok($dev->property_set("verbose", 1),
1373 # set 'em back to the defaults
1374 $dev->property_set("ndmp_username", "ndmp");
1375 $dev->property_set("ndmp_password", "ndmp");
1377 # use a big read_block_size, checking that it's also settable
1378 # via read_buffer_size
1379 ok($dev->property_set("read_block_size", 256*1024),
1380 "can set read_block_size");
1381 is($dev->property_get("read_block_size"), 256*1024,
1382 "and its value is reflected");
1383 ok($dev->property_set("read_block_size", 64*1024),
1384 "set read_block_size back to something smaller");
1386 # ok, let's fire the thing up
1387 ok($dev->start($ACCESS_WRITE, "TEST1", "20090915000000"),
1388 "start device in write mode")
1389 or diag $dev->error_or_status();
1391 ok($dev->start_file($hdr),
1394 { # write to the file
1395 my $xfer = Amanda::Xfer->new([
1396 Amanda::Xfer::Source::Random->new(32768*21, 0xBEEFEE00),
1397 Amanda::Xfer::Dest::Device->new($dev, 0) ]);
1398 $xfer->start(make_cb(xmsg_cb => sub {
1399 my ($src, $msg, $xfer) = @_;
1400 if ($msg->{'type'} == $XMSG_ERROR) {
1401 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1402 } elsif ($msg->{'type'} == $XMSG_DONE) {
1403 Amanda::MainLoop::quit();
1407 Amanda::MainLoop::run();
1408 pass("wrote 21 blocks");
1413 or diag $dev->error_or_status();
1415 is($dev->read_label(), $DEVICE_STATUS_SUCCESS,
1416 "read label from (same) device")
1417 or diag $dev->error_or_status();
1419 is($dev->volume_label, "TEST1",
1420 "volume label read back correctly");
1422 ## label a device and check the label, but open a new device in between
1425 $dev = Amanda::Device->new("ndmp:127.0.0.1:$ndmp_port\@$drive");
1426 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1427 "creation of an ndmp device succeeds with correct syntax");
1428 $dev->property_set("ndmp_username", "ndmp");
1429 $dev->property_set("ndmp_password", "ndmp");
1430 $dev->property_set("verbose", 1);
1433 ok($dev->start($ACCESS_WRITE, "TEST2", "20090915000000"),
1434 "start device in write mode")
1435 or diag $dev->error_or_status();
1438 or diag $dev->error_or_status();
1440 # Read the label with a new device.
1441 $dev = Amanda::Device->new("ndmp:127.0.0.1:$ndmp_port\@$drive");
1442 is($dev->status(), $DEVICE_STATUS_SUCCESS,
1443 "creation of an ndmp device succeeds with correct syntax");
1444 $dev->property_set("ndmp_username", "ndmp");
1445 $dev->property_set("ndmp_password", "ndmp");
1446 $dev->property_set("verbose", 1);
1449 is($dev->read_label(), $DEVICE_STATUS_SUCCESS,
1450 "read label from device")
1451 or diag $dev->error_or_status();
1452 is($dev->volume_label, "TEST2",
1453 "volume label read back correctly");
1456 or diag $dev->error_or_status();
1459 # test the directtcp-target implementation
1462 ok($dev->directtcp_supported(), "is a directtcp target");
1463 for my $dev_use ('initiator', 'listener') {
1464 my ($xfer, $addrs, $dest_elt);
1465 if ($dev_use eq 'listener') {
1466 $addrs = $dev->listen(1);
1467 ok($addrs, "listen returns successfully") or die($dev->error_or_status());
1469 # set up an xfer to write to the device
1470 $dest_elt = Amanda::Xfer::Dest::DirectTCPConnect->new($addrs);
1472 # set up an xfer to write to the device
1473 $dest_elt = Amanda::Xfer::Dest::DirectTCPListen->new();
1475 $xfer = Amanda::Xfer->new([
1476 Amanda::Xfer::Source::Random->new(32768*34, 0xB00),
1481 $xfer->start(make_cb(xmsg_cb => sub {
1482 my ($src, $msg, $xfer) = @_;
1483 if ($msg->{'type'} == $XMSG_ERROR) {
1484 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1485 } elsif ($msg->{'type'} == $XMSG_DONE) {
1486 Amanda::MainLoop::quit();
1490 # write files from the connection until EOF
1493 my ($finish_connection, $start_device, $write_file_cb);
1496 $finish_connection = make_cb(finish_connection => sub {
1497 if ($dev_use eq 'listener') {
1498 $conn = $dev->accept();
1500 $addrs = $dest_elt->get_addrs();
1501 $conn = $dev->connect(1, $addrs);
1503 Amanda::MainLoop::call_later($start_device);
1507 $start_device = make_cb(start_device => sub {
1508 ok($dev->start($ACCESS_WRITE, "TEST2", "20090915000000"),
1509 "start device in write mode")
1510 or diag $dev->error_or_status();
1512 Amanda::MainLoop::call_later($write_file_cb);
1515 $write_file_cb = make_cb(write_file_cb => sub {
1516 ++$num_files < 20 or die "I seem to be in a loop!";
1518 ok($dev->start_file($hdr), "start file $num_files for writing");
1519 is($dev->file, $num_files, "..file number is correct");
1521 my ($ok, $size) = $dev->write_from_connection(32768*15);
1522 push @messages, sprintf("WRITE-%s-%d-%s-%s",
1523 $ok?"OK":"ERR", $size,
1524 $dev->is_eof()? "EOF":"!eof",
1525 $dev->is_eom()? "EOM":"!eom");
1526 ok($ok, "..write from connection succeeds");
1527 my $eof = $dev->is_eof();
1529 ok($dev->finish_file(), "..finish file after writing");
1532 Amanda::MainLoop::call_later($write_file_cb);
1536 Amanda::MainLoop::call_later($finish_connection);
1537 Amanda::MainLoop::run();
1538 is_deeply([@messages], [
1539 'WRITE-OK-491520-!eof-!eom',
1540 'WRITE-OK-491520-!eof-!eom',
1541 'WRITE-OK-131072-EOF-!eom',
1543 "a sequence of write_from_connection calls works correctly");
1547 if (my $err = $conn->close()) {
1557 ok($dev->directtcp_supported(), "is a directtcp target");
1559 $dev->property_set("indirect", 1);
1561 my $addrs = $dev->listen(1);
1562 ok($addrs, "listen returns successfully") or die($dev->error_or_status());
1564 # fork off to evaluate the indirecttcp addresses and then set up an
1565 # xfer to write to the device
1566 if (POSIX::fork() == 0) {
1567 # allow other process to start listening.
1569 my $nc = $Amanda::Constants::NC;
1570 $nc = $Amanda::Constants::NC6 if !$nc;
1571 $nc = $Amanda::Constants::NETCAT if !$nc;
1572 my $sockresult = `$nc localhost $addrs->[0][1] < /dev/null`;
1574 my @sockresult = map { [ split(/:/, $_) ] } split(/ /, $sockresult);
1575 $addrs = [ map { $_->[1] = 0 + $_->[1]; $_ } @sockresult ];
1577 my $xfer = Amanda::Xfer->new([
1578 Amanda::Xfer::Source::Random->new(32768*34, 0xB00),
1579 Amanda::Xfer::Dest::DirectTCPConnect->new($addrs) ]);
1581 $xfer->start(make_cb(xmsg_cb => sub {
1582 my ($src, $msg, $xfer) = @_;
1583 if ($msg->{'type'} == $XMSG_ERROR) {
1584 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1585 } elsif ($msg->{'type'} == $XMSG_DONE) {
1586 Amanda::MainLoop::quit();
1590 Amanda::MainLoop::run();
1594 # write files from the connection until EOF
1598 my ($call_accept, $start_device, $write_file_cb);
1600 $call_accept = make_cb(call_accept => sub {
1601 $conn = $dev->accept();
1602 Amanda::MainLoop::call_later($start_device);
1605 $start_device = make_cb(start_device => sub {
1606 ok($dev->start($ACCESS_WRITE, "TEST2", "20090915000000"),
1607 "start device in write mode")
1608 or diag $dev->error_or_status();
1610 Amanda::MainLoop::call_later($write_file_cb);
1613 $write_file_cb = make_cb(write_file_cb => sub {
1614 ++$num_files < 20 or die "I seem to be in a loop!";
1616 ok($dev->start_file($hdr), "start file $num_files for writing");
1617 is($dev->file, $num_files, "..file number is correct");
1619 my ($ok, $size) = $dev->write_from_connection(32768*15);
1620 push @messages, sprintf("WRITE-%s-%d-%s-%s",
1621 $ok?"OK":"ERR", $size,
1622 $dev->is_eof()? "EOF":"!eof",
1623 $dev->is_eom()? "EOM":"!eom");
1624 ok($ok, "..write from connection succeeds");
1625 my $eof = $dev->is_eof();
1627 ok($dev->finish_file(), "..finish file after writing");
1630 Amanda::MainLoop::call_later($write_file_cb);
1632 Amanda::MainLoop::quit();
1636 Amanda::MainLoop::call_later($call_accept);
1637 Amanda::MainLoop::run();
1638 is_deeply([@messages], [
1639 'WRITE-OK-491520-!eof-!eom',
1640 'WRITE-OK-491520-!eof-!eom',
1641 'WRITE-OK-131072-EOF-!eom',
1643 "a sequence of write_from_connection calls works correctly");
1647 if (my $err = $conn->close()) {
1652 # now try reading that back piece by piece
1655 my $filename = "$Installcheck::TMP/Amanda_Device_ndmp.tmp";
1656 open(my $dest_fh, ">", $filename);
1658 ok($dev->start($ACCESS_READ, undef, undef),
1659 "start device in read mode")
1660 or diag $dev->error_or_status();
1663 for ($file = 1; $file <= 3; $file++) {
1664 ok($dev->seek_file($file),
1666 is($dev->file, $file, "..file num is correct");
1667 is($dev->block, 0, "..block num is correct");
1669 # read the file, writing to our temp file. We'll check that the byte
1670 # sequence is correct later
1671 my $xfer = Amanda::Xfer->new([
1672 Amanda::Xfer::Source::Device->new($dev),
1673 Amanda::Xfer::Dest::Fd->new($dest_fh) ]);
1675 $xfer->start(make_cb(xmsg_cb => sub {
1676 my ($src, $msg, $xfer) = @_;
1677 if ($msg->{'type'} == $XMSG_ERROR) {
1678 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1679 } elsif ($msg->{'type'} == $XMSG_DONE) {
1680 Amanda::MainLoop::quit();
1683 Amanda::MainLoop::run();
1685 pass("read back file " . $file);
1691 # now read back and verify that file
1692 open(my $src_fh, "<", $filename);
1693 my $xfer = Amanda::Xfer->new([
1694 Amanda::Xfer::Source::Fd->new($src_fh),
1695 Amanda::Xfer::Dest::Null->new(0xB00) ]);
1697 $xfer->start(make_cb(xmsg_cb => sub {
1698 my ($src, $msg, $xfer) = @_;
1699 if ($msg->{'type'} == $XMSG_ERROR) {
1700 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1701 } elsif ($msg->{'type'} == $XMSG_DONE) {
1702 Amanda::MainLoop::quit();
1705 Amanda::MainLoop::run();
1707 pass("data in the three parts is correct");
1712 # Test read_to_connection
1714 # This requires something that can connect to a device and read from
1715 # it; the XFA does not have an XFER_MECH_DIRECTTCP_CONNECT, so we fake
1716 # it by manually connecting and then setting up an xfer with a regular
1717 # XferSourceFd. This works because the NDMP server will accept an
1718 # incoming connection before the Device API accept() method is called;
1719 # this trick may not work with other DirectTCP-capable devices. Also,
1720 # this doesn't work so well if there's an error in the xfer (e.g., a
1721 # random value mismatch). But tests are supposed to succeed!
1723 sub test_read2conn {
1724 my ($finished_cb) = @_;
1729 my $steps = define_steps
1730 cb_ref => \$finished_cb;
1733 my $addrs = $dev->listen(0);
1735 # now connect to that
1736 $sock = IO::Socket::INET->new(
1738 PeerHost => $addrs->[0][0],
1739 PeerPort => $addrs->[0][1],
1743 # and set up a transfer to read from that socket
1744 my $xfer = Amanda::Xfer->new([
1745 Amanda::Xfer::Source::Fd->new($sock),
1746 Amanda::Xfer::Dest::Null->new(0xB00) ]);
1748 $xfer->start(make_cb(xmsg_cb => sub {
1749 my ($src, $msg, $xfer) = @_;
1750 if ($msg->{'type'} == $XMSG_ERROR) {
1751 die $msg->{'elt'} . " failed: " . $msg->{'message'};
1753 if ($msg->{'type'} == $XMSG_DONE) {
1754 push @events, "DONE";
1755 $steps->{'quit'}->();
1759 $steps->{'accept'}->();
1762 step accept => sub {
1763 $conn = $dev->accept();
1764 die $dev->error_or_status() unless ($conn);
1766 Amanda::MainLoop::call_later($steps->{'start_dev'});
1769 step start_dev => sub {
1770 ok($dev->start($ACCESS_READ, undef, undef),
1771 "start device in read mode")
1772 or diag $dev->error_or_status();
1774 Amanda::MainLoop::call_later($steps->{'read_part_cb'});
1777 step read_part_cb => sub {
1778 my $hdr = $dev->seek_file($file);
1779 die $dev->error_or_status() unless ($hdr);
1780 my $size = $dev->read_to_connection(0);
1781 push @events, "READ-$size";
1784 Amanda::MainLoop::call_later($steps->{'read_part_cb'});
1786 # close the connection, which will end the xfer, which will
1787 # result in a call to finished_cb. So there.
1788 push @events, "CLOSE";
1794 close $sock or die "close: $!";
1796 is_deeply([@events],
1797 [ "READ-491520", "READ-491520", "READ-131072", "CLOSE", "DONE" ],
1798 "sequential read_to_connection operations read the right amounts " .
1799 "and bytestream matches");
1804 test_read2conn(\&Amanda::MainLoop::quit);
1805 Amanda::MainLoop::run();
1807 # try two seek_file's in a row
1808 $hdr = $dev->seek_file(2);
1809 is($hdr? $hdr->{'type'} : -1, $Amanda::Header::F_DUMPFILE, "seek_file the first time");
1810 $hdr = $dev->seek_file(2);
1811 is($hdr? $hdr->{'type'} : -1, $Amanda::Header::F_DUMPFILE, "seek_file the second time");
1813 ## test seek_file's handling of EOM
1815 $hdr = $dev->seek_file(3);
1816 is($hdr->{type}, $Amanda::Header::F_DUMPFILE, "file 3 is a dumpfile");
1817 $hdr = $dev->seek_file(4);
1818 is($hdr->{type}, $Amanda::Header::F_TAPEEND, "file 4 is tapeend");
1819 $hdr = $dev->seek_file(5);
1820 is($hdr, undef, "file 5 is an error");
1821 $hdr = $dev->seek_file(6);
1822 is($hdr, undef, "file 6 is an error");
1826 unlink($input_filename);
1827 unlink($output_filename);