diff --git a/ncm-sysconfig/src/main/pan/components/sysconfig/schema.pan b/ncm-sysconfig/src/main/pan/components/sysconfig/schema.pan index a2740866fc..6a462e35d4 100755 --- a/ncm-sysconfig/src/main/pan/components/sysconfig/schema.pan +++ b/ncm-sysconfig/src/main/pan/components/sysconfig/schema.pan @@ -2,16 +2,34 @@ # ${developer-info} # ${author-info} - declaration template components/sysconfig/schema; include 'quattor/schema'; +@documentation{ +Contents of a sysconfig file modelled as a dict of key-value pairs. + +Two reserved keys `prologue` and `epilogue` are treated specially, +their values will be copied verbatim into the file before or after the key-pair definitions. + +Example: + +'/software/components/sysconfig/files/scfg' = dict( + 'epilogue', 'export LANG=C', + 'KEY', 'VALUE', +); + +This will create the file `/etc/sysconfig/scfg` which contains: + +KEY=VALUE +export LANG=C +} +type component_sysconfig_file = string(1..){}; + type component_sysconfig = { include structure_component - 'files' ? string{}{} + @{ dict of dicts with a file name as the first key and the contents of each file as the child dict. } + 'files' ? component_sysconfig_file{} }; bind '/software/components/sysconfig' = component_sysconfig; - - diff --git a/ncm-sysconfig/src/main/perl/sysconfig.pm b/ncm-sysconfig/src/main/perl/sysconfig.pm index 6e6c4d1d05..97fa11d385 100755 --- a/ncm-sysconfig/src/main/perl/sysconfig.pm +++ b/ncm-sysconfig/src/main/perl/sysconfig.pm @@ -1,110 +1,121 @@ #${PMcomponent} -use parent qw(NCM::Component); +=head1 NAME + +sysconfig: management of sysconfig files + +=head1 DESCRIPTION + +The I component manages system configuration files in +C<< /etc/sysconfig >> . These are files which contain key-value pairs. +However there is the possibility to add verbatim text either before or after the key-value pair definitions. + +=cut + +use parent qw(NCM::Component CAF::Path); + our $EC = LC::Exception::Context->new->will_store_all; -use LC::Check; +our $NoActionSupported = 1; + +use Readonly; -use File::Path; -use File::Basename; +Readonly my $QUOTE => "\""; +Readonly my $SYSCONFIGDIR => "/etc/sysconfig"; # The base directory for sysconfig files. +sub filelist_read +{ + my ($self) = @_; + + # Read first the list of sysconfig files which have been + # previously managed by this component. These will have to + # be deleted if no longer in the configuration. + my %filelist; + my $fh = CAF::FileReader->open("$SYSCONFIGDIR/ncm-sysconfig", log => $self); + while (my $line = <$fh>) { + chomp($line); + $filelist{$line} = 1; + } + $fh->close(); + + return %filelist; +} + +sub filelist_write +{ + my ($self, %filelist) = @_; + + # Write the list of managed configuration files. + my $fh = CAF::FileWriter->open("$SYSCONFIGDIR/ncm-sysconfig", log => $self); + for my $file (keys %filelist) { + print $fh "$file\n"; + } + $fh->close(); + + return 1; +} sub Configure { + my ($self, $config) = @_; + + # Load configuration into a hash + my $sysconfig_config = $config->getTree($self->prefix()); + + # Ensure that sysconfig directory exists. + $self->directory($SYSCONFIGDIR, owner=>0, group=>0, mode=>0755); + + # This will be a list of the configuration files managed by this component. + my %files_managed; + + my %files_previous = $self->filelist_read(); + + # Loop over all of the defined files, writing each as necessary. + if ( $sysconfig_config->{files} ) { + foreach my $file (sort keys %{$sysconfig_config->{files}}) { + + my $pairs = $sysconfig_config->{files}->{$file}; + + # Start with an empty file. + my $contents = ''; + + # Add the prologue if it exists. + if (defined($pairs->{prologue})) { + $contents .= "$pairs->{prologue}\n"; + } + + # Loop over the pairs adding the information to the file. + for my $key (sort keys %$pairs) { + if ($key ne 'prologue' && $key ne 'epilogue') { + $contents .= "$key=$pairs->{$key}\n"; + } + } + + # Add the epilogue if it exists. + if (defined($pairs->{epilogue})) { + $contents .= "$pairs->{epilogue}\n"; + } + + # Now actually update the file, if needed. + my $fh = CAF::FileWriter->open("$SYSCONFIGDIR/$file", backup => ".old", log => $self); + print $fh $contents; + $fh->close(); + + # Remove this file from the list of old configuration + # files add to the new configuration files. + delete($files_previous{"$SYSCONFIGDIR/$file"}); + $files_managed{"$SYSCONFIGDIR/$file"} = 1; + } + } + + # Remove any old configuration files which haven't been updated. + for my $file (keys %files_previous) { + $self->cleanup($file); + } + + $self->filelist_write(%files_managed); - my ($self, $config) = @_; - - # Define paths for convenience. - my $base = "/software/components/sysconfig"; - - # The base directory for sysconfig files. - my $sysconfigdir = "/etc/sysconfig"; - - # Load configuration into a hash - my $sysconfig_config = $config->getElement($base)->getTree(); - - # Ensure that sysconfig directory exists. - mkpath($sysconfigdir,0,0755) unless (-e $sysconfigdir); - if (! -d $sysconfigdir) { - $self->error("$sysconfigdir isn't a directory or can't be created"); - return 1; - } - - # This will be a list of the configuration files managed by this component. - my %newcfg; - - # Read first the list of sysconfig files which have been - # previously managed by this component. These will have to - # be deleted if no longer in the configuration. - my %oldcfg; - if (-f "$sysconfigdir/ncm-sysconfig") { - open CONF, "<", "$sysconfigdir/ncm-sysconfig"; - while () { - chomp; - $oldcfg{$_} = 1; - } - } - - # Loop over all of the defined files, writing each as necessary. - if ( $sysconfig_config->{files} ) { - for my $file (sort keys %{$sysconfig_config->{files}}) { - - my $pairs = $sysconfig_config->{files}->{$file}; - - # Start with an empty file. - my $contents = ''; - - # Add the prologue if it exists. - if (defined($pairs->{"prologue"})) { - $contents .= $pairs->{"prologue"} . "\n"; - } - - # Loop over the pairs adding the information to the file. - for my $key (sort keys %{$pairs}) { - if ($key ne 'prologue' && $key ne 'epilogue') { - $contents .= $key . '=' . $pairs->{$key} . "\n"; - } - } - - # Add the epilogue if it exists. - if (defined($pairs->{"epilogue"})) { - $contents .= $pairs->{"epilogue"} . "\n"; - } - - # Now actually update the file, if needed. - - my $result = LC::Check::file("$sysconfigdir/$file", - backup => ".old", - contents => $contents, - ); - unless ( $result >= 0 ) { - $self->error("Error updating file $sysconfigdir/$file"); - } - - # Remove this file from the list of old configuration - # files add to the new configuration files. - delete($oldcfg{"$sysconfigdir/$file"}); - $newcfg{"$sysconfigdir/$file"} = 1; - } - } - - # Remove any old configuration files which haven't been updated. - for my $file (keys %oldcfg) { - unlink $file if (-e $file); - } - - # Write the list of managed configuration files. - if(open CONF, ">", "$sysconfigdir/ncm-sysconfig") { - for my $file (keys %newcfg) { - print CONF $file . "\n"; - } - close CONF; - } else { - $self->error("error writing file $sysconfigdir/ncm-sysconfig"); - return 1; - } - - return 0; + return 1; } -1; # Required for PERL modules +1; # Required for PERL modules diff --git a/ncm-sysconfig/src/main/perl/sysconfig.pod b/ncm-sysconfig/src/main/perl/sysconfig.pod deleted file mode 100755 index adf9e68bf0..0000000000 --- a/ncm-sysconfig/src/main/perl/sysconfig.pod +++ /dev/null @@ -1,46 +0,0 @@ -# ${license-info} -# ${developer-info} -# ${author-info} - - -=head1 NAME - -sysconfig: management of sysconfig files - -=head1 DESCRIPTION - -The I component manages system configuration files in -C<< /etc/sysconfig >> . These are files which contain key-value pairs. -However there is the possibility to add verbatim text either -before or after the key-value pair definitions. - -=head1 FILE DEFINITIONS - -=over - -=item * C<< /software/components/sysconfig/files >> - -This is an nlist which has the file name (unescaped) as the key, and -the content information as the value. The value is an nlist. - -=item * C<< /software/components/sysconfig/files// >> - -This is a nlist containing key-value pairs. Both are strings. -There are two special keys C<< prologue >> and C<< epilogue >> which contain -text which will be copied verbatim into the file before or after -the pair definitions, respectively. - -=back - -=head1 EXAMPLE - - "/software/components/sysconfig/files/scfg" = - nlist("epilogue", "export LANG=C", - "KEY", "VALUE"); - -This will create the file C<< /etc/sysconfig/scfg >> which contains: - - KEY=VALUE - export LANG=C - -=cut diff --git a/ncm-sysconfig/src/test/perl/configure.t b/ncm-sysconfig/src/test/perl/configure.t new file mode 100644 index 0000000000..5d075a7b9b --- /dev/null +++ b/ncm-sysconfig/src/test/perl/configure.t @@ -0,0 +1,29 @@ +use strict; +use warnings; +use Test::More; +use Test::Quattor qw(configure); +use NCM::Component::sysconfig; + +$CAF::Object::NoAction = 1; + +my $cmp = NCM::Component::sysconfig->new('sysconfig'); +my $cfg = get_config_for_profile('configure'); + +# Set up list of previously managed files +set_file_contents('/etc/sysconfig/ncm-sysconfig', "/etc/sysconfig/delete_me\n/etc/sysconfig/examplefile\n"); + +# Set up existing files with garbage contents +set_file_contents('/etc/sysconfig/delete_me', "DELETE_ME=delete_me\n"); +set_file_contents('/etc/sysconfig/examplefile', "NOPE=nope_nope\n"); + +is($cmp->Configure($cfg), 1, "Does the component run correctly with a test profile?"); + +isa_ok(get_file('/etc/sysconfig/ncm-sysconfig'), "CAF::FileWriter", "Is the file-handle for the list of managed files the correct class?"); +is(get_file_contents('/etc/sysconfig/ncm-sysconfig'), "/etc/sysconfig/examplefile\n", "Is the list of managed files updated correctly?"); + +is(get_file_contents('/etc/sysconfig/delete_me'), undef, "Has the previously managed file been deleted?"); + +isa_ok(get_file('/etc/sysconfig/examplefile'), "CAF::FileWriter", "Is the file-handle for the example file the correct class?"); +is(get_file_contents('/etc/sysconfig/examplefile'), "key1=testvalue\nkey2=valuetest\n", "Does the example file have the correct contents?"); + +done_testing(); diff --git a/ncm-sysconfig/src/test/resources/configure.pan b/ncm-sysconfig/src/test/resources/configure.pan new file mode 100644 index 0000000000..4a41d6130f --- /dev/null +++ b/ncm-sysconfig/src/test/resources/configure.pan @@ -0,0 +1,8 @@ +object template configure; + +include 'components/sysconfig/schema'; + +prefix '/software/components/sysconfig'; + +'files/examplefile/key1' = 'testvalue'; +'files/examplefile/key2' = 'valuetest';