Linux servers in my company are managed by Puppet.
There's a DNS module which configures /etc/resolv.conf
on all servers based on physical location which is configured as a facter
value.
As you know a /etc/resolv.conf
file looks like so:
search domain.local
nameserver 1.1.1.1
nameserver 2.2.2.2
All servers's host names in the company end with two digits, for example:
proxy73
In order to split DNS network traffic between the two DNS servers I've written a new puppet module which cuts the last two digits of the host name and if it's an odd number then the /etc/resolv.conf
file should look like shown above, but if the digits create an uneven number then the /etc/resolv.conf
file should look like so:
search domain.local
nameserver 2.2.2.2
nameserver 1.1.1.1
But my problem is that no matter how I write the manifest, the lines are always ordered as the first server and then the second instead of second server and then first server.
The relevant portion of the manifest I wrote looks like that (please refer to the part under if $::oddip == false
cause that's the part that doesn't work):
class dns_new::config {
case $::dcd {
'ny4': {
if $::oddip == 'true' {
file_line { "ny4 search domain":
ensure => present,
line => "${::dns_new::params::searchdomny4}",
path => "/etc/resolv.conf",
}
file_line { "ny4dns1 first":
ensure => present,
line => "${::dns_new::params::ny4dns1}",
path => "/etc/resolv.conf",
}
file_line { "ny4dns2 second":
ensure => present,
line => "${::dns_new::params::ny4dns2}",
path => "/etc/resolv.conf",
}
}
elsif $::oddip == 'false' {
file_line { "ny4 search domain":
ensure => present,
line => "${::dns_new::params::searchdomny4}",
path => "/etc/resolv.conf",
}
file_line { "ny4dns2 first":
ensure => present,
line => "${::dns_new::params::ny4dns2}",
path => "/etc/resolv.conf",
require => File_line["ny4 search domain"],
before => File_line["ny4dns1 second"],
}
file_line { "ny4dns1 second":
ensure => present,
line => "${::dns_new::params::ny4dns1}",
path => "/etc/resolv.conf",
require => File_line["ny4dns2 first"],
}
}
}
You can see that I've tried setting the order using the before
directive.
This is all in regard to setting a new server, but how can I set the order of lines for an already installed server?
Edit #2:
sysadmin1183, I've added the "do" as you showed, now the error is this:
[root@nyproxy33 ~]# puppet agent -t
Info: Retrieving plugin
Info: Loading facts
Error: Could not retrieve catalog from remote server: Error 400 on SERVER: compile error
/etc/puppet/environments/production/modules/dns_new/templates/resolv.conf.erb:27: syntax error, unexpected $end, expecting kEND
; _erbout
^
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
The 27th line is:
<% end %>
Tried to edit it to:
<% end -%>
But getting the same output…
Edit #3:
config.pp
looks like so:
class dns_new::config {
file { "/etc/resolv.conf":
path => '/etc/resolv.conf',
ensure => present,
owner => "root",
group => "root",
mode => "775",
content => template("dns_new/resolv.conf.erb"),
}
case $::dcd {
'ny4': {
$search_dom = $::dns_new::params::searchdomny4
if $::oddip == 'true' {
$dns_list = [ "${::dns_new::params::ny4dns1}", "${::dns_new::params::ny4dns2}" ]
}
elsif $::oddip == 'false' {
$dns_list = [ "${::dns_new::params::ny4dns2}", "${::dns_new::params::ny4dns1}" ]
}
}
The resolv.conf.erb
file looks like this:
search <%= @search_dom %>
<% dns_list.each do |serv| -%>
nameserver <%= serv %>
<% end -%>
Running Puppet:
[root@nyproxy33 ~]# puppet agent -t
Info: Retrieving plugin
Info: Loading facts
Error: Could not retrieve catalog from remote server: Error 400 on SERVER: Failed to parse template dns_new/resolv.conf.erb:
Filepath: /usr/lib/ruby/site_ruby/1.8/puppet/parser/templatewrapper.rb
Line: 81
Detail: Could not find value for 'dns_list' at /etc/puppet/environments/production/modules/dns_new/templates/resolv.conf.erb:2
at /etc/puppet/environments/production/modules/dns_new/manifests/config.pp:8 on node nyproxy33.ny4.peer39.com
Warning: Not using cache on failed catalog
Error: Could not retrieve catalog; skipping run
Best Answer
You may be better served with a template in this case. Something like...
With an ERB template that looks something like this:
new_dns/templates/resolv.conf.erb
:Which should emit a resolv.conf file in the order you want.
Another option is to just encode the DNS server list in the module, and use ruby code in the ERB template to determine if you walk down the array (each) or walk up it (reverse.each). That would look like:
With the more complex ERB formatted like:
In case you haven't done templates before, the key for the markup is roughly:
The template will be evaluated line by line. The first line is a simple thing that drops the 'server' part of the resolv conf. The second line is where things get more complex: we're calling ruby functions. This is what allows us to drop as many
nameserver
lines as there are in the array.I see what you're trying to do in the ERB, but I think you're over-complicating it. You're encoding the localization logic (nj vs ams vs lax) in the ERB itself. That can be done, but you might have better luck doing that part in the puppet-code. It's more likely to be readable to someone else if that part of the logic is in with the puppet code.
dns_new/manifests/config.pp
:You now have two variables to deal with in the ERB.
search_domain
anddns_list
. This shortens the ERB quite a bit:dns_new/templates/resolv.conf.erb
:If you're wondering why I'm assigning variables in the class and using those in the ERB instead of using the variables in the params class, its because of the non-intuitive way that out-of-scope variables work in ERB files. It's much easier, and good style, to assign variables that will be used in an ERB file in the same class that calls the template.