Imported Upstream version 3.1.0
[debian/amanda] / installcheck / Amanda_Recovery_Scan.pl
1 # Copyright (c) 2010 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10 # for more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 #
16 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
18
19 use Test::More tests => 95;
20 use File::Path;
21 use Data::Dumper;
22 use strict;
23
24 use lib "@amperldir@";
25 use Installcheck::Config;
26 use Amanda::Paths;
27 use Amanda::Device qw( :constants );
28 use Amanda::Debug;
29 use Amanda::MainLoop;
30 use Amanda::Config qw( :init :getconf config_dir_relative );
31 use Amanda::Changer;
32 use Amanda::Recovery::Scan;
33 use Installcheck::Run qw(run run_get run_err vtape_dir);
34
35 # set up debugging so debug output doesn't interfere with test results
36 Amanda::Debug::dbopen("installcheck");
37 Installcheck::log_test_output();
38
39 # and disable Debug's die() and warn() overrides
40 Amanda::Debug::disable_die_override();
41
42 # --------
43 # Interactive package
44
45 package Amanda::Interactive::Installcheck;
46 use vars qw( @ISA );
47 @ISA = qw( Amanda::Interactive );
48
49 sub new {
50     my $class = shift;
51     my $self = {};
52     return bless ($self, $class);
53 }
54 sub abort() {};
55 sub user_request {
56     my $self = shift;
57     my %params = @_;
58
59     Amanda::Debug::debug("Change changer to multi-changer");
60     $params{'finished_cb'}->(undef, "multi-changer");
61 };
62
63 # --------
64 # back to the perl tests..
65
66 package main;
67
68 my $testconf = Installcheck::Config->new();
69
70 my $taperoot_disk = "$Installcheck::TMP/Amanda_Recovery_Scan_Disk";
71 #create a disk changer with 3 slots
72 {
73     if (-d $taperoot_disk) {
74         rmtree($taperoot_disk);
75     }
76     mkpath($taperoot_disk);
77
78     for my $slot (1 .. 3) {
79         mkdir("$taperoot_disk/slot$slot")
80             or die("Could not mkdir: $!");
81     }
82
83     $testconf->add_changer("disk-changer", [
84         'tpchanger' => "\"chg-disk:$taperoot_disk\"",
85     ]);
86 }
87
88 my $taperoot_multi = "$Installcheck::TMP/Amanda_Recovery_Scan_Multi";
89 #create a multi changer
90 {
91     if (-d $taperoot_multi) {
92         rmtree($taperoot_multi);
93     }
94     mkpath($taperoot_multi);
95
96     my @names;
97     for my $slot (1 .. 3) {
98         mkdir("$taperoot_multi/slot$slot")
99             or die("Could not mkdir: $!");
100         mkdir("$taperoot_multi/slot$slot/data")
101             or die("Could not mkdir: $!");
102         push @names, $slot;
103     }
104
105     my $chg_name = "chg-multi:file:$taperoot_multi/slot{".join(',', @names)."}";
106     $testconf->add_changer("multi-changer", [
107         'tpchanger' => "\"$chg_name\"",
108         'changerfile' => "\"$Installcheck::TMP/Amanda_Recovery_Scan_Multi_status\"",
109     ]);
110 }
111
112 my $taperoot_compat = "$Installcheck::TMP/Amanda_Recovery_Scan_Compat";
113 my $changerfile = "$Installcheck::TMP/scan-changerfile";
114
115 #create a compat changer
116 {
117     if (-d $taperoot_compat) {
118         rmtree($taperoot_compat);
119     }
120     mkpath($taperoot_compat);
121
122     my @names;
123     for my $slot (1 .. 3) {
124         mkdir("$taperoot_compat/slot$slot")
125             or die("Could not mkdir: $!");
126         #mkdir("$taperoot_compat/slot$slot/data")
127         #    or die("Could not mkdir: $!");
128         push @names, $slot;
129     }
130
131     open (CONF, ">$changerfile");
132     print CONF "firstslot=1\n";
133     print CONF "lastslot=3\n";
134     close CONF;
135
136     $testconf->add_changer("compat-changer", [
137         'tpchanger' => '"chg-disk"',
138         'tapedev'   => "\"file:$taperoot_compat\"",
139         'changerfile' => "\"$changerfile\"",
140     ]);
141 }
142
143 my $taperoot_single = "$Installcheck::TMP/Amanda_Recovery_Scan_Single";
144 #create a single changer
145 {
146     if (-d $taperoot_single) {
147         rmtree($taperoot_single);
148     }
149     mkpath($taperoot_single);
150     mkdir("$taperoot_single/data");
151
152     $testconf->add_changer("single-changer", [
153         'tpchanger' => "\"chg-single:file:$taperoot_single\"",
154     ]);
155 }
156
157 $testconf->write();
158
159 Amanda::Config::config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
160
161 # sub to label a slot in a changer
162 sub amlabel {
163     my ($chg, $chg_name, $slot, $label, $finished_cb) = @_;
164     my $res;
165
166     my $steps = define_steps
167         cb_ref => \$finished_cb;
168
169     step start => sub {
170         $chg->load(slot => $slot, res_cb => $steps->{'res_cb'});
171     };
172
173     step res_cb => sub {
174         (my $err, $res) = @_;
175         die "$err" if $err;
176
177         $res->{'device'}->start($ACCESS_WRITE, $label, "20100201010203");
178         $res->set_label(label => $label, finished_cb => $steps->{'set_label_finished'});
179     };
180
181     step set_label_finished => sub {
182         my ($err) = @_;
183         die "$err" if $err;
184
185         $res->release(finished_cb => $steps->{'finished_cb'});
186     };
187
188     step finished_cb => sub {
189         my ($err) = @_;
190         die "$err" if $err;
191         pass("label slot $slot of $chg_name with label '$label'");
192         $finished_cb->();
193     };
194 }
195
196 sub amlabel_sync {
197     my ($chg, $chg_name, $slot, $label) = @_;
198
199     amlabel($chg, $chg_name, $slot, $label,
200         make_cb(finished_cb => sub { Amanda::MainLoop::quit(); }));
201     Amanda::MainLoop::run();
202 }
203
204 # searching tests
205 sub test_searching {
206     my ($chg, $chg_name, $finished_cb) = @_;
207     my $scan;
208     my $res01;
209     my $res02;
210     my $res03;
211
212     my $steps = define_steps
213         cb_ref => \$finished_cb;
214
215     step start => sub {
216         $scan = Amanda::Recovery::Scan->new(chg => $chg);
217         $steps->{'find_04'}->();
218     };
219
220     step find_04 => sub {
221         $scan->find_volume(label  => "TESTCONF04",
222                            res_cb => $steps->{'res_cb_04'});
223     };
224
225     step res_cb_04 => sub {
226         my ($err, $res) = @_;
227
228         ok(!$res, "$chg_name didn't find TESTCONF04");
229         ok($err->notfound, "$chg_name: TESTCONF04 error is notfound");
230
231         $scan->find_volume(label  => "TESTCONF02",
232                            res_cb => $steps->{'res_cb_02'});
233     };
234
235     step res_cb_02 => sub {
236         (my $err, $res02) = @_;
237
238         ok(!$err, "$chg_name found TESTCONF02");
239         ok($res02, "$chg_name: TESTCONF02 give a reservation");
240
241         $scan->find_volume(label  => "TESTCONF02",
242                            res_cb => $steps->{'res_cb_02_volinuse'});
243     };
244
245     step res_cb_02_volinuse => sub {
246         my ($err, $res) = @_;
247
248         ok(!$res, "$chg_name doesn't reserve an already reserved slot");
249         if ($chg_name eq "compat-changer" ||
250             $chg_name eq "single-changer") {
251             ok($err->driveinuse, "$chg_name: TESTCONF02 is driveinuse") ||
252                     diag("$chg_name:".Dumper($err));
253         } else {
254             ok($err->volinuse, "$chg_name: TESTCONF02 is volinuse") ||
255                     diag("$chg_name:".Dumper($err));
256         }
257
258         $scan->find_volume(label  => "TESTCONF03",
259                            res_cb => $steps->{'res_cb_03'});
260     };
261
262     step res_cb_03 => sub {
263         (my $err, $res03) = @_;
264
265         if ($chg_name eq "compat-changer" ||
266             $chg_name eq "single-changer") {
267             ok($err, "$chg_name doesn't found TESTCONF03");
268             ok($err->driveinuse, "$chg_name TESTCONF03 is driveinuse") ||
269                 diag($err."\n");
270             ok(!$res03, "$chg_name: TESTCONF03 give no reservation");
271         } else {
272             ok(!$err, "$chg_name found TESTCONF03");
273             ok($res03, "$chg_name: TESTCONF03 give a reservation");
274         }
275         $scan->find_volume(label  => "TESTCONF01",
276                            res_cb => $steps->{'res_cb_01'});
277     };
278
279     step res_cb_01 => sub {
280         (my $err, $res01) = @_;
281
282         if ($chg_name eq "compat-changer" ||
283             $chg_name eq "single-changer") {
284             ok($err, "$chg_name doesn't found TESTCONF01");
285             ok($err->driveinuse, "$chg_name TESTCONF01 is driveinuse") ||
286                 diag($err."\n");
287             ok(!$res01, "$chg_name: TESTCONF01 give no reservation");
288         } else {
289             ok(!$err, "$chg_name found TESTCONF01");
290             ok($res01, "$chg_name: TESTCONF01 give a reservation");
291         }
292         $scan->find_volume(label  => "TESTCONF05",
293                            res_cb => $steps->{'res_cb_05'});
294     };
295
296     step res_cb_05 => sub {
297         my ($err, $res) = @_;
298
299         if ($chg_name eq "compat-changer" ||
300             $chg_name eq "single-changer") {
301             ok($err, "$chg_name doesn't found TESTCONF05");
302             ok($err->driveinuse, "$chg_name TESTCONF05 is driveinuse") ||
303                 diag($err."\n");
304             ok(!$res, "$chg_name: TESTCONF05 give no reservation");
305         } else {
306             ok(!$res, "$chg_name doesn't found TESTCONF05");
307             ok($err->notfound, "$chg_name: TESTCONF05 is notfound");
308         }
309         $scan->find_volume(label  => "TESTCONF01",
310                            res_cb => $steps->{'res_cb_01_volinuse'});
311     };
312
313     step res_cb_01_volinuse => sub {
314         my ($err, $res) = @_;
315
316         ok($err, "$chg_name doesn't found TESTCONF01");
317         if ($chg_name eq "compat-changer" ||
318             $chg_name eq "single-changer") {
319             ok($err->driveinuse, "$chg_name TESTCONF01 is driveinuse") ||
320                 diag($err."\n");
321         } else {
322             ok($err->volinuse, "$chg_name TESTCONF01 is volinuse") ||
323                 diag($err."\n");
324         }
325         ok(!$res, "$chg_name: TESTCONF01 give no reservation");
326         $steps->{'release01'}->();
327     };
328
329     step release01 => sub {
330         if ($res01) {
331             $res01->release(finished_cb => $steps->{'release02'});
332         } else {
333             $steps->{'release02'}->();
334         }
335     };
336
337     step release02 => sub {
338         $res02->release(finished_cb => $steps->{'release03'});
339     };
340
341     step release03 => sub {
342         if ($res03) {
343             $res03->release(finished_cb => $steps->{'done'});
344         } else {
345             $steps->{'done'}->();
346         }
347     };
348
349     step done => sub {
350         pass("done with searching test on $chg_name");
351         $finished_cb->();
352     };
353 }
354
355 foreach my $chg_name ("disk-changer", "multi-changer", "compat-changer",
356                       "single-changer") {
357     # amlabel has to be done outside of Amanda::MainLoop
358     my $chg = Amanda::Changer->new($chg_name);
359     if ($chg_name eq "single-changer") {
360         amlabel_sync($chg, $chg_name, 1, 'TESTCONF02');
361     } else {
362         amlabel_sync($chg, $chg_name, 1, 'TESTCONF01');
363         amlabel_sync($chg, $chg_name, 2, 'TESTCONF02');
364         amlabel_sync($chg, $chg_name, 3, 'TESTCONF03');
365     }
366
367     test_searching($chg, $chg_name, \&Amanda::MainLoop::quit);
368     Amanda::MainLoop::run();
369 }
370
371 #test SCAN_POLL
372 sub test_scan_poll {
373     my ($chg_name, $finished_cb) = @_;
374
375     my $scan;
376     my $chg;
377     my $res04;
378
379     my $steps = define_steps
380         cb_ref => \$finished_cb;
381
382     step start => sub {
383         $chg = Amanda::Changer->new($chg_name);
384         $scan = Amanda::Recovery::Scan->new(chg => $chg);
385         $scan->{'scan_conf'}->{'notfound'} = Amanda::Recovery::Scan::SCAN_POLL;
386         $scan->{'scan_conf'}->{'volinuse'} = Amanda::Recovery::Scan::SCAN_POLL;
387         $scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
388
389         $steps->{'find_04'}->();
390     };
391
392     step find_04 => sub {
393         Amanda::MainLoop::call_after(100, $steps->{'label_04'});
394         $scan->find_volume(label  => "TESTCONF04",
395                            res_cb => $steps->{'res_cb_04'});
396         pass("began searching for TESTCONF04");
397     };
398
399     step label_04 => sub {
400         # this needs to be run on a different process.
401         ok(run('amlabel', '-f', "-otpchanger=$chg_name", 'TESTCONF',
402                'TESTCONF04', 'slot', '3'),
403            "label slot 3 of $chg_name with label TESTCONF04");
404         # note: nothing to do in the amlabel callback
405     };
406
407     step res_cb_04 => sub {
408         (my $err, $res04) = @_;
409
410         ok(!$err, "$chg_name found TESTCONF04 after POLL");
411         ok($res04, "$chg_name: TESTCONF04 give a reservation after POLL");
412
413         $res04->release(finished_cb => $steps->{'done'});
414     };
415
416     step done => sub {
417         pass("done with SCAN_POLL on $chg_name");
418         $finished_cb->();
419     };
420 }
421
422 foreach my $chg_name ("disk-changer", "multi-changer") {
423     test_scan_poll($chg_name, \&Amanda::MainLoop::quit);
424     Amanda::MainLoop::run();
425 }
426
427 #test SCAN_ASK_POLL which change the changer.
428 #label TESTCONF05 in multi-changer
429 #start the scan on disk-changer
430 #interactivity module change changer to multi-changer
431 sub test_scan_ask_poll {
432     my ($finished_cb) = @_;
433     my $scan;
434     my $res05;
435
436     my $chg_name = "multi-changer";
437     my $chg = Amanda::Changer->new($chg_name);
438     amlabel_sync($chg, $chg_name, 2, 'TESTCONF05');
439     $chg = Amanda::Changer->new("disk-changer");
440
441     my $steps = define_steps
442         cb_ref => \$finished_cb;
443
444     step start => sub {
445         my $interactive = Amanda::Interactive::Installcheck->new();
446         $scan = Amanda::Recovery::Scan->new(chg =>         $chg,
447                                             interactive => $interactive);
448         $scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
449
450         $steps->{'find_05'}->();
451     };
452
453     step find_05 => sub {
454         $scan->find_volume(label  => "TESTCONF05",
455                            res_cb => $steps->{'res_cb_05'});
456     };
457
458     step res_cb_05 => sub {
459         (my $err, $res05) = @_;
460
461         ok(!$err, "found TESTCONF05 on changer multi");
462         ok($res05, "TESTCONF05 give a reservation after interactive");
463         is($res05->{'chg'}->{'chg_name'}, $chg_name,
464            "found TESTCONF05 on correct changer: $chg_name");
465
466         $res05->release(finished_cb => $steps->{'done'});
467     };
468
469     step done => sub {
470         pass("done with SCAN_ASK_POLL");
471         $finished_cb->();
472     };
473 }
474 test_scan_ask_poll(\&Amanda::MainLoop::quit);
475 Amanda::MainLoop::run();
476
477 rmtree($taperoot_disk);
478 rmtree($taperoot_multi);
479 rmtree($taperoot_compat);
480 rmtree($taperoot_single);
481 unlink($changerfile);