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