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