68fa8811f9e2cf4518eb7c33433632e7727039b0
[debian/amanda] / installcheck / amrestore.pl
1 # Copyright (c) 2008, 2009, 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 => 39;
20 use strict;
21 use warnings;
22
23 use lib "@amperldir@";
24 use Installcheck;
25 use Installcheck::Config;
26 use Installcheck::Mock;
27 use Installcheck::Run qw(run run_get run_get_err run_err
28                 $stderr $stdout $diskname vtape_dir);
29 use Installcheck::Dumpcache;
30 use File::Path qw(rmtree mkpath);
31 use Amanda::Paths;
32 use Amanda::Constants;
33 use Amanda::Holding;
34 use Amanda::Config qw( :init :getconf );
35 use Amanda::Device qw( :constants );
36 use Amanda::Xfer qw( :constants );
37 use Amanda::MainLoop;
38 use Cwd;
39 use Data::Dumper;
40
41 my $testdir = "$Installcheck::TMP/amrestore-installcheck";
42 rmtree($testdir);
43 mkpath($testdir);
44
45 my $origdir = getcwd;
46
47 sub cleandir {
48     for my $filename (<$testdir/*>) {
49         unlink($filename);
50     }
51 }
52
53 like(run_err('amrestore'),
54     qr{Usage:},
55     "'amrestore' gives usage message on stderr");
56
57 # put the debug messages somewhere
58 Amanda::Debug::dbopen("installcheck");
59 Installcheck::log_test_output();
60
61 Installcheck::Dumpcache::load("multi");
62 Installcheck::Run::load_vtape(1);
63 chdir($testdir);
64 my @filenames;
65 my ($orig_size, $comp_size, $comp_best_size, $raw_size, $hdr_size);
66
67 cleandir();
68 like(run_get_err('amrestore', "file:".vtape_dir()),
69     qr{Restoring from tape TESTCONF01 starting with file 1.
70 amrestore: 1: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
71 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
72     "simple amrestore from VFS device");
73
74 @filenames = sort <localhost.*>;
75 is(scalar @filenames, 2, "..and the restored files are present in testdir")
76     or diag(join("\n", @filenames));
77 like($filenames[0], qr/localhost\..*\.[0-9]{14}\.0/,
78     "..first filename looks correct");
79 like($filenames[1], qr/localhost\..*_dir\.[0-9]{14}\.0/,
80     "..second filename looks correct");
81
82 # get the size of the first file for later
83 $orig_size = (stat($filenames[0]))[7];
84
85 cleandir();
86 Installcheck::Run::load_vtape(2);
87 like(run_get_err('amrestore', "file:".vtape_dir()),
88     qr{Restoring from tape TESTCONF02 starting with file 1.
89 amrestore: 1: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 1 comp N program .*
90 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
91     "simple amrestore from VFS device (tape 2)");
92
93 @filenames = sort <localhost.*>;
94 is(scalar @filenames, 2, "..and the restored files are present in testdir")
95     or diag(join("\n", @filenames));
96 like($filenames[0], qr/localhost\..*\.[0-9]{14}\.0/,
97     "..first filename looks correct");
98 like($filenames[1], qr/localhost\..*_dir\.[0-9]{14}\.1/,
99     "..second filename looks correct");
100
101 cleandir();
102 Installcheck::Run::load_vtape(1);
103 like(run_get_err('amrestore', "file:".vtape_dir(), "otherhost"),
104     qr{Restoring from tape TESTCONF01 starting with file 1.
105 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
106 amrestore: 2: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
107     "amrestore with a nonexistent hostname restores nothing");
108
109 cleandir();
110 like(run_get_err('amrestore', "file:".vtape_dir(), "localhost", "/NOSuCH/DIR"),
111     qr{Restoring from tape TESTCONF01 starting with file 1.
112 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
113 amrestore: 2: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
114     "amrestore with a good hostname but non-matching dir restores nothing");
115
116 @filenames = <localhost.*>;
117 is(scalar @filenames, 0, "..and no restored files are present in testdir")
118     or diag(join("\n", @filenames));
119
120 cleandir();
121 like(run_get_err('amrestore', "file:".vtape_dir(), "localhost", "$diskname\$"),
122     qr{Restoring from tape TESTCONF01 starting with file 1.
123 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
124 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
125     "amrestore with a good hostname and matching dir restores one file");
126
127 @filenames = <localhost.*>;
128 is(scalar @filenames, 1, "..and the file is present in testdir")
129     or diag(join("\n", @filenames));
130
131 cleandir();
132 like(run_get_err('amrestore', "-l", "TESTCONF01", "file:".vtape_dir(), "localhost", "$diskname/dir"),
133     qr{Restoring from tape TESTCONF01 starting with file 1.
134 amrestore: 1: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
135 amrestore: 2: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
136     "label checking OK");
137
138 cleandir();
139 ok(run('amrestore', "-b", "16384", "file:".vtape_dir()),
140     "blocksize option accepted (although with no particular effect on a VFS device)");
141
142 cleandir();
143 like(run_get_err('amrestore', "-c", "file:".vtape_dir(), "localhost", "$diskname\$"),
144     qr{Restoring from tape TESTCONF01 starting with file 1.
145 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
146 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
147     "compression works");
148
149 @filenames = <localhost.*>;
150 $comp_size = (stat($filenames[0]))[7];
151 ok($comp_size < $orig_size, "..compressed size is smaller than original");
152
153 cleandir();
154 like(run_get_err('amrestore', "-C", "file:".vtape_dir(), "localhost", "$diskname\$"),
155     qr{Restoring from tape TESTCONF01 starting with file 1.
156 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
157 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
158     "best compression works");
159
160 @filenames = <localhost.*>;
161 $comp_best_size = (stat($filenames[0]))[7];
162 ok($comp_best_size < $comp_size, "..compressed best size is smaller than compressed fast");
163
164 cleandir();
165 like(run_get_err('amrestore', "-f", "2", "file:".vtape_dir()),
166     qr{Restoring from tape TESTCONF01 starting with file 2.},
167     "starting at filenum 2 gets no dumps");
168
169 ok(run_err('amrestore', "-c", "-r", "file:".vtape_dir()),
170     "-c and -r conflict");
171
172 cleandir();
173 like(run_get_err('amrestore', "-r", "file:".vtape_dir(), "localhost", "$diskname\$"),
174     qr{Restoring from tape TESTCONF01 starting with file 1.
175 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
176 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
177     "raw restore");
178
179 @filenames = <localhost.*>;
180 is(scalar @filenames, 1, "..and restored file is present in testdir")
181     or diag(join("\n", @filenames));
182
183 # get the size of the file for later
184 $raw_size = (stat($filenames[0]))[7];
185
186 is($raw_size, $orig_size + Amanda::Holding::DISK_BLOCK_BYTES,
187     "raw dump is orig_size + header size");
188
189 cleandir();
190 like(run_get_err('amrestore', "-h", "file:".vtape_dir(), "localhost", "$diskname\$"),
191     qr{Restoring from tape TESTCONF01 starting with file 1.
192 amrestore: 1: skipping split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 0 comp N program .*
193 amrestore: 2: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E part 1/UNKNOWN lev 0 comp N program .*},
194     "header (-h) restore");
195
196 @filenames = <localhost.*>;
197 is(scalar @filenames, 1, "..and restored file is present in testdir")
198     or diag(join("\n", @filenames));
199
200 # get the size of the file for later
201 $hdr_size = (stat($filenames[0]))[7];
202
203 is($hdr_size, $orig_size + Amanda::Holding::DISK_BLOCK_BYTES,
204     "header (-h) dump is orig_size + header size");
205
206 # find the holding files
207 config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
208 @filenames = sort(+Amanda::Holding::files());
209 is(scalar @filenames, 2, "two holding files found") or die("holding is not what I thought");
210 my $holding_filename = $filenames[0];
211
212 cleandir();
213 like(run_get_err('amrestore', $holding_filename),
214     qr{Reading from '\Q$holding_filename\E'
215 FILE: date [0-9]* host localhost disk \Q$diskname\E lev 1 comp N program .*},
216     "simple amrestore from holding disk");
217
218 @filenames = <localhost.*>;
219 is(scalar @filenames, 1, "..and restored file is present in testdir")
220     or diag(join("\n", @filenames));
221
222 cleandir();
223 Installcheck::Run::load_vtape(2);
224 like(run_get_err('amrestore', "-h", "-p", "file:".vtape_dir(), "localhost", "$diskname/dir"),
225     qr{Restoring from tape TESTCONF02 starting with file 1.
226 amrestore: 1: restoring split dumpfile: date [0-9]* host localhost disk \Q$diskname\E/dir part 1/UNKNOWN lev 1 comp N program .*},
227     "piped amrestore");
228 # (note that amrestore does not go on to the next part; it used to, but would either skip or
229 # give an error for every subsequent file)
230
231 @filenames = <localhost.*>;
232 is(scalar @filenames, 0, "..leaves no files in current dir")
233     or diag(join("\n", @filenames));
234
235 like($stdout, qr/AMANDA: /, "..and stdout contains something with a header");
236
237 ####
238 # For DirectTCP, we write a dumpfile to disk manually with the NDMP device, and
239 # then use amrestore to get it.
240
241 SKIP: {
242     skip "not built with ndmp and server", 5 unless
243         Amanda::Util::built_with_component("ndmp") and Amanda::Util::built_with_component("server");
244
245     my $ndmp = Installcheck::Mock::NdmpServer->new();
246     my $port = $ndmp->{'port'};
247     my $drive = $ndmp->{'drive'};
248
249     # set up a header for use below
250     my $hdr = Amanda::Header->new();
251     $hdr->{type} = $Amanda::Header::F_SPLIT_DUMPFILE;
252     $hdr->{datestamp} = "20091220000000";
253     $hdr->{dumplevel} = 0;
254     $hdr->{compressed} = 0;
255     $hdr->{comp_suffix} = 'N';
256     $hdr->{name} = "localhost";
257     $hdr->{disk} = "/home";
258     $hdr->{program} = "INSTALLCHECK";
259
260     my $device_name = "ndmp:127.0.0.1:$port\@$drive";
261     my $dev = Amanda::Device->new($device_name);
262     ($dev->status() == $DEVICE_STATUS_SUCCESS)
263         or die "creation of an ndmp device failed: " . $dev->error_or_status();
264
265     $dev->start($ACCESS_WRITE, "TEST-17", "20091220000000")
266         or die "could not start NDMP device in write mode: " . $dev->error_or_status();
267
268     $dev->start_file($hdr),
269         or die "could not start_file: " . $dev->error_or_status();
270
271     {   # write to the file
272         my $xfer = Amanda::Xfer->new([
273                 Amanda::Xfer::Source::Random->new(32768*40+280, 0xEEEEE),
274                 Amanda::Xfer::Dest::Device->new($dev, 0) ]);
275         $xfer->start(make_cb(xmsg_cb => sub {
276             my ($src, $msg, $xfer) = @_;
277             if ($msg->{'type'} == $XMSG_ERROR) {
278                 die $msg->{'elt'} . " failed: " . $msg->{'message'};
279             } elsif ($msg->{'type'} == $XMSG_DONE) {
280                 Amanda::MainLoop::quit();
281             }
282         }));
283
284         Amanda::MainLoop::run();
285     }
286
287     $dev->finish()
288         or die "could not finish device: " . $dev->error_or_status();
289     undef $dev;
290
291     pass("wrote a file to an NDMP device");
292
293     cleandir();
294     like(run_get_err('amrestore', $device_name),
295         qr{Restoring from tape TEST-17 starting with file 1.
296 amrestore: 1: restoring split dumpfile: date [0-9]* host localhost disk /home part 1/UNKNOWN lev 0 comp N program .*},
297         "simple amrestore from NDMP device");
298
299     @filenames = sort <localhost.*>;
300     is(scalar @filenames, 1, "..and the restored file is present in testdir")
301         or diag(join("\n", @filenames));
302     like($filenames[0], qr/localhost\..*\.[0-9]{14}\.0/,
303         ".. filename looks correct");
304
305     # get the size of the first file for later
306     is((stat($filenames[0]))[7], 32768*41, # note: last block is padded
307         ".. file length is correct");
308
309     # let's just assume the contents of the file are correct, ok?
310 }
311
312 chdir("$testdir/..");
313 rmtree($testdir);
314 Installcheck::Run::cleanup();