2fc3918677f6996d0d2171a67c2ea133142b15ce
[debian/amanda] / installcheck / Installcheck / Config.pm
1 # vim:ft=perl
2 # Copyright (c) 2008,2009 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::Config;
21 use Installcheck;
22 use Amanda::Paths;
23 use Amanda::Constants;
24 use File::Path;
25 use Carp;
26
27 =head1 NAME
28
29 Installcheck::Config - set up amanda configurations for installcheck testing
30
31 =head1 SYNOPSIS
32
33   use Installcheck::Config;
34
35   my $testconf = Installcheck::Config->new();
36   $testconf->add_param("runtapes", "5");
37   $testconf->add_tapetype("DUCKTAPE", [
38     length => "10G", filemark => "4096k",
39   ]);
40   # ...
41   $testconf->write();
42
43 The resulting configuration is always named "TESTCONF".  The basic
44 configuration contains only a few parameters that are necessary
45 just to run Amanda applications in the test environment.  It also
46 contains a tapetype, C<TEST-TAPE>.  To change tapetype parameters,
47 call C<< $cf->add_tapetype >> with a new definition of C<TEST-TAPE>.
48
49 Note that it's quite possible to produce an invalid configuration with this
50 package (and, in fact, some of the tests do just that).
51
52 =head1 WARNING
53
54 Using this module I<will> destroy any existing configuration named
55 TESTDIR.  I<Please> do not use this on a production machine!
56
57 =head1 FUNCTIONS
58
59 =over
60
61 =item C<new()>
62
63 Create a new configuration object
64
65 =cut
66
67 sub new {
68     my $class = shift;
69
70     # An instance is a blessed hash containing parameters.  Start with
71     # some defaults to make sure things run.
72     my $infofile = "$CONFIG_DIR/TESTCONF/curinfo";
73     my $logdir = "$CONFIG_DIR/TESTCONF/log";
74     my $indexdir = "$CONFIG_DIR/TESTCONF/index";
75     my $org = "DailySet1";
76
77     my $self = {
78         'infofile' => $infofile,
79         'logdir' => $logdir,
80         'indexdir' => $indexdir,
81
82         # Global params are stored as an arrayref, so that the same declaration
83         # can appear multiple times
84         'params' => [
85             'dumpuser' => '"' . (getpwuid($<))[0] . '"', # current username
86
87             # These dirs are under CONFIG_DIR just for ease of destruction.
88             # This is not a recommended layout!
89             'infofile' => "\"$infofile\"",
90             'logdir' => "\"$logdir\"",
91             'indexdir' => "\"$indexdir\"",
92             'org' => "\"$org\"",
93
94             # (this is actually added while writing the config file, if not
95             # overridden by the caller)
96             # 'tapetype' => '"TEST-TAPE"',
97         ],
98
99         # global client config
100         'client_params' => [
101             'amandates' => "\"$Installcheck::TMP/TESTCONF/amandates\"",
102             'gnutar_list_dir' => "\"$Installcheck::TMP/TESTCONF/gnutar_listdir\"",
103         ],
104
105         # config-specific client config
106         'client_config_params' => [
107         ],
108
109         # Subsections are stored as a hashref of arrayrefs, keyed by
110         # subsection name
111
112         'tapetypes' => [ ],
113         'dumptypes' => [ ],
114         'interfaces' => [ ],
115         'holdingdisks' => [ ],
116         'application' => [ ],
117         'script' => [ ],
118         'devices' => [ ],
119         'changers' => [ ],
120         'text' => '',
121
122         'dles' => [ ],
123     };
124     bless($self, $class);
125
126     $self->add_tapetype('TEST-TAPE', [
127         'length' => '50 mbytes',
128         'filemark' => '4 kbytes'
129     ]);
130     return $self;
131 }
132
133 =item C<add_param($param, $value)>
134
135 Add the given parameter to the configuration file.  Note that strings which
136 should be quoted in the configuration file itself must be double-quoted here,
137 e.g.,
138
139   $testconf->add_param('org' => '"MyOrganization"');
140
141 =cut
142
143 sub add_param {
144     my $self = shift;
145     my ($param, $value) = @_;
146
147     push @{$self->{'params'}}, $param, $value;
148 }
149
150 =item C<add_client_param($param, $value)>, C<add_client_config_param($param, $value)>
151
152 Add the given parameter to the client configuration file, as C<add_param> does
153 for the server configuration file.  C<add_client_param> addresses the global
154 client configuration file, while C<add_client_config_param> inserts parmeters
155 into C<TESTCONF/amanda-client.conf>.
156
157   $testconf->add_client_param('auth' => '"krb2"');
158   $testconf->add_client_config_param('client_username' => '"freddy"');
159
160 =cut
161
162 sub add_client_param {
163     my $self = shift;
164     my ($param, $value) = @_;
165
166     push @{$self->{'client_params'}}, $param, $value;
167 }
168
169 sub add_client_config_param {
170     my $self = shift;
171     my ($param, $value) = @_;
172
173     push @{$self->{'client_config_params'}}, $param, $value;
174 }
175
176 =item C<remove_param($param)>
177
178 Remove the given parameter from the config file.
179
180 =cut
181
182 sub remove_param {
183     my $self = shift;
184     my ($param) = @_;
185
186     my @new_params;
187
188     while (@{$self->{'params'}}) {
189         my ($p, $v) = (shift @{$self->{'params'}}, shift @{$self->{'params'}});
190         next if $p eq $param;
191         push @new_params, $p, $v;
192     }
193
194     $self->{'params'} = \@new_params;
195 }
196
197 =item C<add_tapetype($name, $values_arrayref)>
198 =item C<add_dumptype($name, $values_arrayref)>
199 =item C<add_holdingdisk($name, $values_arrayref)>
200 =item C<add_holdingdisk_def($name, $values_arrayref)>
201 =item C<add_interface($name, $values_arrayref)>
202 =item C<add_application($name, $values_arrayref)>
203 =item C<add_script($name, $values_arrayref)>
204 =item C<add_device($name, $values_arrayref)>
205 =item C<add_changer($name, $values_arrayref)>
206
207 Add the given subsection to the configuration file, including all values in the
208 arrayref.  The values should be specified as alternating key/value pairs.
209 Since holdingdisk definitions usually don't have a "define" keyword,
210 C<add_holdingdisk> does not add one, but C<add_holdingdisk_def> does.
211
212 =cut
213
214 sub _add_subsec {
215     my $self = shift;
216     my ($subsec, $name, $use_define, $values) = @_;
217
218     # first delete any existing subsections with that name
219     @{$self->{$subsec}} = grep { $_->[0] ne $name } @{$self->{$subsec}};
220     
221     # and now push the new subsection definition on the end
222     push @{$self->{$subsec}}, [$name, $use_define, $values];
223 }
224
225 sub add_tapetype {
226     my $self = shift;
227     my ($name, $values) = @_;
228     $self->_add_subsec("tapetypes", $name, 1, $values);
229 }
230
231 sub add_dumptype {
232     my $self = shift;
233     my ($name, $values) = @_;
234     $self->_add_subsec("dumptypes", $name, 1, $values);
235 }
236
237 # by default, holdingdisks don't have the "define" keyword
238 sub add_holdingdisk {
239     my $self = shift;
240     my ($name, $values) = @_;
241     $self->_add_subsec("holdingdisks", $name, 0, $values);
242 }
243
244 # add a holdingdisk definition only (use "define" keyword)
245 sub add_holdingdisk_def {
246     my $self = shift;
247     my ($name, $values) = @_;
248     $self->_add_subsec("holdingdisks", $name, 1, $values);
249 }
250
251 sub add_interface {
252     my $self = shift;
253     my ($name, $values) = @_;
254     $self->_add_subsec("interfaces", $name, 1, $values);
255 }
256
257 sub add_application {
258     my $self = shift;
259     my ($name, $values) = @_;
260     $self->_add_subsec("application", $name, 1, $values);
261 }
262
263 sub add_script {
264     my $self = shift;
265     my ($name, $values) = @_;
266     $self->_add_subsec("script", $name, 1, $values);
267 }
268
269 sub add_device {
270     my $self = shift;
271     my ($name, $values) = @_;
272     $self->_add_subsec("devices", $name, 1, $values);
273 }
274
275 sub add_changer {
276     my $self = shift;
277     my ($name, $values) = @_;
278     $self->_add_subsec("changers", $name, 1, $values);
279 }
280
281 =item C<add_text($text)>
282
283 Add arbitrary text to the config file.
284
285 =cut
286
287 sub add_text {
288     my $self = shift;
289     my ($text) = @_;
290     $self->{'text'} .= $text;
291 }
292
293 =item C<add_dle($line)>
294
295 Add a disklist entry; C<$line> is inserted verbatim into the disklist.
296
297 =cut
298
299 sub add_dle {
300     my $self = shift;
301     my ($line) = @_;
302     push @{$self->{'dles'}}, $line;
303 }
304
305 =item C<write()>
306
307 Write out the accumulated configuration file, along with any other
308 files necessary to run Amanda.
309
310 =cut
311
312 sub write {
313     my $self = shift;
314
315     cleanup();
316
317     my $testconf_dir = "$CONFIG_DIR/TESTCONF";
318     mkpath($testconf_dir);
319
320     # set up curinfo dir, etc.
321     mkpath($self->{'infofile'}) or die("Could not create infofile directory");
322     mkpath($self->{'logdir'}) or die("Could not create logdir directory");
323     mkpath($self->{'indexdir'}) or die("Could not create indexdir directory");
324     my $amandates = $Installcheck::TMP . "/TESTCONF/amandates";
325     my $gnutar_listdir = $Installcheck::TMP . "/TESTCONF/gnutar_listdir";
326     if (! -d $gnutar_listdir) {
327         mkpath($gnutar_listdir)
328             or die("Could not create '$gnutar_listdir'");
329     }
330
331     $self->_write_tapelist("$testconf_dir/tapelist");
332     $self->_write_disklist("$testconf_dir/disklist");
333     $self->_write_amanda_conf("$testconf_dir/amanda.conf");
334     $self->_write_amandates($amandates);
335     $self->_write_amanda_client_conf("$CONFIG_DIR/amanda-client.conf");
336     $self->_write_amanda_client_config_conf("$testconf_dir/amanda-client.conf");
337 }
338
339 sub _write_tapelist {
340     my $self = shift;
341     my ($filename) = @_;
342
343     # create an empty tapelist
344     open(my $tapelist, ">", $filename);
345     close($tapelist);
346 }
347
348 sub _write_disklist {
349     my $self = shift;
350     my ($filename) = @_;
351
352     # don't bother writing a disklist if there are no dle's
353     return unless $self->{'dles'};
354
355     open(my $disklist, ">", $filename);
356
357     for my $dle_line (@{$self->{'dles'}}) {
358         print $disklist "$dle_line\n";
359     }
360
361     close($disklist);
362 }
363
364 sub _write_amanda_conf {
365     my $self = shift;
366     my ($filename) = @_;
367
368     open my $amanda_conf, ">", $filename
369         or croak("Could not open '$filename'");
370
371     # write key/value pairs
372     my @params = @{$self->{'params'}};
373     my $saw_tapetype = 0;
374     while (@params) {
375         $param = shift @params;
376         $value = shift @params;
377         print $amanda_conf "$param $value\n";
378         $saw_tapetype = 1 if ($param eq "tapetype");
379     }
380
381     # tapetype is special-cased: if the user has not specified a tapetype, use "TEST-TAPE".
382     if (!$saw_tapetype) {
383         print $amanda_conf "tapetype \"TEST-TAPE\"\n";
384     }
385
386     # write out subsections
387     $self->_write_amanda_conf_subsection($amanda_conf, "tapetype", $self->{"tapetypes"});
388     $self->_write_amanda_conf_subsection($amanda_conf, "application", $self->{"application"});
389     $self->_write_amanda_conf_subsection($amanda_conf, "script", $self->{"script"});
390     $self->_write_amanda_conf_subsection($amanda_conf, "dumptype", $self->{"dumptypes"});
391     $self->_write_amanda_conf_subsection($amanda_conf, "interface", $self->{"interfaces"});
392     $self->_write_amanda_conf_subsection($amanda_conf, "holdingdisk", $self->{"holdingdisks"});
393     $self->_write_amanda_conf_subsection($amanda_conf, "device", $self->{"devices"});
394     $self->_write_amanda_conf_subsection($amanda_conf, "changer", $self->{"changers"});
395     print $amanda_conf "\n", $self->{'text'}, "\n";
396
397     close($amanda_conf);
398 }
399
400 sub _write_amanda_conf_subsection {
401     my $self = shift;
402     my ($amanda_conf, $subsec_type, $subsec_ref) = @_;
403
404     for my $subsec_info (@$subsec_ref) {
405         my ($subsec_name, $use_define, $values) = @$subsec_info;
406         
407         my $define = $use_define? "define " : "";
408         print $amanda_conf "\n$define$subsec_type $subsec_name {\n";
409
410         my @values = @$values; # make a copy
411         while (@values) {
412             $param = shift @values;
413             $value = shift @values;
414             if ($param eq "inherit") {
415                 print $amanda_conf "$value\n";
416             } else {
417                 print $amanda_conf "$param $value\n";
418             }
419         }
420         print $amanda_conf "}\n";
421     }
422 }
423
424 sub _write_amandates {
425     my $self = shift;
426     my ($filename) = @_;
427
428     # make sure the containing directory exists
429     mkpath($filename =~ /(^.*)\/amandates/);
430
431     # truncate the file to eliminate any interference from previous runs
432     open(my $amandates, ">", $filename) or die("Could not write to '$filename'");
433     close($amandates);
434 }
435
436 sub _write_amanda_client_conf {
437     my $self = shift;
438     my ($filename, $amandates, $gnutar_listdir) = @_;
439
440     # just an empty file for now
441     open(my $amanda_client_conf, ">", $filename) 
442         or croak("Could not write to '$filename'");
443
444     # write key/value pairs
445     my @params = @{$self->{'client_params'}};
446     while (@params) {
447         $param = shift @params;
448         $value = shift @params;
449         print $amanda_client_conf "$param $value\n";
450     }
451
452     close($amanda_client_conf);
453 }
454
455 sub _write_amanda_client_config_conf {
456     my $self = shift;
457     my ($filename, $amandates, $gnutar_listdir) = @_;
458
459     # just an empty file for now
460     open(my $amanda_client_conf, ">", $filename) 
461         or croak("Could not write to '$filename'");
462
463     # write key/value pairs
464     my @params = @{$self->{'client_config_params'}};
465     while (@params) {
466         $param = shift @params;
467         $value = shift @params;
468         print $amanda_client_conf "$param $value\n";
469     }
470
471     close($amanda_client_conf);
472 }
473
474 =item C<cleanup()> (callable as a package method too)
475
476 Clean up by deleting the configuration directory.
477
478 =cut
479
480 sub cleanup {
481     my $testconf_dir = "$CONFIG_DIR/TESTCONF";
482     if (-e $testconf_dir) {
483         rmtree($testconf_dir) or die("Could not remove '$testconf_dir'");
484     }
485 }
486
487 1;