6e75f7c1acf446ce957fae6a3a2c33d3e26bd572
[debian/amanda] / installcheck / Amanda_Changer_compat.pl
1 # Copyright (c) 2005-2008 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 Mathlida Ave, Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
18
19 use Test::More tests => 22;
20 use File::Path;
21 use strict;
22 use warnings;
23
24 use lib "@amperldir@";
25 use Installcheck::Config;
26 use Installcheck::Run;
27 use Amanda::Paths;
28 use Amanda::Device;
29 use Amanda::Debug;
30 use Amanda::MainLoop;
31 use Amanda::Config qw( :init :getconf config_dir_relative );
32 use Amanda::Changer;
33
34 # set up debugging so debug output doesn't interfere with test results
35 Amanda::Debug::dbopen("installcheck");
36
37 # and disable Debug's die() and warn() overrides
38 Amanda::Debug::disable_die_override();
39
40 my $changer_filename = "$AMANDA_TMPDIR/chg-test";
41 my $result_file = "$AMANDA_TMPDIR/chg-test.result";
42
43 # Set up a 'test' changer; several of these are defined below.
44 sub setup_changer {
45     my ($changer_script) = @_;
46
47     open my $chg_test, ">", $changer_filename or die("Could not create test changer");
48
49     $changer_script =~ s/\$AMANDA_TMPDIR/$AMANDA_TMPDIR/g;
50
51     print $chg_test "#! /bin/sh\n";
52     print $chg_test $changer_script;
53
54     close $chg_test;
55     chmod 0755, $changer_filename;
56 }
57
58 # slurp the $result_file
59 sub slurp_result {
60     return '' unless (-r $result_file);
61
62     open(my $fh, "<", $result_file) or die("open $result_file: $!");
63     my $result = do { local $/; <$fh> };
64     close($fh);
65
66     return $result;
67 }
68
69 # Functions to invoke the changer and later verify the result
70 {
71     my $expected_err_re;
72     my $expected_dev;
73     my $msg;
74
75     sub check_res_cb {
76         my ($err, $res) = @_;
77         Amanda::MainLoop::quit();
78
79         if ($err) {
80             if (defined($expected_err_re)) {
81                 like($err, $expected_err_re, $msg);
82             } else {
83                 fail($msg);
84                 debug("Unexpected error: $err");
85             }
86         } else {
87             if (defined($expected_dev)) {
88                 is($res->{'device_name'}, $expected_dev, $msg);
89             } else {
90                 fail($msg);
91                 diag("Unexpected reservation");
92             }
93         }
94     }
95
96     sub check_finished_cb {
97         my ($err) = @_;
98         Amanda::MainLoop::quit();
99
100         if ($err) {
101             if (defined($expected_err_re)) {
102                 like($err, $expected_err_re, $msg);
103             } else {
104                 fail($msg);
105                 diag("Unexpected error: $err");
106             }
107         } else {
108             if (!defined($expected_err_re)) {
109                 pass($msg);
110             } else {
111                 fail($msg);
112                 diag("Unexpected success");
113             }
114         }
115     }
116
117     sub try_run_changer {
118         my $sub;
119         ($sub, $expected_err_re, $expected_dev, $msg) = @_;
120
121         Amanda::MainLoop::call_later($sub);
122         Amanda::MainLoop::run();
123     }
124 }
125
126 # OK, let's get started with some simple stuff
127 setup_changer <<'EOC';
128 case "${1}" in
129     -slot)
130         case "${2}" in
131             1) echo "1 fake:1"; exit 0;;
132             2) echo "<ignored> slot 2 is empty"; exit 1;;
133             3) echo "1"; exit 0;; # test missing 'device' portion
134         esac;;
135     -reset)
136         echo "reset" > @AMANDA_TMPDIR@/chg-test.result
137         echo "reset ignored";;
138     -eject)
139         echo "eject" > @AMANDA_TMPDIR@/chg-test.result
140         echo "eject ignored";;
141     -clean)
142         echo "clean" > @AMANDA_TMPDIR@/chg-test.result
143         echo "clean ignored";;
144     -label)
145         case "${2}" in
146             foo?bar) echo "1 ok"; exit 0;;
147             *) echo "<error> bad label"; exit 1;;
148         esac;;
149     -info) echo "7 10 1 1"; exit 0;;
150     -search)
151         case "${2}" in
152             TAPE?01) echo "5 fakedev"; exit 0;;
153             *) echo "<error> not found"; exit 1;;
154         esac;;
155 esac
156 EOC
157
158 # set up a config for this changer, implicitly using Amanda::Changer::Compat
159 my $testconf;
160 $testconf = Installcheck::Config->new();
161 $testconf->add_param("tpchanger", "\"$changer_filename\"");
162 $testconf->write();
163
164 my $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
165 if ($cfg_result != $CFGERR_OK) {
166     my ($level, @errors) = Amanda::Config::config_errors();
167     die(join "\n", @errors);
168 }
169
170 my $chg = Amanda::Changer->new();
171 try_run_changer(
172     sub { $chg->load(label => 'TAPE-01', res_cb => \&check_res_cb); },
173     undef, "fakedev", "search by label");
174
175 try_run_changer(
176     sub { $chg->load(label => 'TAPE-99', res_cb => \&check_res_cb); },
177     qr/^not found$/, undef, "search by label; nonexistent tape");
178
179 try_run_changer(
180     sub { $chg->load(slot => '1', res_cb => \&check_res_cb); },
181     undef, "fake:1", "search by slot");
182
183 try_run_changer(
184     sub { $chg->load(slot => '2', res_cb => \&check_res_cb); },
185     qr/^slot 2 is empty$/, undef, "search by slot; empty slot");
186
187 # TODO: what *should* happen here?
188 #try_run_changer(
189 #    sub { $chg->load(slot => '3', res_cb => \&check_res_cb); },
190 #    undef, undef, "search by slot; invalid response");
191
192 try_run_changer(
193     sub { $chg->eject(finished_cb => \&check_finished_cb); },
194     undef, undef, "chg->eject doesn't fail");
195 like(slurp_result(), qr/eject/, ".. and calls chg-test -eject");
196
197 try_run_changer(
198     sub { $chg->reset(finished_cb => \&check_finished_cb); },
199     undef, undef, "chg->reset doesn't fail");
200 like(slurp_result(), qr/reset/, ".. and calls chg-test -reset");
201
202 try_run_changer(
203     sub { $chg->clean(finished_cb => \&check_finished_cb); },
204     undef, undef, "chg->clean doesn't fail");
205 like(slurp_result(), qr/clean/, ".. and calls chg-test -clean");
206
207 # TODO test update()
208
209 # make sure only one reservation can be held at once
210 {
211     my $first_res;
212
213     my ($load_1, $load_2, $check_load_2, $check_eject);
214
215     $load_1 = sub {
216         $chg->load(slot => 1, res_cb => $load_2);
217     };
218
219     $load_2 = sub {
220         my ($err, $res) = @_;
221         die $err if ($err);
222
223         # keep this in scope through the next load
224         $first_res = $res;
225
226         $chg->load(slot => 2, res_cb => $check_load_2);
227     };
228
229     $check_load_2 = sub {
230         my ($err, $res) = @_;
231
232         like($err, qr/Changer is already reserved/,
233             "mulitple simultaneous reservations not alowed");
234
235         $first_res->release(eject => 1, finished_cb => $check_eject);
236     };
237
238     $check_eject = sub {
239         my ($err) = @_;
240
241         ok(!defined $err, "release with eject succeeds");
242
243         like(slurp_result(), qr/eject/, "..and calls chg-test -eject");
244
245         Amanda::MainLoop::quit();
246     };
247
248     Amanda::MainLoop::call_later($load_1);
249     Amanda::MainLoop::run();
250 }
251
252 ## check chg-disk
253
254 # Installcheck::Run sets up the whole chg-disk thing for us
255 $testconf = Installcheck::Run->setup();
256 $testconf->write();
257
258 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
259 if ($cfg_result != $CFGERR_OK) {
260     my ($level, @errors) = Amanda::Config::config_errors();
261     die(join "\n", @errors);
262 }
263
264 $chg = Amanda::Changer->new();
265
266 {
267     my ($get_info, $load_current, $label_current, $load_next,
268         $release_next, $load_by_label, $check_by_label);
269
270     $get_info = sub {
271         $chg->info(info_cb => $load_current, info => [ 'num_slots' ]);
272     };
273
274     $load_current = sub {
275         my $err = shift;
276         my %results = @_;
277         die($err) if defined($err);
278
279         is($results{'num_slots'}, 15, "info() returns the correct num_slots");
280
281         $chg->load(slot => "1", res_cb => $label_current);
282     };
283
284     $label_current = sub {
285         my ($err, $res) = @_;
286         die $err if ($err);
287
288         pass("seek to current slot succeeded");
289
290         my $dev = Amanda::Device->new($res->{'device_name'});
291         $dev->start($Amanda::Device::ACCESS_WRITE, "TESTCONF18", undef)
292             or die $dev->error_or_status();
293         $dev->finish()
294             or die $dev->error_or_status();
295
296         is($res->{'this_slot'}, "1", "this slot is '1'");
297         is($res->{'next_slot'}, "next", "next slot is 'next'");
298         $res->set_label(label => "TESTCONF18", finished_cb => $load_next);
299     };
300
301     $load_next = sub {
302         my ($err) = @_;
303         die $err if ($err);
304
305         pass("set_label succeeded");
306
307         $chg->load(slot => "next", res_cb => $release_next);
308     };
309
310     $release_next = sub {
311         my ($err, $res) = @_;
312         die $err if ($err);
313
314         pass("load 'next' succeeded");
315
316         $res->release(finished_cb => $load_by_label);
317     };
318
319     $load_by_label = sub {
320         my ($err) = @_;
321         die $err if ($err);
322
323         pass("release loaded");
324
325         $chg->load(label => "TESTCONF18", res_cb => $check_by_label);
326     };
327
328     $check_by_label = sub {
329         my ($err, $res) = @_;
330         die $err if ($err);
331
332         pass("load by label succeeded");
333
334         my $dev = Amanda::Device->new($res->{'device_name'});
335         $dev->read_label() == 0
336             or die $dev->error_or_status();
337
338         is($dev->volume_label(), "TESTCONF18",
339             "..and finds the right volume");
340
341         Amanda::MainLoop::quit();
342     };
343
344     Amanda::MainLoop::call_later($get_info);
345     Amanda::MainLoop::run();
346 }