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