Imported Upstream version 3.3.3
[debian/amanda] / installcheck / Installcheck / Mock.pm
1 # vim:ft=perl
2 # Copyright (c) 2009-2012 Zmanda, Inc.  All Rights Reserved.
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 # for more details.
13 #
14 # You should have received a copy of the GNU General Public License along
15 # with this program; if not, write to the Free Software Foundation, Inc.,
16 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
19 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
20
21 package Installcheck::Mock;
22
23 =head1 NAME
24
25 Installcheck::Mock - utilities for the installcheck mocks
26
27 =head1 SYNOPSIS
28
29   use Installcheck::Mock;
30
31   my $statefile = Installcheck::Mock::setup_mock_mtx(
32          num_slots => 5,
33          num_ie => 1,
34          barcodes => 1,
35          track_orig => 1,
36          num_drives => 2,
37          loaded_slots => {
38              1 => '023984'
39          },
40          first_slot => 1,
41          first_drive => 0,
42          first_ie => 6,
43        );
44
45 =head1 USAGE
46
47 =head2 mtx
48
49 C<setup_mock_mtx> sets up a state file for C<mock/mtx> with the given config
50 hash, and returns the filename of the state file.  This function must be run
51 with the current dirctory pointing to 'installcheck/'.
52
53 =head2 ndmjob
54
55 Ndmjob can be used to provide an NDMP server, complete with two tape drives and
56 a 10-tape changer.  Start it with:
57
58   my $ndmpserver = Installcheck::Mock::NdmpServer->new();
59
60 All keyword arguments are optional, and include:
61
62   tape_limit        size limit for simulated tapes
63
64 The resulting object has a number of useful attributes:
65
66   $n->{'port'}      port the NDMP server is listening on
67   $n->{'changer'}   device name for the NDMP changer
68   $n->{'drive0'}    device name for changer's drive 0
69   $n->{'drive1'}    device name for changer's drive 1
70   $n->{'drive'}     device name for a drive not attached to the changer,
71                     and already loaded with a volume
72
73 The constructor takes the following keyword arguments:
74
75   no_reset => 1     do not empty out the changer of any existing state or data
76
77 The C<config> method, given an L<Installcheck::Config> object, will add the
78 necessary config to use C<chg-ndmp>:
79
80   $ndmp->config($testconf);
81   $testconf->write();
82
83 The C<edit_config> method is intended for use with NDMP dumps in
84 L<Installcheck::Dumpcache>.  It edits an existing, on-disk configuration that
85 was created by the C<config> method to reflect the NDMP port in use by this
86 instance:
87
88   Installcheck:Dumpcache::load("ndmp")
89   $ndmp->edit_config();
90
91 The C<cleanup> method will clean up any data files, and should be called when a
92 test suite finishes.  The C<reset> method resets the changer to its initial
93 state, without restarting ndmjob.
94
95 =cut
96
97 use Installcheck;
98 use Cwd qw(abs_path);
99 use File::Path qw( mkpath rmtree );
100 use Data::Dumper;
101 use POSIX;
102 use IPC::Open3;
103 use Amanda::Paths;
104
105 require Exporter;
106 @ISA = qw(Exporter);
107 @EXPORT_OK = qw( setup_mock_mtx $mock_mtx_path );
108
109 sub setup_mock_mtx {
110     my %config = @_;
111     my $state_filename = "$Installcheck::TMP/mtx_state";
112     open (my $fh, ">", $state_filename) or die $!;
113     print $fh (Data::Dumper->Dump([
114             { config => \%config }
115         ], ["STATE"]));
116     close ($fh);
117
118     return $state_filename;
119 }
120
121 our $mock_mtx_path = abs_path("mock") . "/mtx"; # abs_path only does dirs in perl-5.6
122
123 package Installcheck::Mock::NdmpServer;
124
125 use File::Path qw( mkpath rmtree );
126 use Amanda::Paths;
127
128 sub new {
129     my $class = shift;
130     my %params = @_;
131
132     my $self = bless {}, $class;
133
134     no warnings; # don't complain that OUTFH is used only once
135
136     # note that we put this under /vtapes/ so that Dumpcache will pick it up
137     $self->{'simdir'} = "$Installcheck::TMP/vtapes/ndmjob";
138     $self->reset() unless ($params{'no_reset'});
139
140     # launch ndmjob
141     my $port = Installcheck::get_unused_port();
142     my @extra_args;
143     if ($params{'tape_limit'}) {
144         push @extra_args, "-o", "tape-limit=".$params{'tape_limit'};
145     }
146     my $ndmjob_pid = IPC::Open3::open3("INFH", "OUTFH", "ERRFH",
147         "$amlibexecdir/ndmjob", "-d8", "-o", "test-daemon",
148                 "-p", $port, @extra_args);
149
150     # wait for it to start up
151     unless (<OUTFH> =~ /READY/) {
152         for (<ERRFH>) {
153             main::diag($_);
154         }
155         die "ndmjob did not start up";
156     }
157
158     # and fill in the various attributes
159     $self->{'port'} = $port;
160     $self->{'changer'} = $self->{'simdir'};
161     $self->{'drive0'} = $self->{'simdir'} . '/drive0';
162     $self->{'drive1'} = $self->{'simdir'} . '/drive1';
163     $self->{'drive'} = $self->{'simdir'} . '/solo-drive';
164     $self->{'pid'} = $ndmjob_pid;
165
166     return $self;
167 }
168
169 sub config {
170     my $self = shift;
171     my ($testconf) = @_;
172
173     my $port = $self->{'port'};
174     my $chg = $self->{'changer'};
175     my $drive0 = $self->{'drive0'};
176     my $drive1 = $self->{'drive1'};
177
178     $testconf->remove_param('tapedev');
179     $testconf->remove_param('tpchanger');
180     $testconf->remove_param('changerfile');
181     $testconf->add_param('tpchanger', '"ndmp_server"');
182     $testconf->add_changer('ndmp_server', [
183         tpchanger => "\"chg-ndmp:127.0.0.1:$port\@$chg\"",
184         property => "\"tape-device\" \"0=ndmp:127.0.0.1:$port\@$drive0\"",
185         property => "append \"tape-device\" \"1=ndmp:127.0.0.1:$port\@$drive1\"",
186         changerfile => "\"$chg-state\"",
187     ]);
188 }
189
190 sub edit_config {
191     my $self = shift;
192
193     # this is a bit sneaky, but is useful for dumpcache'd ndmp runs
194     my $amanda_conf_filename = "$CONFIG_DIR/TESTCONF/amanda.conf";
195     
196     # slurp the whole file
197     open(my $fh, "<", $amanda_conf_filename) or die("open $amanda_conf_filename: $!");
198     my $amanda_conf = do { local $/; <$fh> };
199     close($fh);
200
201     # replace all existing ndmp changers and devices with the new port
202     # note that we assume that the remaining paths are correct
203     my $port = $self->{'port'};
204     $amanda_conf =~ s/ndmp:127.0.0.1:\d+/ndmp:127.0.0.1:$port/g;
205
206     open($fh, ">", $amanda_conf_filename) or die("open $amanda_conf_filename for writing: $!");
207     print $fh $amanda_conf;
208     close($fh);
209 }
210
211 sub reset {
212     my $self = shift;
213
214     $self->cleanup();
215     mkpath($self->{'simdir'})
216         or die("cannot create " . $self->{'simdir'});
217
218     # non-changer drive must exist
219     my $drive = $self->{'simdir'} . '/solo-drive';
220     open(my $fd, ">", $drive)
221         or die("Could not open '$drive': $!");
222     close($fd);
223 }
224
225 sub cleanup {
226     my $self = shift;
227
228     rmtree($self->{'simdir'}) if -d $self->{'simdir'};
229 }
230
231 1;