# Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
# Copyright (c) 2013-2016 Carbonite, 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
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Contact information: Carbonite Inc., 756 N Pastoria Ave
# Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
use Test::More tests => 73;
use File::Path;
use Data::Dumper;
use strict;
use warnings;
use lib '@amperldir@';
use Installcheck::Config;
use Amanda::Paths;
use Amanda::Device qw( :constants );
use Amanda::Debug;
use Amanda::MainLoop;
use Amanda::Config qw( :init :getconf config_dir_relative );
use Amanda::Changer;
use Amanda::Recovery::Scan;
use Installcheck::Run qw(run run_get run_err vtape_dir);
# 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();
# --------
# Interactivity package
package Amanda::Interactivity::Installcheck;
use vars qw( @ISA );
@ISA = qw( Amanda::Interactivity );
sub new {
my $class = shift;
my $self = {};
return bless ($self, $class);
}
sub abort() {};
sub user_request {
my $self = shift;
my %params = @_;
Amanda::Debug::debug("Change changer to multi-changer");
$params{'request_cb'}->(undef, "multi-changer");
};
# --------
# back to the perl tests..
package main;
my $testconf = Installcheck::Config->new();
my $taperoot_disk = "$Installcheck::TMP/Amanda_Recovery_Scan_Disk";
#create a disk changer with 3 slots
{
if (-d $taperoot_disk) {
rmtree($taperoot_disk);
}
mkpath($taperoot_disk);
for my $slot (1 .. 3) {
mkdir("$taperoot_disk/slot$slot")
or die("Could not mkdir: $!");
}
$testconf->add_changer("disk-changer", [
'tpchanger' => "\"chg-disk:$taperoot_disk\"",
]);
}
my $taperoot_multi = "$Installcheck::TMP/Amanda_Recovery_Scan_Multi";
#create a multi changer
{
if (-d $taperoot_multi) {
rmtree($taperoot_multi);
}
mkpath($taperoot_multi);
my @names;
for my $slot (1 .. 3) {
mkdir("$taperoot_multi/slot$slot")
or die("Could not mkdir: $!");
mkdir("$taperoot_multi/slot$slot/data")
or die("Could not mkdir: $!");
push @names, $slot;
}
my $chg_name = "chg-multi:file:$taperoot_multi/slot{".join(',', @names)."}";
$testconf->add_changer("multi-changer", [
'tpchanger' => "\"$chg_name\"",
'changerfile' => "\"$Installcheck::TMP/Amanda_Recovery_Scan_Multi_status\"",
]);
}
my $taperoot_single = "$Installcheck::TMP/Amanda_Recovery_Scan_Single";
#create a single changer
{
if (-d $taperoot_single) {
rmtree($taperoot_single);
}
mkpath($taperoot_single);
mkdir("$taperoot_single/data");
$testconf->add_changer("single-changer", [
'tpchanger' => "\"chg-single:file:$taperoot_single\"",
]);
}
$testconf->write();
Amanda::Config::config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
# sub to label a slot in a changer
sub amlabel {
my ($chg, $chg_name, $slot, $label, $finished_cb) = @_;
my $res;
my $steps = define_steps
cb_ref => \$finished_cb;
step start => sub {
$chg->load(slot => $slot, res_cb => $steps->{'res_cb'});
};
step res_cb => sub {
(my $err, $res) = @_;
die "$err" if $err;
$res->{'device'}->start($ACCESS_WRITE, $label, "20100201010203");
$res->set_label(label => $label, finished_cb => $steps->{'set_label_finished'});
};
step set_label_finished => sub {
my ($err) = @_;
die "$err" if $err;
$res->release(finished_cb => $steps->{'finished_cb'});
};
step finished_cb => sub {
my ($err) = @_;
die "$err" if $err;
pass("label slot $slot of $chg_name with label '$label'");
$finished_cb->();
};
}
sub amlabel_sync {
my ($chg, $chg_name, $slot, $label) = @_;
amlabel($chg, $chg_name, $slot, $label,
make_cb(finished_cb => sub { Amanda::MainLoop::quit(); }));
Amanda::MainLoop::run();
}
# searching tests
sub test_searching {
my ($chg, $chg_name, $finished_cb) = @_;
my $scan;
my $res01;
my $res02;
my $res03;
my $steps = define_steps
cb_ref => \$finished_cb,
finalize => sub { $scan->quit() };
step start => sub {
$scan = Amanda::Recovery::Scan->new(chg => $chg);
$steps->{'find_04'}->();
};
step find_04 => sub {
$scan->find_volume(label => "TESTCONF04",
res_cb => $steps->{'res_cb_04'});
};
step res_cb_04 => sub {
my ($err, $res) = @_;
ok(!$res, "$chg_name didn't find TESTCONF04");
ok($err->notfound, "$chg_name: TESTCONF04 error is notfound");
$scan->find_volume(label => "TESTCONF02",
res_cb => $steps->{'res_cb_02'});
};
step res_cb_02 => sub {
(my $err, $res02) = @_;
ok(!$err, "$chg_name found TESTCONF02");
ok($res02, "$chg_name: TESTCONF02 give a reservation");
$scan->find_volume(label => "TESTCONF02",
res_cb => $steps->{'res_cb_02_volinuse'});
};
step res_cb_02_volinuse => sub {
my ($err, $res) = @_;
ok(!$res, "$chg_name doesn't reserve an already reserved slot");
if ($chg_name eq "single-changer") {
ok($err->driveinuse, "$chg_name: TESTCONF02 is driveinuse") ||
diag("$chg_name:".Dumper($err));
} else {
ok($err->volinuse, "$chg_name: TESTCONF02 is volinuse") ||
diag("$chg_name:".Dumper($err));
}
$scan->find_volume(label => "TESTCONF03",
res_cb => $steps->{'res_cb_03'});
};
step res_cb_03 => sub {
(my $err, $res03) = @_;
if ($chg_name eq "single-changer") {
ok($err, "$chg_name doesn't found TESTCONF03");
ok($err->notfound, "$chg_name: TESTCONF03 is notfound");
ok(!$res03, "$chg_name: TESTCONF03 give no reservation");
} else {
ok(!$err, "$chg_name found TESTCONF03");
ok($res03, "$chg_name: TESTCONF03 give a reservation");
}
$scan->find_volume(label => "TESTCONF01",
res_cb => $steps->{'res_cb_01'});
};
step res_cb_01 => sub {
(my $err, $res01) = @_;
if ($chg_name eq "single-changer") {
ok($err, "$chg_name doesn't found TESTCONF01");
ok($err->notfound, "$chg_name: TESTCONF01 is notfound");
ok(!$res01, "$chg_name: TESTCONF01 give no reservation");
} else {
ok(!$err, "$chg_name found TESTCONF01");
ok($res01, "$chg_name: TESTCONF01 give a reservation");
}
$scan->find_volume(label => "TESTCONF05",
res_cb => $steps->{'res_cb_05'});
};
step res_cb_05 => sub {
my ($err, $res) = @_;
if ($chg_name eq "single-changer") {
ok($err, "$chg_name doesn't found TESTCONF05");
ok($err->notfound, "$chg_name: TESTCONF05 is notfound");
ok(!$res, "$chg_name: TESTCONF05 give no reservation");
} else {
ok(!$res, "$chg_name doesn't found TESTCONF05");
ok($err->notfound, "$chg_name: TESTCONF05 is notfound");
}
$scan->find_volume(label => "TESTCONF01",
res_cb => $steps->{'res_cb_01_volinuse'});
};
step res_cb_01_volinuse => sub {
my ($err, $res) = @_;
ok($err, "$chg_name doesn't found TESTCONF01");
if ($chg_name eq "single-changer") {
ok($err->notfound, "$chg_name: TESTCONF01 is notfound");
} else {
ok($err->volinuse, "$chg_name TESTCONF01 is volinuse") ||
diag($err."\n");
}
ok(!$res, "$chg_name: TESTCONF01 give no reservation");
$steps->{'release01'}->();
};
step release01 => sub {
if ($res01) {
$res01->release(finished_cb => $steps->{'release02'});
} else {
$steps->{'release02'}->();
}
};
step release02 => sub {
$res02->release(finished_cb => $steps->{'release03'});
};
step release03 => sub {
if ($res03) {
$res03->release(finished_cb => $steps->{'done'});
} else {
$steps->{'done'}->();
}
};
step done => sub {
pass("done with searching test on $chg_name");
$finished_cb->();
};
}
foreach my $chg_name ("disk-changer", "multi-changer", "single-changer") {
# amlabel has to be done outside of Amanda::MainLoop
my $chg = Amanda::Changer->new($chg_name);
if ($chg_name eq "single-changer") {
amlabel_sync($chg, $chg_name, 1, 'TESTCONF02');
} else {
amlabel_sync($chg, $chg_name, 1, 'TESTCONF01');
amlabel_sync($chg, $chg_name, 2, 'TESTCONF02');
amlabel_sync($chg, $chg_name, 3, 'TESTCONF03');
}
test_searching($chg, $chg_name, \&Amanda::MainLoop::quit);
Amanda::MainLoop::run();
}
#test SCAN_POLL
sub test_scan_poll {
my ($chg_name, $finished_cb) = @_;
my $scan;
my $chg;
my $res04;
my $steps = define_steps
cb_ref => \$finished_cb,
finalize => sub { $scan->quit() };
step start => sub {
$chg = Amanda::Changer->new($chg_name);
$scan = Amanda::Recovery::Scan->new(chg => $chg);
$scan->{'scan_conf'}->{'notfound'} = Amanda::Recovery::Scan::SCAN_POLL;
$scan->{'scan_conf'}->{'volinuse'} = Amanda::Recovery::Scan::SCAN_POLL;
$scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
$steps->{'find_04'}->();
};
step find_04 => sub {
Amanda::MainLoop::call_after(100, $steps->{'label_04'});
$scan->find_volume(label => "TESTCONF04",
res_cb => $steps->{'res_cb_04'});
pass("began searching for TESTCONF04");
};
step label_04 => sub {
# this needs to be run on a different process.
ok(run('amlabel', '-f', "-otpchanger=$chg_name", 'TESTCONF',
'TESTCONF04', 'slot', '3'),
"label slot 3 of $chg_name with label TESTCONF04");
# note: nothing to do in the amlabel callback
};
step res_cb_04 => sub {
(my $err, $res04) = @_;
ok(!$err, "$chg_name found TESTCONF04 after POLL");
ok($res04, "$chg_name: TESTCONF04 give a reservation after POLL");
$res04->release(finished_cb => $steps->{'done'});
};
step done => sub {
pass("done with SCAN_POLL on $chg_name");
$finished_cb->();
};
}
foreach my $chg_name ("disk-changer", "multi-changer") {
test_scan_poll($chg_name, \&Amanda::MainLoop::quit);
Amanda::MainLoop::run();
}
#test SCAN_ASK_POLL which change the changer.
#label TESTCONF05 in multi-changer
#start the scan on disk-changer
#interactivity module change changer to multi-changer
sub test_scan_ask_poll {
my ($finished_cb) = @_;
my $scan;
my $res05;
my $chg_name = "multi-changer";
my $chg = Amanda::Changer->new($chg_name);
amlabel_sync($chg, $chg_name, 2, 'TESTCONF05');
$chg->quit();
$chg = Amanda::Changer->new("disk-changer");
my $steps = define_steps
cb_ref => \$finished_cb,
finalize => sub { $scan->quit() };
step start => sub {
my $interactivity = Amanda::Interactivity::Installcheck->new();
$scan = Amanda::Recovery::Scan->new(chg => $chg,
interactivity => $interactivity);
$scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
$steps->{'find_05'}->();
};
step find_05 => sub {
$scan->find_volume(label => "TESTCONF05",
res_cb => $steps->{'res_cb_05'});
};
step res_cb_05 => sub {
(my $err, $res05) = @_;
ok(!$err, "found TESTCONF05 on changer multi");
ok($res05, "TESTCONF05 give a reservation after interactivity");
is($res05->{'chg'}->{'chg_name'}, $chg_name,
"found TESTCONF05 on correct changer: $chg_name");
$res05->release(finished_cb => $steps->{'done'});
};
step done => sub {
pass("done with SCAN_ASK_POLL");
$finished_cb->();
};
}
test_scan_ask_poll(\&Amanda::MainLoop::quit);
Amanda::MainLoop::run();
rmtree($taperoot_disk);
rmtree($taperoot_multi);
rmtree($taperoot_single);