Imported Upstream version 3.2.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 # Interactive package
45
46 package Amanda::Interactive::Installcheck;
47 use vars qw( @ISA );
48 @ISA = qw( Amanda::Interactive );
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{'finished_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
216     step start => sub {
217         $scan = Amanda::Recovery::Scan->new(chg => $chg);
218         $steps->{'find_04'}->();
219     };
220
221     step find_04 => sub {
222         $scan->find_volume(label  => "TESTCONF04",
223                            res_cb => $steps->{'res_cb_04'});
224     };
225
226     step res_cb_04 => sub {
227         my ($err, $res) = @_;
228
229         ok(!$res, "$chg_name didn't find TESTCONF04");
230         ok($err->notfound, "$chg_name: TESTCONF04 error is notfound");
231
232         $scan->find_volume(label  => "TESTCONF02",
233                            res_cb => $steps->{'res_cb_02'});
234     };
235
236     step res_cb_02 => sub {
237         (my $err, $res02) = @_;
238
239         ok(!$err, "$chg_name found TESTCONF02");
240         ok($res02, "$chg_name: TESTCONF02 give a reservation");
241
242         $scan->find_volume(label  => "TESTCONF02",
243                            res_cb => $steps->{'res_cb_02_volinuse'});
244     };
245
246     step res_cb_02_volinuse => sub {
247         my ($err, $res) = @_;
248
249         ok(!$res, "$chg_name doesn't reserve an already reserved slot");
250         if ($chg_name eq "compat-changer" ||
251             $chg_name eq "single-changer") {
252             ok($err->driveinuse, "$chg_name: TESTCONF02 is driveinuse") ||
253                     diag("$chg_name:".Dumper($err));
254         } else {
255             ok($err->volinuse, "$chg_name: TESTCONF02 is volinuse") ||
256                     diag("$chg_name:".Dumper($err));
257         }
258
259         $scan->find_volume(label  => "TESTCONF03",
260                            res_cb => $steps->{'res_cb_03'});
261     };
262
263     step res_cb_03 => sub {
264         (my $err, $res03) = @_;
265
266         if ($chg_name eq "compat-changer" ||
267             $chg_name eq "single-changer") {
268             ok($err, "$chg_name doesn't found TESTCONF03");
269             ok($err->driveinuse, "$chg_name TESTCONF03 is driveinuse") ||
270                 diag($err."\n");
271             ok(!$res03, "$chg_name: TESTCONF03 give no reservation");
272         } else {
273             ok(!$err, "$chg_name found TESTCONF03");
274             ok($res03, "$chg_name: TESTCONF03 give a reservation");
275         }
276         $scan->find_volume(label  => "TESTCONF01",
277                            res_cb => $steps->{'res_cb_01'});
278     };
279
280     step res_cb_01 => sub {
281         (my $err, $res01) = @_;
282
283         if ($chg_name eq "compat-changer" ||
284             $chg_name eq "single-changer") {
285             ok($err, "$chg_name doesn't found TESTCONF01");
286             ok($err->driveinuse, "$chg_name TESTCONF01 is driveinuse") ||
287                 diag($err."\n");
288             ok(!$res01, "$chg_name: TESTCONF01 give no reservation");
289         } else {
290             ok(!$err, "$chg_name found TESTCONF01");
291             ok($res01, "$chg_name: TESTCONF01 give a reservation");
292         }
293         $scan->find_volume(label  => "TESTCONF05",
294                            res_cb => $steps->{'res_cb_05'});
295     };
296
297     step res_cb_05 => sub {
298         my ($err, $res) = @_;
299
300         if ($chg_name eq "compat-changer" ||
301             $chg_name eq "single-changer") {
302             ok($err, "$chg_name doesn't found TESTCONF05");
303             ok($err->driveinuse, "$chg_name TESTCONF05 is driveinuse") ||
304                 diag($err."\n");
305             ok(!$res, "$chg_name: TESTCONF05 give no reservation");
306         } else {
307             ok(!$res, "$chg_name doesn't found TESTCONF05");
308             ok($err->notfound, "$chg_name: TESTCONF05 is notfound");
309         }
310         $scan->find_volume(label  => "TESTCONF01",
311                            res_cb => $steps->{'res_cb_01_volinuse'});
312     };
313
314     step res_cb_01_volinuse => sub {
315         my ($err, $res) = @_;
316
317         ok($err, "$chg_name doesn't found TESTCONF01");
318         if ($chg_name eq "compat-changer" ||
319             $chg_name eq "single-changer") {
320             ok($err->driveinuse, "$chg_name TESTCONF01 is driveinuse") ||
321                 diag($err."\n");
322         } else {
323             ok($err->volinuse, "$chg_name TESTCONF01 is volinuse") ||
324                 diag($err."\n");
325         }
326         ok(!$res, "$chg_name: TESTCONF01 give no reservation");
327         $steps->{'release01'}->();
328     };
329
330     step release01 => sub {
331         if ($res01) {
332             $res01->release(finished_cb => $steps->{'release02'});
333         } else {
334             $steps->{'release02'}->();
335         }
336     };
337
338     step release02 => sub {
339         $res02->release(finished_cb => $steps->{'release03'});
340     };
341
342     step release03 => sub {
343         if ($res03) {
344             $res03->release(finished_cb => $steps->{'done'});
345         } else {
346             $steps->{'done'}->();
347         }
348     };
349
350     step done => sub {
351         pass("done with searching test on $chg_name");
352         $finished_cb->();
353     };
354 }
355
356 foreach my $chg_name ("disk-changer", "multi-changer", "compat-changer",
357                       "single-changer") {
358     # amlabel has to be done outside of Amanda::MainLoop
359     my $chg = Amanda::Changer->new($chg_name);
360     if ($chg_name eq "single-changer") {
361         amlabel_sync($chg, $chg_name, 1, 'TESTCONF02');
362     } else {
363         amlabel_sync($chg, $chg_name, 1, 'TESTCONF01');
364         amlabel_sync($chg, $chg_name, 2, 'TESTCONF02');
365         amlabel_sync($chg, $chg_name, 3, 'TESTCONF03');
366     }
367
368     test_searching($chg, $chg_name, \&Amanda::MainLoop::quit);
369     Amanda::MainLoop::run();
370 }
371
372 #test SCAN_POLL
373 sub test_scan_poll {
374     my ($chg_name, $finished_cb) = @_;
375
376     my $scan;
377     my $chg;
378     my $res04;
379
380     my $steps = define_steps
381         cb_ref => \$finished_cb;
382
383     step start => sub {
384         $chg = Amanda::Changer->new($chg_name);
385         $scan = Amanda::Recovery::Scan->new(chg => $chg);
386         $scan->{'scan_conf'}->{'notfound'} = Amanda::Recovery::Scan::SCAN_POLL;
387         $scan->{'scan_conf'}->{'volinuse'} = Amanda::Recovery::Scan::SCAN_POLL;
388         $scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
389
390         $steps->{'find_04'}->();
391     };
392
393     step find_04 => sub {
394         Amanda::MainLoop::call_after(100, $steps->{'label_04'});
395         $scan->find_volume(label  => "TESTCONF04",
396                            res_cb => $steps->{'res_cb_04'});
397         pass("began searching for TESTCONF04");
398     };
399
400     step label_04 => sub {
401         # this needs to be run on a different process.
402         ok(run('amlabel', '-f', "-otpchanger=$chg_name", 'TESTCONF',
403                'TESTCONF04', 'slot', '3'),
404            "label slot 3 of $chg_name with label TESTCONF04");
405         # note: nothing to do in the amlabel callback
406     };
407
408     step res_cb_04 => sub {
409         (my $err, $res04) = @_;
410
411         ok(!$err, "$chg_name found TESTCONF04 after POLL");
412         ok($res04, "$chg_name: TESTCONF04 give a reservation after POLL");
413
414         $res04->release(finished_cb => $steps->{'done'});
415     };
416
417     step done => sub {
418         pass("done with SCAN_POLL on $chg_name");
419         $finished_cb->();
420     };
421 }
422
423 foreach my $chg_name ("disk-changer", "multi-changer") {
424     test_scan_poll($chg_name, \&Amanda::MainLoop::quit);
425     Amanda::MainLoop::run();
426 }
427
428 #test SCAN_ASK_POLL which change the changer.
429 #label TESTCONF05 in multi-changer
430 #start the scan on disk-changer
431 #interactivity module change changer to multi-changer
432 sub test_scan_ask_poll {
433     my ($finished_cb) = @_;
434     my $scan;
435     my $res05;
436
437     my $chg_name = "multi-changer";
438     my $chg = Amanda::Changer->new($chg_name);
439     amlabel_sync($chg, $chg_name, 2, 'TESTCONF05');
440     $chg = Amanda::Changer->new("disk-changer");
441
442     my $steps = define_steps
443         cb_ref => \$finished_cb;
444
445     step start => sub {
446         my $interactive = Amanda::Interactive::Installcheck->new();
447         $scan = Amanda::Recovery::Scan->new(chg =>         $chg,
448                                             interactive => $interactive);
449         $scan->{'scan_conf'}->{'poll_delay'} = 10; # 10 ms
450
451         $steps->{'find_05'}->();
452     };
453
454     step find_05 => sub {
455         $scan->find_volume(label  => "TESTCONF05",
456                            res_cb => $steps->{'res_cb_05'});
457     };
458
459     step res_cb_05 => sub {
460         (my $err, $res05) = @_;
461
462         ok(!$err, "found TESTCONF05 on changer multi");
463         ok($res05, "TESTCONF05 give a reservation after interactive");
464         is($res05->{'chg'}->{'chg_name'}, $chg_name,
465            "found TESTCONF05 on correct changer: $chg_name");
466
467         $res05->release(finished_cb => $steps->{'done'});
468     };
469
470     step done => sub {
471         pass("done with SCAN_ASK_POLL");
472         $finished_cb->();
473     };
474 }
475 test_scan_ask_poll(\&Amanda::MainLoop::quit);
476 Amanda::MainLoop::run();
477
478 rmtree($taperoot_disk);
479 rmtree($taperoot_multi);
480 rmtree($taperoot_compat);
481 rmtree($taperoot_single);
482 unlink($changerfile);