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