Simple Text edits
Problem: doing minor non-invasive edits
There is currently no way to simply describe various small modifications on the galores of minor configuration files.
Solution
Here are several definitions to handle localized changes to configuration files.
Ensure that the line "line" exists in "file" from
http://git.black.co.at/?p=manifests.git;a=blob;f=modules/common/manifests/defines/line.pp;hb=HEAD :
define line($file, $line, $ensure = 'present') {
case $ensure {
default : { err ( "unknown ensure value ${ensure}" ) }
present: {
exec { "/bin/echo '${line}' >> '${file}'":
unless => "/bin/grep -qFx '${line}' '${file}'"
}
}
absent: {
exec { "/usr/bin/perl -ni -e 'print unless /^\\Q${line}\\E\$/' '${file}'":
onlyif => "/bin/grep -qFx '${line}' '${file}'"
}
}
}
}
Usage example: Add 'dummy' to the list of automatically loaded modules
file { "/etc/modules": ensure => present, }
line { dummy_module:
file => "/etc/modules",
line => "dummy",
}
Usage example: Remove 'dummy' from the list of automatically loaded modules
file { "/etc/modules": ensure => present, }
line { dummy_module:
file => "/etc/modules",
line => "dummy",
ensure => absent
}
This can easily adapted to removing lines by patter by removing the \Q and \E from the perl call.
You can also extend the preceding recipe to comment or uncomment lines in a shell script. Add the following anywhere into the case statement. Note that I am using pattern matching, not whole line matching here.
uncomment: {
exec { "/bin/sed -i -e'/${line}/s/^#\+//' '${file}'":
onlyif => "/bin/grep '${line}' '${file}' | /bin/grep '^#' | /usr/bin/wc -l"
}
}
comment: {
exec { "/bin/sed -i -e'/${line}/s/^\(.\+\)$/#\1/' '${file}'":
onlyif => "/usr/bin/test `/bin/grep '${line}' '${file}' | /bin/grep -v '^#' | /usr/bin/wc -l` -ne 0"
}
}
Usage example (activate logsentry with vixie-cron):
line { "logsentrycron":
file => "/etc/cron.hourly/logsentry.cron",
line => "logcheck.sh",
ensure => uncomment
}
Ensure that a line exists, and put it at the top instead of the bottom:
define prepend_if_no_such_line($file, $line, $refreshonly = 'false') {
exec { "/usr/bin/perl -p0i -e 's/^/$line\n/;' '$file'":
unless => "/bin/grep -Fxqe '$line' '$file'",
path => "/bin",
refreshonly => $refreshonly,
}
}
Delete lines matching a pattern from a file (using extended POSIX regexp):
define delete_lines($file, $pattern) {
exec { "/bin/sed -i -r -e '/$pattern/d' $file":
onlyif => "/bin/grep -E '$pattern' '$file'",
}
}
Usage example: remove sysctl 'kernel.printk' from /etc/sysctl.conf:
delete_lines { suppress_printk:
file => "/etc/sysctl.conf",
pattern => "^[[:space:]]*kernel[/.]printk[=[:space:]]+",
}
Replace a perl regexp with a string:
define replace($file, $pattern, $replacement) {
$pattern_no_slashes = slash_escape($pattern)
$replacement_no_slashes = slash_escape($replacement)
exec { "/usr/bin/perl -pi -e 's/$pattern_no_slashes/$replacement_no_slashes/' '$file'":
onlyif => "/usr/bin/perl -ne 'BEGIN { \$ret = 1; } \$ret = 0 if /$pattern_no_slashes/ && ! /$replacement_no_slashes/ ; END { exit \$ret; }' '$file'",
}
}
The slash_escape() is defined by putting the following sourcecode into $rubylibdir/puppet/parser/functions/slash_escape.rb. For more details, see Writing Your Own Functions.
# escape slashes in a String
module Puppet::Parser::Functions
newfunction(:slash_escape, :type => :rvalue) do |args|
args[0].gsub(/\//, '\\/')
end
end
Usage example: manage parameters in /etc/munin/munin-node.conf, only disturbing the file when needed
define munin_parameter($ensure) {
replace { set_munin_node_port:
file => "/etc/munin/munin-node.conf",
pattern => "^$name (?!$ensure).*",
replacement => "$name $ensure # set by puppet",
}
}
munin_parameter {
"hostname": ensure => $fqdn;
"port": ensure => 4950;
}
Attention: This only works for parameters which already exist in the config file. A variation on the line define can be used to ensure that.
Discussion
I'm still not satisfied with using perl in the last definition, but no other tool has the "(?!pattern)" negative look-ahead to do the onlyif condition in one command. and I still prefer starting a perl process to forking of several subprocesses which pipe around potentially big files. -- David Schmitt?
Replace the rest of a line given the beginning of the line
From http://mail.madstop.com/pipermail/puppet-users/2007-August/004043.html :
define ensure_key_value($file, $key, $value, $delimeter = " ") {
# append line if "$key" not in "$file"
exec { "echo '$key$delimeter$value' >> $file":
unless => "grep -qe '^$key[[:space:]]*$delimeter' -- $file",
path => "/bin:/usr/bin"
}
# update it if it already exists...
exec { "sed -i '' 's/^$key$delimeter.*$/$key$delimeter$value/g' $file":
unless => "grep -xqe '$key[[:space:]]*$delimeter[[:space:]]*$value' --
$file",
path => "/bin:/usr/bin"
}
}