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