# Copyright (c) 2008-2012 Zmanda, Inc. All Rights Reserved.
# Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Contact information: Carbonite Inc., 756 N Pastoria Ave
# Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
use Test::More tests => 33;
use lib '@amperldir@';
use Installcheck;
use Installcheck::Config;
use Installcheck::Run qw(run run_get run_err $diskname);
use Installcheck::Dumpcache;
use File::Path qw(rmtree mkpath);
use Amanda::Paths;
use Amanda::Header;
use Amanda::Debug;
use Cwd;
use warnings;
use strict;
no strict 'subs';
unless ($Installcheck::Run::have_expect) {
SKIP: {
skip("Expect.pm not available", Test::More->builder->expected_tests);
}
exit 0;
}
## NOTE:
#
# Not all features of amfetchdump can be tested without a lot of extra work:
# --header-fd: Expect doesn't pass through nonstandard fd's
# -p: Expect would have to deal with the dumpfile data, which it won't like
my $testconf;
my $dumpok;
my @filenames;
my $exp;
my @results;
my $fok;
my $last_file_size;
my $testdir = "$Installcheck::TMP/amfetchdump-installcheck/files";
rmtree($testdir);
mkpath($testdir);
my $origdir = getcwd;
chdir($testdir);
Amanda::Debug::dbopen("installcheck");
Installcheck::log_test_output();
sub cleandir {
for my $filename (<$testdir/*>) {
unlink($filename);
}
}
sub got_files {
my ($count, $msg) = @_;
my $ok = 1;
my @filenames = <localhost.*>;
# check for .tmp files and empty files
for my $fn (@filenames) {
if ($fn =~ /\.tmp$/ || -z "$testdir/$fn") {
$ok = 0;
}
}
if (scalar @filenames != $count) {
diag("expected $count files");
$ok = 0;
}
# capture file size if there's only one file
if (@filenames == 1) {
$last_file_size = -s $filenames[0];
}
ok($ok, $msg) or diag(`ls -l $testdir`);
}
Installcheck::Dumpcache::load("basic");
like(run_err('amfetchdump', 'TESTCONF'),
qr{^Usage:},
"'amfetchdump TESTCONF' gives usage message on stderr");
like(run_err('amfetchdump', '-b', '65536', 'TESTCONF', 'localhost'),
qr{ERROR: The -b option is no longer},
"-b option gives a warning stderr");
##
# plain vanilla
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading", "restoring", "eof" ],
"simple restore follows the correct steps") || diag(join(' ', @results) . "\n");;
got_files(1, "A ..and restored file is present in testdir");
##
# -a (assume)
cleandir();
ok(run('amfetchdump', '-a', '-l', 'TESTCONF', 'localhost'),
"run with -a and -l successful");
got_files(1, "B ..and restored file is present in testdir ($last_file_size bytes)");
my $uncomp_size = $last_file_size;
##
# -C (should make output file smaller)
cleandir();
ok(run('amfetchdump', '-a', '-C', 'TESTCONF', 'localhost'),
"run with -a and -C successful");
got_files(1, "C ..and restored file is present in testdir");
ok($last_file_size < $uncomp_size,
"..and is smaller than previous run ($last_file_size bytes)");
##
# -O
cleandir();
chdir($Installcheck::TMP);
$exp = Installcheck::Run::run_expect('amfetchdump', '-O', $testdir, 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading", "restoring", "eof" ],
"restore with -O follows the correct steps");
chdir($testdir);
got_files(1, "D ..and restored file is present in testdir");
##
# -h
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', '-h', 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading", "restoring", "eof" ],
"restore with -h follows the correct steps");
$fok = got_files(1, "E ..and restored file is present in testdir");
# check that it starts with a header
if ($fok) {
my @filenames = <localhost.*>;
open(my $fh, "<", $filenames[0]) or die "error opening: $!";
sysread($fh, my $hdr_dat, 32768) or die "error reading: $!";
close($fh);
my $hdr = Amanda::Header->from_string($hdr_dat);
is($hdr->{type}+0, $Amanda::Header::F_SPLIT_DUMPFILE,
"..dumpfile begins with a split dumpfile header");
} else {
fail();
}
##
# --header-file
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', '--header-file', 'hdr',
'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading", "restoring", "eof" ],
"restore with --header-file follows the correct steps");
$fok = got_files(1, "F ..and restored file is present in testdir");
# check that it starts with a header
if ($fok) {
my @filenames = <localhost.*>;
open(my $fh, "<", "$testdir/hdr") or die "error opening: $!";
sysread($fh, my $hdr_dat, 32768) or die "error reading: $!";
close($fh);
my $hdr = Amanda::Header->from_string($hdr_dat);
is($hdr->{type}+0, $Amanda::Header::F_SPLIT_DUMPFILE,
"..and the header file contains the right header");
} else {
fail();
}
##
# -d and prompting for volumes one at a time
cleandir();
my $vfsdev = 'file:' . Installcheck::Run::vtape_dir();
Installcheck::Run::load_vtape(3); # wrong vtape
$exp = Installcheck::Run::run_expect('amfetchdump', '-d', $vfsdev,
'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ qr{Insert (tape|volume) labeled '?TESTCONF01'? in .*\n.*to abort}, sub {
push @results, "insert-tape";
Installcheck::Run::load_vtape(1); # right vtape
$exp->send("\n");
exp_continue;
}, ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter",
"insert-tape", "reading", "restoring", "eof" ],
"restore with an explicit device follows the correct steps, prompting for each");
got_files(1, "G ..and restored file is present in testdir");
##
# -n (using a multipart dump)
Installcheck::Dumpcache::load("parts");
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', '-n', 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading",
("restoring",)x9, "eof" ],
"restore with -n follows the correct steps");
got_files(9, "H ..and restored file is present in testdir");
##
# -l, no options, and -c for compressed dumps
Installcheck::Dumpcache::load("compress");
cleandir();
ok(run('amfetchdump', '-a', 'TESTCONF', 'localhost'),
"run with -a successful (should uncompress)");
got_files(1, "I ..and restored file is present in testdir ($last_file_size bytes)");
$uncomp_size = $last_file_size;
cleandir();
ok(run('amfetchdump', '-a', '-l', 'TESTCONF', 'localhost'),
"run with -a and -l successful (should not uncompress)");
got_files(1, "J ..and restored file is present in testdir");
ok($last_file_size < $uncomp_size,
"..and is smaller than previous run ($last_file_size bytes)");
cleandir();
ok(run('amfetchdump', '-a', '-c', 'TESTCONF', 'localhost'),
"run with -a and -c successful (should not uncompress)");
got_files(1, "K ..and restored file is present in testdir");
ok($last_file_size < $uncomp_size,
"..and is smaller than previous run ($last_file_size bytes)");
Installcheck::Dumpcache::load("multi");
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{2 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01 TESTCONF02.*}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{2 holding file\(s\) needed for restoration}, sub {
push @results, "holding-count";
exp_continue;
} ],
[ qr{Reading .*\nFILE: date [[:digit:]]+ host localhost disk .*},
sub {
push @results, "reading";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "holding-count",
"press-enter", "reading", "reading", "eof" ],
"restore from holding follows the correct steps");
got_files(6, "..and all restored files are present in testdir");
SKIP: {
skip "Expect not installed or not built with ndmp and server", 2 unless
Amanda::Util::built_with_component("ndmp") and
Amanda::Util::built_with_component("server") and
$Installcheck::Run::have_expect;
Installcheck::Dumpcache::load("ndmp");
my $ndmp = Installcheck::Mock::NdmpServer->new(no_reset => 1);
$ndmp->edit_config();
cleandir();
$exp = Installcheck::Run::run_expect('amfetchdump', 'TESTCONF', 'localhost');
$exp->log_stdout(0);
@results = ();
$exp->expect(60,
[ qr{1 (tape|volume)\(s\) needed for restoration}, sub {
push @results, "tape-count";
exp_continue;
} ],
[ qr{The following (tapes|volumes) are needed: TESTCONF01}, sub {
push @results, "tapes-needed";
exp_continue;
} ],
[ qr{Reading label 'TESTCONF01' filenum 1}, sub {
push @results, "reading";
exp_continue;
} ],
[ qr{split dumpfile: date [[:digit:]]+ host localhost disk .*}, sub {
push @results, "restoring";
exp_continue;
} ],
[ 'Press enter when ready', sub {
push @results, "press-enter";
$exp->send("\n");
exp_continue;
}, ],
[ 'eof', sub {
push @results, "eof";
}, ],
);
is_deeply([ @results ], [ "tape-count", "tapes-needed", "press-enter", "reading", "restoring", "eof" ],
"ndmp restore follows the correct steps");
got_files(1, "L ..and restored file is present in testdir");
}
chdir("$testdir/..");
rmtree($testdir);