File Coverage

blib/lib/Xcruciate/XcruciateConfig.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Xcruciate::XcruciateConfig;
2              
3 2     2   11 use Exporter;
  2         4  
  2         138  
4             @ISA = ('Exporter');
5             @EXPORT = qw();
6             our $VERSION = 0.21;
7              
8 2     2   11 use strict;
  2         4  
  2         62  
9 2     2   13 use warnings;
  2         3  
  2         46  
10 2     2   12 use Carp;
  2         3  
  2         188  
11 2     2   1248 use Xcruciate::Utils 0.21;
  0            
  0            
12              
13             our $default_xcr_executable_dir = '/usr/local/bin';
14              
15             =head1 NAME
16              
17             Xcruciate::XcruciateConfig - OO API for reading xcruciate config files.
18              
19             =head1 SYNOPSIS
20              
21             my $config=Xcruciate::XcruciateConfig->new('xcruciate.conf');
22              
23             my $xacd_path=$config->xacd_path;
24              
25             my @unit_paths=$config->unit_config_files;
26              
27             =head1 DESCRIPTION
28              
29             Xcruciate::XcruciateConfig is part of the Xcruciate project (F). It provides an
30             OO interface to an xcruciate configuration file.
31              
32             The methods all take an optional verbose argument. If this is perlishly true the
33             methods will show their working to STDOUT.
34              
35             At present the methods look for configuration errors and die noisily if they find any.
36             This is useful behaviour for management scripts - continuing to set up server daemons
37             on the basis of broken configurations is not best practice - but non-fatal error
38             reporting could be provided if/when an application requires it.
39              
40             =head1 AUTHOR
41              
42             Mark Howe, Emelonman@cpan.orgE
43              
44             =head2 EXPORT
45              
46             None
47              
48             =cut
49              
50             #Records fields:
51             # scalar/list
52             # Optional? (1 means 'yes')
53             # data type
54             # data type specific fields:
55             # min, max for numbers
56             # required permissions for files/directories
57              
58             my $xcr_settings = {
59             'config_type', [ 'scalar', 0, 'word' ],
60             'restart_sleep', [ 'scalar', 1, 'duration' ],
61             'start_test_sleep', [ 'scalar', 1, 'duration' ],
62             'stop_test_sleep', [ 'scalar', 1, 'duration' ],
63             'unit_config_files', [ 'list', 0, 'abs_file', 'r' ],
64             'xacd_path', [ 'scalar', 1, 'abs_file', 'x' ],
65             'xted_path', [ 'scalar', 1, 'abs_file', 'x' ],
66             'mxmlc_path', [ 'scalar', 1, 'abs_file', 'x' ],
67             'fop_path', [ 'scalar', 1, 'abs_file', 'x' ],
68             'xmlroff_path', [ 'scalar', 1, 'abs_file', 'x' ]
69             };
70              
71             =head1 METHODS
72              
73             =head2 new(config_file_path [,verbose])
74              
75             Creates and returns an Xcruciate::XcruciateConfig object which can then be queried.
76              
77             =cut
78              
79             sub new {
80             my $class = shift;
81             my $path = shift;
82             my $verbose = 0;
83             $verbose = shift if defined $_[0];
84             my $with_defaults = 0;
85             $with_defaults = shift if defined $_[0];
86             my $self = {};
87              
88             local_croak(
89             Xcruciate::Utils::check_path( 'xcruciate config file', $path, 'r', 1 )
90             );
91             print "Attempting to parse xcruciate config file... " if $verbose;
92             my $parser = XML::LibXML->new();
93             my $xcr_dom = $parser->parse_file($path);
94             my @config = $xcr_dom->findnodes("/config/scalar");
95             croak
96             "Config file doesn't look anything like a config file - 'xcruciate file_help' for some clues"
97             unless $config[0];
98             my @config_type =
99             $xcr_dom->findnodes("/config/scalar[\@name='config_type']/text()");
100             croak "config_type entry not found in unit config file"
101             unless $config_type[0];
102             my $config_type = $config_type[0]->toString;
103             croak
104             "config_type in unit config file is '$config_type' (should be 'xcruciate' or 'unit')"
105             unless $config_type =~ /^((xcruciate)|(unit))$/;
106              
107             if ( $config_type eq 'unit' ) {
108             my $instant_config = <<"HERE";
109            
110            
111             xcruciate
112            
113             $path
114            
115            
116             HERE
117             $xcr_dom = $parser->parse_string($instant_config);
118             }
119             my @errors = ();
120             foreach my $entry (
121             $xcr_dom->findnodes(
122             "/config/*[(local-name() = 'scalar') or (local-name() = 'list')]")
123             )
124             {
125             push @errors,
126             sprintf( "No name attribute for element '%s'", $entry->nodeName )
127             unless $entry->hasAttribute('name');
128             my $entry_record = $xcr_settings->{ $entry->getAttribute('name') };
129             if ( not defined $entry_record ) {
130             carp "Unknown xcruciate config entry '"
131             . ( $entry->getAttribute('name') ) . "'";
132             }
133             elsif ( not( $entry->nodeName eq $entry_record->[0] ) ) {
134             push @errors,
135             sprintf(
136             "Entry called %s should be a %s not a %s",
137             $entry->getAttribute('name'),
138             $entry_record->[0], $entry->nodeName
139             );
140             }
141             elsif ( ( not $entry->textContent )
142             and
143             ( ( not $entry_record->[1] ) or $entry->textContent !~ /^\s*$/s ) )
144             {
145             push @errors,
146             sprintf( "Entry called %s requires a value",
147             $entry->getAttribute('name') );
148             }
149             elsif (
150             ( $entry->nodeName eq 'scalar' )
151             and $entry_record->[2]
152             and ( ( not $entry_record->[1] )
153             or $entry->textContent !~ /^\s*$/s
154             or $entry->textContent )
155             )
156             {
157             push @errors,
158             Xcruciate::Utils::type_check( '', $entry->getAttribute('name'),
159             $entry->textContent, $entry_record );
160             }
161             elsif ( ( $entry->nodeName eq 'list' ) and $entry_record ) {
162             my @items = $entry->findnodes('item/text()');
163             push @errors,
164             sprintf( "Entry called %s requires at least one item",
165             $entry->getAttribute('name') )
166             if ( ( not $entry_record->[2] ) and ( not @items ) );
167             my $count = 1;
168             foreach my $item (@items) {
169             push @errors,
170             Xcruciate::Utils::type_check( '',
171             $entry->getAttribute('name'),
172             $item->textContent, $entry_record, $count );
173             $count++;
174             }
175             }
176             push @errors,
177             sprintf( "Duplicate entry called %s", $entry->getAttribute('name') )
178             if defined $self->{ $entry->getAttribute('name') };
179             if ( $entry->nodeName eq 'scalar' ) {
180             $self->{ $entry->getAttribute('name') } = $entry->textContent;
181             }
182             else {
183             $self->{ $entry->getAttribute('name') } = []
184             unless defined $self->{ $entry->getAttribute('name') };
185             foreach my $item ( $entry->findnodes('item/text()') ) {
186             push @{ $self->{ $entry->getAttribute('name') } },
187             $item->textContent;
188             }
189             }
190             }
191             foreach my $entry ( keys %{$xcr_settings} ) {
192             push @errors, sprintf( "No xcruciate entry called %s", $entry )
193             unless ( ( defined $self->{$entry} )
194             or ( $xcr_settings->{$entry}->[1] ) );
195             }
196             if ( $with_defaults and not(@errors) ) {
197             unless ( $self->{xacd_path} ) {
198             local_croak(
199             Xcruciate::Utils::check_path(
200             'default xacd path',
201             "$default_xcr_executable_dir/xacd",
202             'x', 1
203             )
204             );
205             $self->{xacd_path} = "$default_xcr_executable_dir/xacd";
206             }
207             unless ( $self->{xted_path} ) {
208             $self->{xted_path} = "$default_xcr_executable_dir/xted"
209             if -x "$default_xcr_executable_dir/xted";
210             }
211             unless ( $self->{restart_sleep} ) {
212             $self->{restart_sleep} = 2;
213             }
214             unless ( $self->{start_test_sleep} ) {
215             $self->{start_test_sleep} = 1;
216             }
217             unless ( $self->{stop_test_sleep} ) {
218             $self->{stop_test_sleep} = 1;
219             }
220             }
221              
222             if (@errors) {
223             print join "\n", @errors;
224             print "\n";
225             croak "Errors in xcruciate config file - cannot continue";
226             }
227             else {
228             bless( $self, $class );
229             print "done\n" if $verbose;
230             return $self;
231             }
232             }
233              
234             =head2 xcr_file_format_description()
235              
236             Returns multi-lined human-friendly description of the xcr config file
237              
238             =cut
239              
240             sub xcr_file_format_description {
241             my $self = shift;
242             my $ret = '';
243             foreach my $entry ( sort keys %{$xcr_settings} ) {
244             my $record = $xcr_settings->{$entry};
245             $ret .= "$entry (";
246             $ret .= "optional " if $record->[1];
247             $ret .= "$record->[0])";
248             if ( not $record->[2] ) {
249             }
250             elsif ( ( $record->[2] eq 'integer' ) or ( $record->[2] eq 'float' ) ) {
251             $ret .= " - $record->[2]";
252             $ret .= " >= $record->[3]" if defined $record->[3];
253             $ret .= " and <= $record->[4]" if defined $record->[4];
254             }
255             elsif ( $record->[2] eq 'ip' ) {
256             $ret .= " - ip address";
257             }
258             elsif ( $record->[2] eq 'word' ) {
259             $ret .= " - word (ie no whitespace)";
260             }
261             elsif ( $record->[2] eq 'path' ) {
262             $ret .= " - path (currently a word)";
263             }
264             elsif ( $record->[2] eq 'xml_leaf' ) {
265             $ret .= " - filename with an xml suffix";
266             }
267             elsif ( $record->[2] eq 'xsl_leaf' ) {
268             $ret .= " - filename with an xsl suffix";
269             }
270             elsif ( $record->[2] eq 'yes_no' ) {
271             $ret .= " - 'yes' or 'no'";
272             }
273             elsif ( $record->[2] eq 'email' ) {
274             $ret .= " - email address";
275             }
276             elsif ( $record->[2] eq 'abs_dir' ) {
277             $ret .= " - absolute directory path with $record->[3] permissions";
278             }
279             elsif ( $record->[2] eq 'abs_file' ) {
280             $ret .= " - absolute file path with $record->[3] permissions";
281             }
282             elsif ( $record->[2] eq 'abs_create' ) {
283             $ret .=
284             " - absolute file path with $record->[3] permissions for directory";
285             }
286             $ret .= "\n";
287             }
288             return $ret;
289             }
290              
291             =head2 restart_sleep()
292              
293             Returns the time to sleep for between stopping and starting during a restart.
294              
295             =cut
296              
297             sub restart_sleep {
298             my $self = shift;
299             return $self->{restart_sleep};
300             }
301              
302             =head2 start_test_sleep()
303              
304             Returns the time to sleep for between starting a process and checking that it started correctly.
305              
306             =cut
307              
308             sub start_test_sleep {
309             my $self = shift;
310             return $self->{start_test_sleep};
311             }
312              
313             =head2 stop_test_sleep()
314              
315             Returns the time to sleep for between killing a process and checking that it died.
316              
317             =cut
318              
319             sub stop_test_sleep {
320             my $self = shift;
321             return $self->{stop_test_sleep};
322             }
323              
324             =head2 xacd_path()
325              
326             Returns the path to the xacd executable.
327              
328             =cut
329              
330             sub xacd_path {
331             my $self = shift;
332             return $self->{xacd_path};
333             }
334              
335             =head2 xted_path()
336              
337             Returns the path to the xted executable.
338              
339             =cut
340              
341             sub xted_path {
342             my $self = shift;
343             return $self->{xted_path};
344             }
345              
346             =head2 mxmlc_path()
347              
348             Returns the path to the mxmlc (Flex 3) executable.
349              
350             =cut
351              
352             sub mxmlc_path {
353             my $self = shift;
354             return $self->{mxmlc_path};
355             }
356              
357             =head2 fop_path()
358              
359             Returns the path to the fop executable.
360              
361             =cut
362              
363             sub fop_path {
364             my $self = shift;
365             return $self->{fop_path};
366             }
367              
368             =head2 xmlroff_path()
369              
370             Returns the path to the xmlroff executable.
371              
372             =cut
373              
374             sub xmlroff_path {
375             my $self = shift;
376             return $self->{xmlroff_path};
377             }
378              
379             =head2 unit_config_files()
380              
381             Returns a list of paths to Xacerbate configuration files.
382              
383             =cut
384              
385             sub unit_config_files {
386             my $self = shift;
387             return @{ $self->{unit_config_files} };
388             }
389              
390             =head2 local_croak()
391              
392             Function for croaking
393              
394             =cut
395              
396             sub local_croak {
397             my $message = shift;
398             croak $message if $message;
399             }
400              
401             =head1 BUGS
402              
403             The best way to report bugs is via the Xcruciate bugzilla site (F).
404              
405             =head1 PREVIOUS VERSIONS
406              
407             =over
408              
409             B<0.01>: First upload
410              
411             B<0.03>: First upload containing module
412              
413             B<0.04>: Changed minimum perl version to 5.8.8
414              
415             B<0.05>: Warn about unknown entries
416              
417             B<0.07>: Attempt to put all Xcruciate modules in one PAUSE tarball
418              
419             B<0.08>: Global version upgrade
420              
421             B<0.09>: Made most entries optional. Use Carp for errors
422              
423             B<0.10>: Prepend path entry to relative paths
424              
425             B<0.12>: Improvise Xcruciate config file if provided with Unit config file
426              
427             B<0.14>: Global update
428              
429             B<0.16>: Global update
430              
431             B<0.17>: use warnings
432              
433             B<0.18>: Global update
434              
435             B<0.19>: Use XML Schema durations
436              
437             B<0.20>: Optional paths for fop, mxmlc and xmlroff
438              
439             =back
440              
441             =head1 COPYRIGHT AND LICENSE
442              
443             Copyright (C) 2007 - 2009 by SARL Cyberporte/Menteith Consulting
444              
445             This library is distributed under BSD licence (F).
446              
447             =cut
448              
449             1;