1 # Copyright (c) 2007, 2008, 2009, 2010 Zmanda, Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 # Contact information: Zmanda Inc, 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19 use Test::More tests => 224;
24 use lib "@amperldir@";
25 use Installcheck::Config;
28 use Amanda::Config qw( :init :getconf string_to_boolean amandaify_property_name );
34 Amanda::Debug::dbopen("installcheck");
35 Installcheck::log_test_output();
39 sub diag_config_errors {
40 my ($level, @errors) = Amanda::Config::config_errors();
41 for my $errmsg (@errors) {
47 # Try starting with no configuration at all
49 is(config_init(0, ''), $CFGERR_OK,
50 "Initialize with no configuration")
51 or diag_config_errors();
54 $config_overrides = new_config_overrides(1);
55 add_config_override($config_overrides, "tapedev", "null:TEST");
56 set_config_overrides($config_overrides);
58 is(config_init(0, undef), $CFGERR_OK,
59 "Initialize with no configuration, passing a NULL config name")
60 or diag_config_errors();
62 is(getconf($CNF_TAPEDEV), "null:TEST",
63 "config overwrites work with null config");
66 # Check out error handling
68 $testconf = Installcheck::Config->new();
69 $testconf->add_param('label_new_tapes', '"xx"'); # a deprecated keyword -> warning
73 is(config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF"), $CFGERR_WARNINGS,
74 "Deprecated keyword generates a warning");
75 my ($error_level, @errors) = Amanda::Config::config_errors();
76 like($errors[0], qr/is deprecated/,
77 "config_get_errors returns the warning string");
79 Amanda::Config::config_clear_errors();
80 ($error_level, @errors) = Amanda::Config::config_errors();
81 is(scalar(@errors), 0, "config_clear_errors clears error list");
84 $testconf = Installcheck::Config->new();
85 $testconf->add_param('invalid-param', 'random-value'); # a deprecated keyword -> warning
88 is(config_init($CONFIG_INIT_EXPLICIT_NAME, "NO-SUCH-CONFIGURATION"), $CFGERR_ERRORS,
89 "Non-existent config generates an error");
91 is(config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF"), $CFGERR_ERRORS,
92 "Invalid keyword generates an error");
95 # try a client configuration
97 # (note use of uppercase letters to test lower-casing of property names)
98 $testconf = Installcheck::Config->new();
99 $testconf->add_client_param('property', '"client-prop" "yep"');
100 $testconf->add_client_param('property', 'priority "clIent-prop1" "foo"');
101 $testconf->add_client_param('property', 'append "clieNt-prop" "bar"');
102 $testconf->add_client_param('property', '"ANotHer_prOp" "baz"');
103 $testconf->add_client_param('property', 'append "ANOTHER-prop" "boo"');
106 my $cfg_result = config_init($CONFIG_INIT_CLIENT, undef);
107 is($cfg_result, $CFGERR_OK,
108 "Load test client configuration")
109 or diag_config_errors();
111 is_deeply(getconf($CNF_PROPERTY), { "client-prop1" => { priority => 1,
113 values => [ "foo" ]},
114 "client-prop" => { priority => 0,
116 values => [ "yep", "bar" ] },
117 "another-prop" => { priority => 0,
119 values => [ "baz", "boo" ] }},
120 "Client PROPERTY parameter parsed correctly");
123 # Parse up a basic configuration
125 # invent a "large" unsigned number, and make $size_t_num
126 # depend on the length of size_t
127 my $int64_num = '171801575472'; # 0xA000B000C000 / 1024
129 if (Amanda::Tests::sizeof_size_t() > 4) {
130 $size_t_num = $int64_num;
132 $size_t_num = '2147483647'; # 0x7fffffff
135 $testconf = Installcheck::Config->new();
136 $testconf->add_param('reserve', '75');
137 $testconf->add_param('autoflush', 'yes');
138 $testconf->add_param('usetimestamps', '0');
139 $testconf->add_param('tapedev', '"/dev/foo"');
140 $testconf->add_param('bumpsize', $int64_num);
141 $testconf->add_param('bumpmult', '1.4');
142 $testconf->add_param('reserved_udp-port', '100,200'); # note use of '-' and '_'
143 $testconf->add_param('device_output_buffer_size', $size_t_num);
144 $testconf->add_param('taperalgo', 'last');
145 $testconf->add_param('device_property', '"foo" "bar"');
146 $testconf->add_param('device_property', '"blUE" "car" "tar"');
147 $testconf->add_param('autolabel', 'non-amanda empty');
148 $testconf->add_param('displayunit', '"m"');
149 $testconf->add_param('debug_auth', '1');
150 $testconf->add_tapetype('mytapetype', [
151 'comment' => '"mine"',
153 'part_size' => '100M',
154 'part_cache_type' => 'disk',
155 'part_cache_dir' => '"/usr/bin"',
156 'part_cache_max_size' => '50M',
158 $testconf->add_dumptype('mydump-type', [ # note dash
159 'comment' => '"mine"',
160 'priority' => 'high', # == 2
161 'bumpsize' => $int64_num,
164 'holdingdisk' => 'required',
165 'compress' => 'client best',
166 'encrypt' => 'server',
167 'strategy' => 'incronly',
168 'comprate' => '0.25,0.75',
169 'exclude list' => '"foo" "bar"',
170 'exclude list append' => '"true" "star"',
171 'exclude file' => '"foolist"',
172 'include list' => '"bing" "ting"',
173 'include list append' => '"string" "fling"',
174 'include file optional' => '"rhyme"',
175 'property' => '"prop" "erty"',
176 'property' => '"DROP" "qwerty" "asdfg"',
177 'estimate' => 'server calcsize client',
178 'allow_split' => 'no',
179 'allow_split' => 'no',
181 $testconf->add_dumptype('second_dumptype', [ # note underscore
183 'comment' => '"refers to mydump-type with a dash"',
185 $testconf->add_dumptype('third_dumptype', [
186 '' => 'second_dumptype',
187 'comment' => '"refers to second_dumptype with an underscore"',
188 'recovery-limit' => '"left" same-host "right"',
190 $testconf->add_interface('ethernet', [
191 'comment' => '"mine"',
194 $testconf->add_interface('nic', [
195 'comment' => '"empty"',
197 $testconf->add_holdingdisk('hd1', [
198 'comment' => '"mine"',
199 'directory' => '"/mnt/hd1"',
201 'chunksize' => '1024k',
203 $testconf->add_holdingdisk('hd2', [
204 'comment' => '"empty"',
206 $testconf->add_application('my_app', [
207 'comment' => '"my_app_comment"',
208 'plugin' => '"amgtar"',
210 $testconf->add_script('my_script', [
211 'comment' => '"my_script_comment"',
212 'plugin' => '"script-email"',
213 'execute-on' => 'pre-host-backup, post-host-backup',
214 'execute-where' => 'client',
215 'property' => '"mailto" "amandabackup" "amanda"',
217 $testconf->add_device('my_device', [
218 'comment' => '"my device is mine, not yours"',
219 'tapedev' => '"tape:/dev/nst0"',
220 'device_property' => '"BLOCK_SIZE" "128k"',
221 'device_property' => '"CoMmENT" "what up?"',
223 $testconf->add_changer('my_changer', [
224 'comment' => '"my changer is mine, not yours"',
225 'tpchanger' => '"chg-foo"',
226 'changerdev' => '"/dev/sg0"',
227 'changerfile' => '"chg.state"',
228 'property' => '"testprop" "testval"',
229 'device_property' => '"testdprop" "testdval"',
231 $testconf->add_interactivity('my_interactivity', [
232 'comment' => '"my interactivity is mine, not yours"',
233 'plugin' => '"MY-interactivity"',
234 'property' => '"testprop" "testval"',
237 $testconf->add_taperscan('my_taperscan', [
238 'comment' => '"my taperscan is mine, not yours"',
239 'plugin' => '"MY-taperscan"',
240 'property' => '"testprop" "testval"',
245 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
246 if (!is($cfg_result, $CFGERR_OK,
247 "Load test configuration")) {
248 diag_config_errors();
249 die "aborting after config errors";
252 is(Amanda::Config::get_config_name(), "TESTCONF",
254 is(Amanda::Config::get_config_dir(), "$CONFIG_DIR/TESTCONF",
256 is(Amanda::Config::get_config_filename(),
257 "$CONFIG_DIR/TESTCONF/amanda.conf",
258 "config_filename set");
260 is(getconf($CNF_RESERVE), 75,
261 "integer global confparm");
262 is(getconf($CNF_BUMPSIZE), $int64_num+0,
263 "int64 global confparm");
264 is(getconf($CNF_TAPEDEV), "/dev/foo",
265 "string global confparm");
266 is(getconf($CNF_DEVICE_OUTPUT_BUFFER_SIZE), $size_t_num+0,
267 "size global confparm");
268 ok(getconf($CNF_AUTOFLUSH),
269 "boolean global confparm");
270 is(getconf($CNF_USETIMESTAMPS), 0,
271 "boolean global confparm, passing an integer (0)");
272 is(getconf($CNF_TAPERALGO), $Amanda::Config::ALGO_LAST,
273 "taperalgo global confparam");
274 is_deeply([getconf($CNF_RESERVED_UDP_PORT)], [100,200],
275 "intrange global confparm");
276 is(getconf($CNF_DISPLAYUNIT), "M",
277 "displayunit is correctly uppercased");
278 is_deeply(getconf($CNF_DEVICE_PROPERTY),
279 { "foo" => { priority => 0, append => 0, values => ["bar"]},
280 "blue" => { priority => 0, append => 0,
281 values => ["car", "tar"]} },
282 "proplist global confparm");
283 is_deeply(getconf($CNF_AUTOLABEL),
284 { template => undef, other_config => '',
285 non_amanda => 1, volume_error => '', empty => 1 },
286 "'autolabel non-amanda empty' represented correctly");
287 ok(getconf_seen($CNF_TAPEDEV),
288 "'tapedev' parm was seen");
289 ok(!getconf_seen($CNF_CHANGERFILE),
290 "'changerfile' parm was not seen");
292 is(Amanda::Config::getconf_unit_divisor(), 1024,
293 "correct unit divisor (from displayunit -> KB)");
294 ok($Amanda::Config::debug_auth,
295 "debug_auth setting reflected in global variable");
296 ok(!$Amanda::Config::debug_amandad,
297 "debug_amandad defaults to false");
299 my $ttyp = lookup_tapetype("mytapetype");
300 ok($ttyp, "found mytapetype");
301 is(tapetype_getconf($ttyp, $TAPETYPE_COMMENT), 'mine',
303 is(tapetype_getconf($ttyp, $TAPETYPE_LENGTH), 128 * 1024,
306 ok(tapetype_seen($ttyp, $TAPETYPE_COMMENT),
307 "tapetype comment was seen");
308 ok(!tapetype_seen($ttyp, $TAPETYPE_LBL_TEMPL),
309 "tapetype lbl_templ was not seen");
311 is(tapetype_getconf($ttyp, $TAPETYPE_PART_SIZE), 100*1024,
312 "tapetype part_size");
313 is(tapetype_getconf($ttyp, $TAPETYPE_PART_CACHE_TYPE), $PART_CACHE_TYPE_DISK,
314 "tapetype part_cache_type");
315 is(tapetype_getconf($ttyp, $TAPETYPE_PART_CACHE_DIR), "/usr/bin",
316 "tapetype part_cache_dir");
317 is(tapetype_getconf($ttyp, $TAPETYPE_PART_CACHE_MAX_SIZE), 50*1024,
318 "tapetype part_cache_max_size");
320 is_deeply([ sort(+getconf_list("tapetype")) ],
321 [ sort("mytapetype", "TEST-TAPE") ],
322 "getconf_list lists all tapetypes");
324 my $dtyp = lookup_dumptype("mydump-type");
325 ok($dtyp, "found mydump-type");
326 is(dumptype_getconf($dtyp, $DUMPTYPE_COMMENT), 'mine',
328 is(dumptype_getconf($dtyp, $DUMPTYPE_PRIORITY), 2,
329 "dumptype priority");
330 is(dumptype_getconf($dtyp, $DUMPTYPE_BUMPSIZE), $int64_num+0,
332 is(dumptype_getconf($dtyp, $DUMPTYPE_BUMPMULT), 1.75,
334 is(dumptype_getconf($dtyp, $DUMPTYPE_STARTTIME), 1829,
336 is(dumptype_getconf($dtyp, $DUMPTYPE_HOLDINGDISK), $HOLD_REQUIRED,
337 "dumptype holdingdisk");
338 is(dumptype_getconf($dtyp, $DUMPTYPE_COMPRESS), $COMP_BEST,
339 "dumptype compress");
340 is(dumptype_getconf($dtyp, $DUMPTYPE_ENCRYPT), $ENCRYPT_SERV_CUST,
342 is(dumptype_getconf($dtyp, $DUMPTYPE_STRATEGY), $DS_INCRONLY,
343 "dumptype strategy");
344 is_deeply([dumptype_getconf($dtyp, $DUMPTYPE_COMPRATE)], [0.25, 0.75],
345 "dumptype comprate");
346 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_INCLUDE),
347 { 'file' => [ 'rhyme' ],
348 'list' => [ 'bing', 'ting', 'string', 'fling' ],
350 "dumptype include list");
351 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_EXCLUDE),
352 { 'file' => [ 'foolist' ],
353 'list' => [ 'foo', 'bar', 'true', 'star' ],
355 "dumptype exclude list");
356 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_ESTIMATELIST),
357 [ $ES_SERVER, $ES_CALCSIZE, $ES_CLIENT ],
358 "dumptype estimate list");
359 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_PROPERTY),
360 { "prop" => { priority => 0, append => 0, values => ["erty"]},
361 "drop" => { priority => 0, append => 0,
362 values => ["qwerty", "asdfg"] }},
363 "dumptype proplist");
364 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_RECOVERY_LIMIT),
366 "dumptype recovery limit with no limit specified => empty");
368 ok(dumptype_seen($dtyp, $DUMPTYPE_EXCLUDE),
369 "'exclude' parm was seen");
370 ok(!dumptype_seen($dtyp, $DUMPTYPE_RECORD),
371 "'record' parm was not seen");
373 is_deeply([ sort(+getconf_list("dumptype")) ],
375 mydump-type second_dumptype third_dumptype
376 NO-COMPRESS COMPRESS-FAST COMPRESS-BEST COMPRESS-CUST
377 SRVCOMPRESS BSD-AUTH BSDTCP-AUTH NO-RECORD NO-HOLD
380 "getconf_list lists all dumptypes (including defaults)");
381 is(dumptype_getconf($dtyp, $DUMPTYPE_ALLOW_SPLIT), 0,
382 "dumptype allow_split");
384 my $iface = lookup_interface("ethernet");
385 ok($iface, "found ethernet");
386 is(interface_name($iface), "ethernet",
387 "interface knows its name");
388 is(interface_getconf($iface, $INTER_COMMENT), 'mine',
389 "interface comment");
390 is(interface_getconf($iface, $INTER_MAXUSAGE), 100,
391 "interface maxusage");
393 $iface = lookup_interface("nic");
394 ok($iface, "found nic");
395 ok(interface_seen($iface, $INTER_COMMENT),
396 "seen set for parameters that appeared");
397 ok(!interface_seen($iface, $INTER_MAXUSAGE),
398 "seen not set for parameters that did not appear");
400 is_deeply([ sort(+getconf_list("interface")) ],
401 [ sort('ethernet', 'nic', 'default') ],
402 "getconf_list lists all interfaces (in any order)");
404 skip "error loading config", 13 unless $cfg_result == $CFGERR_OK;
405 my $hdisk = lookup_holdingdisk("hd1");
406 ok($hdisk, "found hd1");
407 is(holdingdisk_name($hdisk), "hd1",
408 "hd1 knows its name");
409 is(holdingdisk_getconf($hdisk, $HOLDING_COMMENT), 'mine',
410 "holdingdisk comment");
411 is(holdingdisk_getconf($hdisk, $HOLDING_DISKDIR), '/mnt/hd1',
412 "holdingdisk diskdir (directory)");
413 is(holdingdisk_getconf($hdisk, $HOLDING_DISKSIZE), 100*1024,
414 "holdingdisk disksize (use)");
415 is(holdingdisk_getconf($hdisk, $HOLDING_CHUNKSIZE), 1024,
416 "holdingdisk chunksize");
418 $hdisk = lookup_holdingdisk("hd2");
419 ok($hdisk, "found hd2");
420 ok(holdingdisk_seen($hdisk, $HOLDING_COMMENT),
421 "seen set for parameters that appeared");
422 ok(!holdingdisk_seen($hdisk, $HOLDING_CHUNKSIZE),
423 "seen not set for parameters that did not appear");
425 # only holdingdisks have this linked-list structure
427 my $hdisklist = getconf($CNF_HOLDINGDISK);
428 my $first_disk = @$hdisklist[0];
429 $hdisk = lookup_holdingdisk($first_disk);
430 like(holdingdisk_name($hdisk), qr/hd[12]/,
431 "one disk is first in list of holdingdisks");
432 $hdisk = lookup_holdingdisk(@$hdisklist[1]);
433 like(holdingdisk_name($hdisk), qr/hd[12]/,
434 "another is second in list of holdingdisks");
435 ok($#$hdisklist == 1,
436 "no third holding disk");
438 is_deeply([ sort(+getconf_list("holdingdisk")) ],
439 [ sort('hd1', 'hd2') ],
440 "getconf_list lists all holdingdisks (in any order)");
442 skip "error loading config", 5 unless $cfg_result == $CFGERR_OK;
443 my $app = lookup_application("my_app");
444 ok($app, "found my_app");
445 is(application_name($app), "my_app",
446 "my_app knows its name");
447 is(application_getconf($app, $APPLICATION_COMMENT), 'my_app_comment',
448 "application comment");
449 is(application_getconf($app, $APPLICATION_PLUGIN), 'amgtar',
450 "application plugin (amgtar)");
452 is_deeply([ sort(+getconf_list("application-tool")) ],
454 "getconf_list lists all applications");
455 # test backward compatibility
456 is_deeply([ sort(+getconf_list("application")) ],
458 "getconf_list works for 'application-tool', too");
460 my $sc = lookup_pp_script("my_script");
461 ok($sc, "found my_script");
462 is(pp_script_name($sc), "my_script",
463 "my_script knows its name");
464 is(pp_script_getconf($sc, $PP_SCRIPT_COMMENT), 'my_script_comment',
466 is(pp_script_getconf($sc, $PP_SCRIPT_PLUGIN), 'script-email',
467 "script plugin (script-email)");
468 is(pp_script_getconf($sc, $PP_SCRIPT_EXECUTE_WHERE), $ES_CLIENT,
469 "script execute_where (client)");
470 is(pp_script_getconf($sc, $PP_SCRIPT_EXECUTE_ON),
471 $EXECUTE_ON_PRE_HOST_BACKUP|$EXECUTE_ON_POST_HOST_BACKUP,
472 "script execute_on");
474 is_deeply([ sort(+getconf_list("script")) ],
475 [ sort("my_script") ],
476 "getconf_list lists all script");
478 is_deeply([ sort(+getconf_list("script-tool")) ],
479 [ sort("my_script") ],
480 "getconf_list works for 'script-tool', too");
482 my $dc = lookup_device_config("my_device");
483 ok($dc, "found my_device");
484 is(device_config_name($dc), "my_device",
485 "my_device knows its name");
486 is(device_config_getconf($dc, $DEVICE_CONFIG_COMMENT), 'my device is mine, not yours',
488 is(device_config_getconf($dc, $DEVICE_CONFIG_TAPEDEV), 'tape:/dev/nst0',
490 # TODO do we really need all of this equipment for device properties?
491 is_deeply(device_config_getconf($dc, $DEVICE_CONFIG_DEVICE_PROPERTY),
492 { "block-size" => { 'priority' => 0, 'values' => ["128k"], 'append' => 0 },
493 "comment" => { 'priority' => 0, 'values' => ["what up?"], 'append' => 0 }, },
494 "device config proplist");
496 is_deeply([ sort(+getconf_list("device")) ],
497 [ sort("my_device") ],
498 "getconf_list lists all devices");
500 skip "error loading config", 7 unless $cfg_result == $CFGERR_OK;
501 $dc = lookup_changer_config("my_changer");
502 ok($dc, "found my_changer");
503 is(changer_config_name($dc), "my_changer",
504 "my_changer knows its name");
505 is(changer_config_getconf($dc, $CHANGER_CONFIG_COMMENT), 'my changer is mine, not yours',
507 is(changer_config_getconf($dc, $CHANGER_CONFIG_CHANGERDEV), '/dev/sg0',
509 is_deeply(changer_config_getconf($dc, $CHANGER_CONFIG_PROPERTY),
512 'values' => [ 'testval' ],
515 }, "changer properties represented correctly");
517 is_deeply(changer_config_getconf($dc, $CHANGER_CONFIG_DEVICE_PROPERTY),
520 'values' => [ 'testdval' ],
523 }, "changer device properties represented correctly");
525 is_deeply([ sort(+getconf_list("changer")) ],
526 [ sort("my_changer") ],
527 "getconf_list lists all changers");
529 $dc = lookup_interactivity("my_interactivity");
530 ok($dc, "found my_interactivity");
531 is(interactivity_name($dc), "my_interactivity",
532 "my_interactivity knows its name");
533 is(interactivity_getconf($dc, $INTERACTIVITY_COMMENT), 'my interactivity is mine, not yours',
534 "interactivity comment");
535 is(interactivity_getconf($dc, $INTERACTIVITY_PLUGIN), 'MY-interactivity',
536 "interactivity plugin");
537 is_deeply(interactivity_getconf($dc, $INTERACTIVITY_PROPERTY),
540 'values' => [ 'testval' ],
543 }, "interactivity properties represented correctly");
545 is_deeply([ sort(+getconf_list("interactivity")) ],
546 [ sort("my_interactivity") ],
547 "getconf_list lists all interactivity");
549 $dc = lookup_taperscan("my_taperscan");
550 ok($dc, "found my_taperscan");
551 is(taperscan_name($dc), "my_taperscan",
552 "my_taperscan knows its name");
553 is(taperscan_getconf($dc, $TAPERSCAN_COMMENT), 'my taperscan is mine, not yours',
554 "taperscan comment");
555 is(taperscan_getconf($dc, $TAPERSCAN_PLUGIN), 'MY-taperscan',
557 is_deeply(taperscan_getconf($dc, $TAPERSCAN_PROPERTY),
560 'values' => [ 'testval' ],
563 }, "taperscan properties represented correctly");
565 is_deeply([ sort(+getconf_list("taperscan")) ],
566 [ sort("my_taperscan") ],
567 "getconf_list lists all taperscan");
571 # Test config overwrites (using the config from above)
574 $config_overrides = new_config_overrides(1); # note estimate is too small
575 add_config_override($config_overrides, "tapedev", "null:TEST");
576 add_config_override($config_overrides, "tpchanger", "chg-test");
577 add_config_override_opt($config_overrides, "org=KAOS");
578 set_config_overrides($config_overrides);
579 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
581 is(getconf($CNF_TAPEDEV), "null:TEST",
582 "config overwrites work with real config");
583 is(getconf($CNF_ORG), "KAOS",
584 "add_config_override_opt parsed correctly");
588 $config_overrides = new_config_overrides(1);
589 add_config_override($config_overrides, "bogusparam", "foo");
590 set_config_overrides($config_overrides);
591 config_init($CONFIG_INIT_EXPLICIT_NAME, 'TESTCONF');
593 my ($error_level, @errors) = Amanda::Config::config_errors();
594 is($error_level, $CFGERR_ERRORS, "bogus config overwrite flagged as an error");
597 # Test configuration dumping
599 # (uses the config from the previous section)
601 # fork a child and capture its stdout
602 my $pid = open(my $kid, "-|");
603 die "Can't fork: $!" unless defined($pid);
605 Amanda::Config::dump_configuration();
608 my $dump_first_line = <$kid>;
609 my $dump = join'', $dump_first_line, <$kid>;
613 my $fn = Amanda::Config::get_config_filename();
614 my $dump_filename = $dump_first_line;
615 chomp $dump_filename;
616 $dump_filename =~ s/^# AMANDA CONFIGURATION FROM FILE "//g;
617 $dump_filename =~ s/":$//g;
618 is($dump_filename, $fn,
619 "config filename is included correctly");
621 like($dump, qr/DEVICE-PROPERTY\s+"foo" "bar"\n/i,
622 "DEVICE-PROPERTY appears in dump output");
624 like($dump, qr/AMRECOVER-CHECK-LABEL\s+(yes|no)/i,
625 "AMRECOVER-CHECK-LABEL has a trailing space");
627 like($dump, qr/AMRECOVER-CHECK-LABEL\s+(yes|no)/i,
628 "AMRECOVER-CHECK-LABEL has a trailing space");
630 like($dump, qr/EXCLUDE\s+LIST "foo" "bar" "true" "star"/i,
631 "EXCLUDE LIST is in the dump");
632 like($dump, qr/EXCLUDE\s+FILE "foolist"/i,
633 "EXCLUDE FILE is in the dump");
634 like($dump, qr/INCLUDE\s+LIST OPTIONAL "bing" "ting" "string" "fling"/i,
635 "INCLUDE LIST is in the dump");
636 like($dump, qr/INCLUDE\s+FILE OPTIONAL "rhyme"/i,
637 "INCLUDE FILE is in the dump");
638 like($dump, qr/RECOVERY-LIMIT.*SAME-HOST/i,
639 "RECOVERY-LIST is in the dump");
642 # Test nested definitions inside a dumptype
644 $testconf = Installcheck::Config->new();
645 $testconf->add_dumptype('nested_stuff', [
646 'comment' => '"contains a nested application, pp_script"',
659 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
660 is($cfg_result, $CFGERR_OK,
661 "parsing nested config loaded")
662 or diag_config_errors();
664 skip "error loading config", 8 unless $cfg_result == $CFGERR_OK;
666 my $dtyp = lookup_dumptype("nested_stuff");
667 ok($dtyp, "found nested_stuff");
669 my $appname = dumptype_getconf($dtyp, $DUMPTYPE_APPLICATION);
670 like($appname, qr/^custom\(/,
671 "DUMPTYPE_APPLICATION is the generated name of an application subsection");
673 my $app = lookup_application($appname);
674 ok($app, ".. and that name leads to an application object");
675 is(application_getconf($app, $APPLICATION_COMMENT), "my app",
676 ".. that has the right comment");
678 my $sc = dumptype_getconf($dtyp, $DUMPTYPE_SCRIPTLIST);
679 ok(ref($sc) eq 'ARRAY' && @$sc == 1, "DUMPTYPE_SCRIPTLIST returns a 1-element list");
680 like($sc->[0], qr/^custom\(/,
681 ".. and the first element is the generated name of a script subsection");
683 $sc = lookup_pp_script($sc->[0]);
684 ok($sc, ".. and that name leads to a pp_script object");
685 is(pp_script_getconf($sc, $PP_SCRIPT_COMMENT), "my script",
686 ".. that has the right comment");
690 # Explore a quirk of exinclude parsing. Only the last
691 # exclude (or include) directive affects the 'optional' flag.
692 # We may want to change this, but we should do so intentionally.
693 # This is also tested by the 'amgetconf' installcheck.
695 $testconf = Installcheck::Config->new();
696 $testconf->add_dumptype('mydump-type', [
697 'exclude list' => '"foo" "bar"',
698 'exclude list optional append' => '"true" "star"',
699 'exclude list append' => '"true" "star"',
703 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
704 is($cfg_result, $CFGERR_OK,
705 "first exinclude parsing config loaded")
706 or diag_config_errors();
708 skip "error loading config", 2 unless $cfg_result == $CFGERR_OK;
710 my $dtyp = lookup_dumptype("mydump-type");
711 ok($dtyp, "found mydump-type");
712 is(dumptype_getconf($dtyp, $DUMPTYPE_EXCLUDE)->{'optional'}, 0,
713 "'optional' has no effect when not on the last occurrence");
717 # Check out recovery-limit parsing
719 $testconf = Installcheck::Config->new();
720 $testconf->add_param('recovery-limit', '"foo" "bar"');
721 $testconf->add_dumptype('rl1', [
722 'recovery-limit' => 'same-host server',
724 $testconf->add_dumptype('rl2', [
725 'recovery-limit' => '"somehost"',
727 $testconf->add_dumptype('rl3', [
728 'recovery-limit' => 'same-host server "somehost"',
730 $testconf->add_dumptype('rl4', [
731 'recovery-limit' => '"foohost" same-host',
735 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
736 is($cfg_result, $CFGERR_OK,
737 "recovery-limit config loaded")
738 or diag_config_errors();
740 skip "error loading config", 5 unless $cfg_result == $CFGERR_OK;
743 is_deeply(getconf($CNF_RECOVERY_LIMIT),
745 "global recovery-limit parameter");
747 $dtyp = lookup_dumptype("rl1");
748 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_RECOVERY_LIMIT),
749 [ "SAMEHOST-SAMEHOST-SAMEHOST", "SERVER-SERVER-SERVER" ],
750 "same-host => undef in list");
752 $dtyp = lookup_dumptype("rl2");
753 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_RECOVERY_LIMIT),
755 "hostname => match pattern");
757 $dtyp = lookup_dumptype("rl3");
758 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_RECOVERY_LIMIT),
759 [ "SAMEHOST-SAMEHOST-SAMEHOST", "SERVER-SERVER-SERVER", "somehost" ],
760 "hostname and same-host parsed correctly");
762 $dtyp = lookup_dumptype("rl4");
763 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_RECOVERY_LIMIT),
764 [ "SAMEHOST-SAMEHOST-SAMEHOST", "foohost" ], # note that the order is an implementation detail
765 ".. even if same-host comes last");
769 # Check out dump-limit parsing
771 $testconf = Installcheck::Config->new();
772 $testconf->add_dumptype('dl1', [
773 'dump-limit' => 'same-host',
775 $testconf->add_dumptype('dl2', [
776 'dump-limit' => 'server',
778 $testconf->add_dumptype('dl3', [
779 'dump-limit' => 'same-host server',
783 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
784 is($cfg_result, $CFGERR_OK,
785 "dump-limit config loaded")
786 or diag_config_errors();
788 skip "error loading config", 5 unless $cfg_result == $CFGERR_OK;
791 $dtyp = lookup_dumptype("dl1");
792 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_DUMP_LIMIT),
793 [ "SAMEHOST-SAMEHOST-SAMEHOST" ],
794 "same-host => \"SAMEHOST-SAMEHOST-SAMEHOST\" in list");
796 $dtyp = lookup_dumptype("dl2");
797 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_DUMP_LIMIT),
798 [ "SERVER-SERVER-SERVER" ],
799 "server => \"SERVER-SERVER-SERVER\" in list");
801 $dtyp = lookup_dumptype("dl3");
802 is_deeply(dumptype_getconf($dtyp, $DUMPTYPE_DUMP_LIMIT),
803 [ "SAMEHOST-SAMEHOST-SAMEHOST", "SERVER-SERVER-SERVER" ],
804 "same-host and server");
807 $testconf->add_dumptype('dl4', [
808 'dump-limit' => 'same-host server "somehost"',
811 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
812 isnt($cfg_result, $CFGERR_OK,
813 "dump-limit do not accept hostname");
816 # Try an autolabel with a template and 'any'
818 $testconf = Installcheck::Config->new();
819 $testconf->add_param('autolabel', '"FOO%%%BAR" any');
822 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
823 is($cfg_result, $CFGERR_OK,
824 "first exinclude parsing config loaded")
825 or diag_config_errors();
827 skip "error loading config", 1 unless $cfg_result == $CFGERR_OK;
828 is_deeply(getconf($CNF_AUTOLABEL),
829 { template => "FOO%%%BAR", other_config => 1,
830 non_amanda => 1, volume_error => 1, empty => 1 },
831 "'autolabel \"FOO%%%BAR\" any' represented correctly");
835 # Check out where quoting is and is not required.
837 $testconf = Installcheck::Config->new();
839 # make sure an unquoted tapetype is OK
840 $testconf->add_param('tapetype', 'TEST-TAPE'); # unquoted (Installcheck::Config uses quoted)
842 # strings can optionally be quoted
843 $testconf->add_param('dumporder', '"STSTST"');
845 # enumerations (e.g., taperalgo) must not be quoted; implicitly tested above
848 $testconf->add_dumptype('"parent"', [ # note quotes
849 'bumpsize' => '10240',
851 $testconf->add_dumptype('child', [
852 '' => '"parent"', # note quotes
854 $testconf->add_dumptype('child2', [
859 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
860 is($cfg_result, $CFGERR_OK,
861 "parsed config to test strings vs. identifiers")
862 or diag_config_errors();
864 skip "error loading config", 3 unless $cfg_result == $CFGERR_OK;
866 my $dtyp = lookup_dumptype("parent");
867 ok($dtyp, "found parent");
868 $dtyp = lookup_dumptype("child");
869 ok($dtyp, "found child");
870 is(dumptype_getconf($dtyp, $DUMPTYPE_BUMPSIZE), 10240,
871 "child dumptype correctly inherited bumpsize");
875 # Explore a quirk of read_int_or_str parsing.
877 $testconf = Installcheck::Config->new();
878 $testconf->add_dumptype('mydump-type1', [
879 'client_port' => '12345',
881 $testconf->add_dumptype('mydump-type2', [
882 'client_port' => '"newamanda"',
884 $testconf->add_dumptype('mydump-type3', [
885 'client_port' => '"67890"',
889 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
890 is($cfg_result, $CFGERR_OK,
891 "read_int_or_str parsing config loaded")
892 or diag_config_errors();
894 skip "error loading config", 6 unless $cfg_result == $CFGERR_OK;
896 my $dtyp = lookup_dumptype("mydump-type1");
897 ok($dtyp, "found mydump-type1");
898 is(dumptype_getconf($dtyp, $DUMPTYPE_CLIENT_PORT), "12345",
899 "client_port set to 12345");
901 $dtyp = lookup_dumptype("mydump-type2");
902 ok($dtyp, "found mydump-type1");
903 is(dumptype_getconf($dtyp, $DUMPTYPE_CLIENT_PORT), "newamanda",
904 "client_port set to \"newamanda\"");
906 $dtyp = lookup_dumptype("mydump-type3");
907 ok($dtyp, "found mydump-type1");
908 is(dumptype_getconf($dtyp, $DUMPTYPE_CLIENT_PORT), "67890",
909 "client_port set to \"67890\"");
913 # Check property inheritance
915 $testconf = Installcheck::Config->new();
916 $testconf->add_application('app1', [
917 'property' => '"prop1" "val1"'
919 $testconf->add_application('app2', [
920 'property' => 'append "prop2" "val2"'
922 $testconf->add_application('app3', [
923 'property' => '"prop3" "val3"'
925 $testconf->add_application('app1a', [
926 'property' => '"prop4" "val4"',
927 'property' => '"prop1" "val1a"',
930 $testconf->add_application('app2a', [
931 'property' => '"prop5" "val5"',
932 'property' => '"prop2" "val2a"',
935 $testconf->add_application('app3a', [
936 'property' => '"prop6" "val6"',
938 'property' => '"prop7" "val7"'
940 $testconf->add_application('app1b', [
941 'property' => '"prop4" "val4"',
942 'property' => '"prop1" "val1a"',
944 'property' => '"prop1" "val1b"',
946 $testconf->add_application('app2b', [
947 'property' => '"prop5" "val5"',
948 'property' => '"prop2" "val2a"',
950 'property' => 'append "prop2" "val2b"',
954 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
955 is($cfg_result, $CFGERR_OK,
956 "application properties inheritance")
957 or diag_config_errors();
959 skip "error loading config", 15 unless $cfg_result == $CFGERR_OK;
961 my $app = lookup_application("app1a");
962 ok($app, "found app1a");
963 is(application_name($app), "app1a",
964 "app1a knows its name");
965 my $prop = application_getconf($app, $APPLICATION_PROPERTY);
966 is_deeply($prop, { "prop4" => { priority => 0,
968 values => [ "val4" ]},
969 "prop1" => { priority => 0,
971 values => [ "val1" ] }},
972 "PROPERTY parameter of app1a parsed correctly");
974 $app = lookup_application("app2a");
975 ok($app, "found app2a");
976 is(application_name($app), "app2a",
977 "app2a knows its name");
978 $prop = application_getconf($app, $APPLICATION_PROPERTY);
979 is_deeply($prop, { "prop5" => { priority => 0,
981 values => [ "val5" ]},
982 "prop2" => { priority => 0,
984 values => [ "val2a", "val2" ] }},
985 "PROPERTY parameter of app2a parsed correctly");
987 $app = lookup_application("app3a");
988 ok($app, "found app3a");
989 is(application_name($app), "app3a",
990 "app3a knows its name");
991 $prop = application_getconf($app, $APPLICATION_PROPERTY);
992 is_deeply($prop, { "prop3" => { priority => 0,
994 values => [ "val3" ]},
995 "prop6" => { priority => 0,
997 values => [ "val6" ] },
998 "prop7" => { priority => 0,
1000 values => [ "val7" ] }},
1001 "PROPERTY parameter of app3a parsed correctly");
1003 $app = lookup_application("app1b");
1004 ok($app, "found app1b");
1005 is(application_name($app), "app1b",
1006 "app1b knows its name");
1007 $prop = application_getconf($app, $APPLICATION_PROPERTY);
1008 is_deeply($prop, { "prop4" => { priority => 0,
1010 values => [ "val4" ]},
1011 "prop1" => { priority => 0,
1013 values => [ "val1b" ] }},
1014 "PROPERTY parameter of app1b parsed correctly");
1016 $app = lookup_application("app2b");
1017 ok($app, "found app2b");
1018 is(application_name($app), "app2b",
1019 "app2b knows its name");
1020 $prop = application_getconf($app, $APPLICATION_PROPERTY);
1021 is_deeply($prop, { "prop5" => { priority => 0,
1023 values => [ "val5" ]},
1024 "prop2" => { priority => 0,
1026 values => [ "val2a", "val2", "val2b" ] }},
1027 "PROPERTY parameter of app2b parsed correctly");
1032 # Check getconf_byname and getconf_byname_strs
1034 $testconf = Installcheck::Config->new();
1035 $testconf->add_param('tapedev', '"thats a funny name"');
1036 $testconf->add_application('app1', [
1037 'comment' => '"one"',
1039 $testconf->add_script('scr1', [
1040 'comment' => '"one"',
1042 # check old names, too
1043 $testconf->add_text(<<EOF);
1044 define application-tool "app2" {
1048 $testconf->add_text(<<EOF);
1049 define script-tool "scr2" {
1055 $cfg_result = config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
1056 is($cfg_result, $CFGERR_OK,
1058 or diag_config_errors();
1060 skip "error loading config", 7 unless $cfg_result == $CFGERR_OK;
1062 is(getconf_byname("Tapedev"), "thats a funny name",
1063 "getconf_byname for global param");
1064 is_deeply([ getconf_byname_strs("Tapedev", 1) ],
1065 [ "\"thats a funny name\"" ],
1066 "getconf_byname_strs for global param with quotes");
1067 is_deeply([ getconf_byname_strs("Tapedev", 0) ],
1068 [ "thats a funny name" ],
1069 "getconf_byname_strs for global param without quotes");
1071 # test * and *-tool (the old name)
1072 is(getconf_byname("application-tool:app1:comment"), "one",
1073 "getconf_byname for appplication-tool param");
1074 is(getconf_byname("application:app2:comment"), "two",
1075 "getconf_byname for application param");
1076 is(getconf_byname("script-tool:scr1:comment"), "one",
1077 "getconf_byname for appplication-tool param");
1078 is(getconf_byname("script:scr2:comment"), "two",
1079 "getconf_byname for script param");
1082 my @boolean_vals = (
1083 {'val' => '1', 'expected' => 1},
1084 {'val' => '0', 'expected' => 0},
1085 {'val' => 't', 'expected' => 1},
1086 {'val' => 'true', 'expected' => 1},
1087 {'val' => 'f', 'expected' => 0},
1088 {'val' => 'false', 'expected' => 0},
1089 {'val' => 'y', 'expected' => 1},
1090 {'val' => 'yes', 'expected' => 1},
1091 {'val' => 'n', 'expected' => 0},
1092 {'val' => 'no', 'expected' => 0},
1093 {'val' => 'on', 'expected' => 1},
1094 {'val' => 'off', 'expected' => 0},
1095 {'val' => 'oFf', 'expected' => 0},
1096 {'val' => 'foo', 'expected' => undef},
1099 for my $bv (@boolean_vals) {
1100 is(string_to_boolean($bv->{'val'}), $bv->{'expected'},
1101 "string_to_boolean('$bv->{'val'}') is right");
1105 {'val' => '', 'expected' => ''},
1106 {'val' => 'prop-name', 'expected' => 'prop-name'},
1107 {'val' => 'PRoP-NaME', 'expected' => 'prop-name'},
1108 {'val' => 'prop_name', 'expected' => 'prop-name'},
1109 {'val' => 'FaNCy_ProP', 'expected' => 'fancy-prop'},
1110 {'val' => '_under_', 'expected' => '-under-'},
1111 {'val' => '-dash-', 'expected' => '-dash-'},
1112 {'val' => '-', 'expected' => '-'},
1113 {'val' => '_', 'expected' => '-'},
1116 for my $pn (@prop_names) {
1117 is(amandaify_property_name($pn->{'val'}), $pn->{'expected'},
1118 "amandaify_property_name('$pn->{'val'}') is right");
1121 $testconf = Installcheck::Config->new();
1122 $testconf->add_param('property', '"PrOP_nAme" "VALUE"');
1124 config_init($CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
1125 my $properties = getconf($CNF_PROPERTY);
1128 {'val' => 'prop-name'},
1129 {'val' => 'PRoP-NaME'},
1130 {'val' => 'prop_name'},
1131 {'val' => 'PROP_NAME'},
1132 {'val' => 'PRoP-NaME'},
1133 {'val' => 'prop_name'},
1136 for my $pn (@prop_names) {
1137 is_deeply($properties->{$pn->{'val'}}->{values}, [ "VALUE" ]);
1140 $testconf = Installcheck::Config->new();
1141 $testconf->add_client_config_param('amdump-server', '"amdump.localhost"');
1142 $testconf->add_client_config_param('index-server', '"index.localhost"');
1143 $testconf->add_client_config_param('tape-server', '"tape.localhost"');
1145 config_init($CONFIG_INIT_CLIENT | $CONFIG_INIT_EXPLICIT_NAME, "TESTCONF");
1146 my $amdump_server = getconf($CNF_AMDUMP_SERVER);
1147 is ($amdump_server, "amdump.localhost", "amdump-server is \"amdump.localhost\"");
1148 my $index_server = getconf($CNF_INDEX_SERVER);
1149 is ($index_server, "index.localhost", "index-server is \"index.localhost\"");
1150 my $tape_server = getconf($CNF_TAPE_SERVER);
1151 is ($tape_server, "tape.localhost", "amdump is \"tape.localhost\"");