X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=installcheck%2FAmanda_Changer_compat.pl;h=fc6d9ad0831054bd3979720bb310ffc77484389a;hb=bd1ae9014cb0c6d1e012ddb8a15d243458cf649c;hp=6e75f7c1acf446ce957fae6a3a2c33d3e26bd572;hpb=3469241adf5f8b45020b0896ee13d17c4c7a2abf;p=debian%2Famanda diff --git a/installcheck/Amanda_Changer_compat.pl b/installcheck/Amanda_Changer_compat.pl index 6e75f7c..fc6d9ad 100644 --- a/installcheck/Amanda_Changer_compat.pl +++ b/installcheck/Amanda_Changer_compat.pl @@ -1,4 +1,4 @@ -# Copyright (c) 2005-2008 Zmanda Inc. All Rights Reserved. +# Copyright (c) 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License version 2 as published @@ -13,32 +13,35 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # -# Contact information: Zmanda Inc, 465 S Mathlida Ave, Suite 300 +# Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com -use Test::More tests => 22; +use Test::More tests => 30; use File::Path; use strict; use warnings; use lib "@amperldir@"; +use Installcheck; use Installcheck::Config; use Installcheck::Run; +use Installcheck::Changer; use Amanda::Paths; use Amanda::Device; -use Amanda::Debug; +use Amanda::Debug qw( :logging ); use Amanda::MainLoop; use Amanda::Config qw( :init :getconf config_dir_relative ); use Amanda::Changer; # set up debugging so debug output doesn't interfere with test results Amanda::Debug::dbopen("installcheck"); +Installcheck::log_test_output(); # and disable Debug's die() and warn() overrides Amanda::Debug::disable_die_override(); -my $changer_filename = "$AMANDA_TMPDIR/chg-test"; -my $result_file = "$AMANDA_TMPDIR/chg-test.result"; +my $changer_filename = "$Installcheck::TMP/chg-test"; +my $result_file = "$Installcheck::TMP/chg-test.result"; # Set up a 'test' changer; several of these are defined below. sub setup_changer { @@ -46,7 +49,7 @@ sub setup_changer { open my $chg_test, ">", $changer_filename or die("Could not create test changer"); - $changer_script =~ s/\$AMANDA_TMPDIR/$AMANDA_TMPDIR/g; + $changer_script =~ s/\$Installcheck::TMP/$Installcheck::TMP/g; print $chg_test "#! /bin/sh\n"; print $chg_test $changer_script; @@ -67,56 +70,75 @@ sub slurp_result { } # Functions to invoke the changer and later verify the result +my ($check_res_cb, $check_finished_cb); { - my $expected_err_re; + my $expected_err_info; my $expected_dev; my $msg; + my $quit; - sub check_res_cb { + $check_res_cb = make_cb('check_res_cb' => sub { my ($err, $res) = @_; - Amanda::MainLoop::quit(); if ($err) { - if (defined($expected_err_re)) { - like($err, $expected_err_re, $msg); + if (defined($expected_err_info)) { + chg_err_like($err, $expected_err_info, $msg); } else { fail($msg); - debug("Unexpected error: $err"); + diag("Unexpected error: $err"); } } else { if (defined($expected_dev)) { - is($res->{'device_name'}, $expected_dev, $msg); + is($res->{'device'}->device_name, $expected_dev, $msg); } else { fail($msg); diag("Unexpected reservation"); } } - } - sub check_finished_cb { - my ($err) = @_; - Amanda::MainLoop::quit(); + if ($res) { + $res->release(finished_cb => $quit); + } else { + $quit->(); + } + }); + + $check_finished_cb = make_cb('check_finished_cb' => sub { + my ($err, $res) = @_; if ($err) { - if (defined($expected_err_re)) { - like($err, $expected_err_re, $msg); + if (defined($expected_err_info)) { + chg_err_like($err, $expected_err_info, $msg); } else { fail($msg); diag("Unexpected error: $err"); } } else { - if (!defined($expected_err_re)) { + if (!defined($expected_err_info)) { pass($msg); } else { fail($msg); diag("Unexpected success"); } } - } + + if ($res) { + $res->release(finished_cb => $quit); + } else { + $quit->(); + } + }); + + $quit = make_cb(quit => sub { + my ($err) = @_; + die $err if $err; + + Amanda::MainLoop::quit(); + }); sub try_run_changer { my $sub; - ($sub, $expected_err_re, $expected_dev, $msg) = @_; + ($sub, $expected_err_info, $expected_dev, $msg) = @_; Amanda::MainLoop::call_later($sub); Amanda::MainLoop::run(); @@ -128,18 +150,22 @@ setup_changer <<'EOC'; case "${1}" in -slot) case "${2}" in - 1) echo "1 fake:1"; exit 0;; + 1) echo "1 null:fake1"; exit 0;; 2) echo " slot 2 is empty"; exit 1;; 3) echo "1"; exit 0;; # test missing 'device' portion + 4) echo "1 bogus:dev"; exit 0;; + 5) echo " multiline error"; echo "line 2"; exit 1;; + current) echo "1 null:current"; exit 0;; + next) echo "1 null:next"; exit 0;; esac;; -reset) - echo "reset" > @AMANDA_TMPDIR@/chg-test.result + echo "reset" > $Installcheck::TMP/chg-test.result echo "reset ignored";; -eject) - echo "eject" > @AMANDA_TMPDIR@/chg-test.result + echo "eject" > $Installcheck::TMP/chg-test.result echo "eject ignored";; -clean) - echo "clean" > @AMANDA_TMPDIR@/chg-test.result + echo "clean" > $Installcheck::TMP/chg-test.result echo "clean ignored";; -label) case "${2}" in @@ -149,7 +175,8 @@ case "${1}" in -info) echo "7 10 1 1"; exit 0;; -search) case "${2}" in - TAPE?01) echo "5 fakedev"; exit 0;; + TAPE?01) echo "5 null:fakedev"; exit 0;; + fatal) echo " game over"; exit 2;; *) echo " not found"; exit 1;; esac;; esac @@ -168,43 +195,101 @@ if ($cfg_result != $CFGERR_OK) { } my $chg = Amanda::Changer->new(); +die($chg) if $chg->isa("Amanda::Changer::Error"); + +try_run_changer( + sub { $chg->load(label => 'TAPE-01', res_cb => $check_res_cb); }, + undef, + "null:fakedev", + "search by label succeeds"); + +try_run_changer( + sub { $chg->load(label => 'TAPE-99', res_cb => $check_res_cb); }, + { message => "not found", type => 'failed', reason => 'notfound' }, + undef, + "search by label; nonexistent tape"); + try_run_changer( - sub { $chg->load(label => 'TAPE-01', res_cb => \&check_res_cb); }, - undef, "fakedev", "search by label"); + sub { $chg->load(slot => '1', res_cb => $check_res_cb); }, + undef, + "null:fake1", + "search by slot"); try_run_changer( - sub { $chg->load(label => 'TAPE-99', res_cb => \&check_res_cb); }, - qr/^not found$/, undef, "search by label; nonexistent tape"); + sub { $chg->load(slot => '2', res_cb => $check_res_cb); }, + { message => "slot 2 is empty", type => 'failed', reason => 'notfound' }, + undef, + "search by slot; empty slot"); try_run_changer( - sub { $chg->load(slot => '1', res_cb => \&check_res_cb); }, - undef, "fake:1", "search by slot"); + sub { $chg->load(slot => '3', res_cb => $check_res_cb); }, + { message => "changer script did not provide a device name", type => 'fatal' }, + undef, + "search by slot; no device in response"); try_run_changer( - sub { $chg->load(slot => '2', res_cb => \&check_res_cb); }, - qr/^slot 2 is empty$/, undef, "search by slot; empty slot"); + sub { $chg->load(slot => '1', res_cb => $check_res_cb); }, + { message => "changer script did not provide a device name", type => 'fatal' }, + undef, + "fatal error is sticky"); -# TODO: what *should* happen here? -#try_run_changer( -# sub { $chg->load(slot => '3', res_cb => \&check_res_cb); }, -# undef, undef, "search by slot; invalid response"); +$chg->{'fatal_error'} = undef; # reset the fatal error try_run_changer( - sub { $chg->eject(finished_cb => \&check_finished_cb); }, + sub { $chg->load(slot => '4', res_cb => $check_res_cb); }, + { message => "opening 'bogus:dev': Device type bogus is not known.", + type => 'failed', + reason => 'device' }, + undef, + "search by slot; bogus device leads to 'failed' error"); + +$chg->{'fatal_error'} = undef; # reset the fatal error + +try_run_changer( + sub { $chg->load(slot => '5', res_cb => $check_res_cb); }, + { message => "multiline error\nline 2", + type => 'failed', + reason => 'notfound' }, + undef, + "multiline error response captured in its entirety"); + +$chg->{'fatal_error'} = undef; # reset the fatal error + +try_run_changer( + sub { $chg->load(label => 'fatal', res_cb => $check_res_cb); }, + { message => "game over", type => 'fatal' }, + undef, + "search by label with fatal error"); + +# reset the fatal error +$chg->{'fatal_error'} = undef; + +try_run_changer( + sub { $chg->eject(finished_cb => $check_finished_cb); }, undef, undef, "chg->eject doesn't fail"); like(slurp_result(), qr/eject/, ".. and calls chg-test -eject"); try_run_changer( - sub { $chg->reset(finished_cb => \&check_finished_cb); }, + sub { $chg->reset(finished_cb => $check_finished_cb); }, undef, undef, "chg->reset doesn't fail"); like(slurp_result(), qr/reset/, ".. and calls chg-test -reset"); try_run_changer( - sub { $chg->clean(finished_cb => \&check_finished_cb); }, + sub { $chg->clean(finished_cb => $check_finished_cb); }, undef, undef, "chg->clean doesn't fail"); like(slurp_result(), qr/clean/, ".. and calls chg-test -clean"); -# TODO test update() +try_run_changer( + sub { $chg->update(finished_cb => $check_finished_cb); }, + undef, undef, "chg->update doesn't fail"); + +try_run_changer( + sub { $chg->inventory(inventory_cb => $check_finished_cb); }, + { message => "'chg-compat:' does not support inventory", + type => 'failed', reason => 'notimpl' }, + undef, + "inventory not implemented"); + # make sure only one reservation can be held at once { @@ -212,11 +297,11 @@ like(slurp_result(), qr/clean/, ".. and calls chg-test -clean"); my ($load_1, $load_2, $check_load_2, $check_eject); - $load_1 = sub { + $load_1 = make_cb('load_1' => sub { $chg->load(slot => 1, res_cb => $load_2); - }; + }); - $load_2 = sub { + $load_2 = make_cb('load_2' => sub { my ($err, $res) = @_; die $err if ($err); @@ -224,18 +309,18 @@ like(slurp_result(), qr/clean/, ".. and calls chg-test -clean"); $first_res = $res; $chg->load(slot => 2, res_cb => $check_load_2); - }; + }); - $check_load_2 = sub { + $check_load_2 = make_cb('check_load_2' => sub { my ($err, $res) = @_; like($err, qr/Changer is already reserved/, "mulitple simultaneous reservations not alowed"); $first_res->release(eject => 1, finished_cb => $check_eject); - }; + }); - $check_eject = sub { + $check_eject = make_cb('check_eject' => sub { my ($err) = @_; ok(!defined $err, "release with eject succeeds"); @@ -243,16 +328,16 @@ like(slurp_result(), qr/clean/, ".. and calls chg-test -clean"); like(slurp_result(), qr/eject/, "..and calls chg-test -eject"); Amanda::MainLoop::quit(); - }; + }); - Amanda::MainLoop::call_later($load_1); + $load_1->(); Amanda::MainLoop::run(); } ## check chg-disk # Installcheck::Run sets up the whole chg-disk thing for us -$testconf = Installcheck::Run->setup(); +$testconf = Installcheck::Run::setup(); $testconf->write(); $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF'); @@ -262,85 +347,193 @@ if ($cfg_result != $CFGERR_OK) { } $chg = Amanda::Changer->new(); +die($chg) if $chg->isa("Amanda::Changer::Error"); { + my $res; my ($get_info, $load_current, $label_current, $load_next, - $release_next, $load_by_label, $check_by_label); + $released1, $release_next, $load_by_label, $check_by_label); - $get_info = sub { - $chg->info(info_cb => $load_current, info => [ 'num_slots' ]); - }; + $get_info = make_cb('get_info' => sub { + $chg->info(info_cb => $load_current, info => [ 'num_slots', 'fast_search' ]); + }); - $load_current = sub { + $load_current = make_cb('load_current' => sub { my $err = shift; my %results = @_; die($err) if defined($err); - is($results{'num_slots'}, 15, "info() returns the correct num_slots"); + is_deeply({ %results }, + { num_slots => 3, fast_search => 0 }, # old chg-disk is not searchable + "info() returns the correct num_slots and fast_search"); $chg->load(slot => "1", res_cb => $label_current); - }; + }); - $label_current = sub { - my ($err, $res) = @_; + $label_current = make_cb('label_current' => sub { + (my $err, $res) = @_; die $err if ($err); pass("seek to current slot succeeded"); - my $dev = Amanda::Device->new($res->{'device_name'}); + my $dev = $res->{'device'}; $dev->start($Amanda::Device::ACCESS_WRITE, "TESTCONF18", undef) or die $dev->error_or_status(); $dev->finish() or die $dev->error_or_status(); is($res->{'this_slot'}, "1", "this slot is '1'"); - is($res->{'next_slot'}, "next", "next slot is 'next'"); $res->set_label(label => "TESTCONF18", finished_cb => $load_next); - }; + }); - $load_next = sub { + $load_next = make_cb('load_next' => sub { my ($err) = @_; die $err if ($err); pass("set_label succeeded"); - $chg->load(slot => "next", res_cb => $release_next); - }; + $res->release(finished_cb => $released1); + }); - $release_next = sub { - my ($err, $res) = @_; + $released1 = make_cb(released1 => sub { + my ($err) = @_; + die $err if $err; + + $chg->load(relative_slot => "next", res_cb => $release_next); + }); + + $release_next = make_cb('release_next' => sub { + (my $err, $res) = @_; die $err if ($err); - pass("load 'next' succeeded"); + pass("load relative slot 'next' succeeded"); $res->release(finished_cb => $load_by_label); - }; + }); - $load_by_label = sub { + $load_by_label = make_cb('load_by_label' => sub { my ($err) = @_; die $err if ($err); pass("release loaded"); $chg->load(label => "TESTCONF18", res_cb => $check_by_label); - }; + }); - $check_by_label = sub { - my ($err, $res) = @_; + $check_by_label = make_cb('check_by_label' => sub { + (my $err, $res) = @_; die $err if ($err); pass("load by label succeeded"); - my $dev = Amanda::Device->new($res->{'device_name'}); + my $dev = $res->{'device'}; $dev->read_label() == 0 or die $dev->error_or_status(); is($dev->volume_label(), "TESTCONF18", "..and finds the right volume"); - Amanda::MainLoop::quit(); - }; + $res->release(finished_cb => sub { + my ($err) = @_; + die $err if $err; - Amanda::MainLoop::call_later($get_info); + Amanda::MainLoop::quit(); + }); + }); + + $get_info->(); Amanda::MainLoop::run(); } + +# test two simultaneous invocations of info() + +$chg = Amanda::Changer->new(); +die($chg) if $chg->isa("Amanda::Changer::Error"); + +sub test_get_infos { + my ($finished_cb) = @_; + my $n_info_results = 0; + + my $steps = define_steps + cb_ref => \$finished_cb; + + step get_infos => sub { + # convince the changer that it has not gotten any info yet + $chg->{'got_info'} = 0; + + $chg->info(info_cb => $steps->{'got_info_result'}, info => [ 'num_slots' ]); + $chg->info(info_cb => $steps->{'got_info_result'}, info => [ 'fast_search' ]); + }; + + step got_info_result => sub { + my ($err, %info) = @_; + die $err if $err; + ++$n_info_results; + if ($n_info_results >= 2) { + pass("two simultaneous info() invocations are successful"); + $finished_cb->(); + } + }; +} +test_get_infos(\&Amanda::MainLoop::quit); +Amanda::MainLoop::run(); + +# scan the changer using except_slots +sub test_except_slots { + my ($finished_cb) = @_; + my $slot; + my %except_slots; + + my $steps = define_steps + cb_ref => \$finished_cb; + + step start => sub { + $chg->load(relative_slot => "current", + except_slots => { %except_slots }, + res_cb => $steps->{'loaded'}); + }; + + step loaded => sub { + my ($err, $res) = @_; + if ($err) { + if ($err->notfound) { + # this means the scan is done + return $steps->{'quit'}->(); + } elsif ($err->volinuse and defined $err->{'slot'}) { + $slot = $err->{'slot'}; + } else { + die $err; + } + } else { + $slot = $res->{'this_slot'}; + } + + $except_slots{$slot} = 1; + + if ($res) { + $res->release(finished_cb => $steps->{'released'}); + } else { + $steps->{'released'}->(); + } + }; + + step released => sub { + my ($err) = @_; + die $err if $err; + + $chg->load(relative_slot => 'next', slot => $slot, + except_slots => { %except_slots }, + res_cb => $steps->{'loaded'}); + }; + + step quit => sub { + is_deeply({ %except_slots }, { 1=>1, 2=>1, 3=>1 }, + "scanning with except_slots works"); + $finished_cb->(); + }; +} +test_except_slots(\&Amanda::MainLoop::quit); +Amanda::MainLoop::run(); + +unlink($changer_filename); +unlink($result_file);