Puppet:如何在文件中设置行顺序?

Ita*_*not 2 resolv.conf puppet

我公司的 Linux 服务器由 Puppet 管理。

有一个 DNS 模块,它/etc/resolv.conf根据配置为facter值的物理位置在所有服务器上进行配置。

如您所知,/etc/resolv.conf文件如下所示:

search domain.local
nameserver 1.1.1.1
nameserver 2.2.2.2
Run Code Online (Sandbox Code Playgroud)

公司内所有服务器的主机名都以两位数结尾,例如:

proxy73
Run Code Online (Sandbox Code Playgroud)

为了在两个 DNS 服务器之间拆分 DNS 网络流量,我编写了一个新的 puppet 模块,它会删除主机名的最后两位数字,如果它是奇数,则该/etc/resolv.conf文件应如上所示,但如果数字创建一个奇数,那么/etc/resolv.conf文件应该是这样的:

search domain.local
nameserver 2.2.2.2
nameserver 1.1.1.1
Run Code Online (Sandbox Code Playgroud)

但我的问题是,无论我如何编写清单,这些行总是按第一台服务器排序,然后是第二台服务器,而不是第二台服务器,然后是第一台服务器。

我写的清单的相关部分看起来像这样(请参阅if $::oddip == false原因部分,即不起作用的部分):

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"],
          }
      }
    }
Run Code Online (Sandbox Code Playgroud)

您可以看到我已经尝试使用before指令设置顺序。

这都是关于设置新服务器的,但是如何为已安装的服务器设置行的顺序?

编辑#2:

sysadmin1183,我已经添加了你显示的“做”,现在错误是这样的:

[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
Run Code Online (Sandbox Code Playgroud)

第 27 行是:

<% end %>
Run Code Online (Sandbox Code Playgroud)

试图将其编辑为:

<% end -%>
Run Code Online (Sandbox Code Playgroud)

但是得到相同的输出......

编辑#3: config.pp看起来像这样:

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}" ]
        }
    }
Run Code Online (Sandbox Code Playgroud)

resolv.conf.erb文件如下所示:

search <%= @search_dom %>
<% dns_list.each do |serv| -%>
nameserver <%= serv %>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

运行木偶:

[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
Run Code Online (Sandbox Code Playgroud)

sys*_*138 5

在这种情况下,使用模板可能会更好。就像是...

# This is done to bring the variable into scope.
$search_dom = $::dns_new::params::searchdomny4
if $::oddip == 'true' {
  $dns_order = [ '1.1.1.1', '2.2.2.2' ]
} elsif $::oddip == 'false' {
  $dns_order = [ '2.2.2.2', '1.1.1.1' ]
}

file { '/etc/resolv.conf':
  [usual stuff]
  content => template('dns_new/resolv.conf.erb'),
}
Run Code Online (Sandbox Code Playgroud)

使用如下所示的 ERB 模板:

new_dns/templates/resolv.conf.erb

search <%= @search_dom %>
<% @dns_order.each do |serv| %>
nameserver <%= serv %>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

这应该按照您想要的顺序发出一个 resolv.conf 文件。

另一种选择是仅对模块中的 DNS 服务器列表进行编码,并在 ERB 模板中使用 ruby​​ 代码来确定您是沿着阵列(每个)还是沿着阵列(reverse.each)前进。那看起来像:

$search_dom = $::dns_new::params::searchdomny4
$dns_list   = $::dns_new::params::dnslist
$oddip      = $::oddip
file { '/etc/resolv.conf':
  [usual stuff]
  content => template('dns_new/resolv.conf.erb')
}
Run Code Online (Sandbox Code Playgroud)

使用更复杂的 ERB 格式如下:

search <%= @search_dom %>
<% if @odd_ip == 'true' %>
  <% @dns_list.each do |srv| -%>
nameserver <%= srv %>
  <% end -%>
<% elsif @odd_ip == 'false' -%>
  <% @dns_list.reverse.each do |srv| -%>
nameserver <%= srv %>
  <% end -%>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

如果你之前没有做过模板,标记的关键大概是:

<%   : Here is ruby code.
<%=  : Here is an evaluated value. Replace this block with the 
         result, or enter a blank line.
-%>  : End a block. Don't render a blank line if it doesn't evaluate to anything.
Run Code Online (Sandbox Code Playgroud)

模板将逐行评估。第一行是一个简单的事情,它删除了 resolv conf 的“服务器”部分。第二行是事情变得更复杂的地方:我们正在调用 ruby​​ 函数。这就是允许我们删除nameserver数组中尽可能多的行的原因。


我知道你想在 ERB 中做什么,但我认为你把它复杂化了。您正在 ERB 本身中编码本地化逻辑(nj vs ams vs lax)。这可以做到,但你可能会在 puppet-code 中做这部分的运气更好。如果逻辑的那部分包含在 puppet 代码中,那么它更有可能被其他人阅读。

dns_new/manifests/config.pp

case $::dcd {
  'ny4': {
            $search_domain = $::dns_new::params::searchdomny4
            $dns_list = [ "${::dns_new::params::ny4dns1}",    "${::dns_new::params::ny4dns2}" ]
         }
  'nj':  {
            $search_domain = $::dns_new::params::searchdomnj
            $dns_list = [ "${::dns_new::params::njdns1}",    "${::dns_new::params::njdns2}" ]
         }
  'ams2': {
            $search_domain = $::dns_new::params::searchdomams2
            $dns_list = [ "${::dns_new::params::ams2dns1}",    "${::dns_new::params::ams2dns2}" ]
          }
}

file { '/etc/resolv.conf':
  [the usual stuff]
  content = template('dns_new/resolv.conf.erb')
}
Run Code Online (Sandbox Code Playgroud)

您现在有两个变量需要在 ERB 中处理。search_domaindns_list。这大大缩短了 ERB:

dns_new/templates/resolv.conf.erb

search <%= @search_domain %>
<% dns_list.each do |serv| -%>
nameserver <%= serv %>
<% end -%>
Run Code Online (Sandbox Code Playgroud)

如果您想知道为什么我在类中分配变量并在 ERB 中使用这些变量而不是在 params 类中使用变量,那是因为范围外变量在 ERB 文件中工作的非直观方式。分配将在调用模板的同一类中的 ERB 文件中使用的变量要容易得多,而且风格也很好。

  • @ItaiGanot 那是 ruby​​ 代码。它接受数组 `dns_order` 并对其应用 `each` 函数。`|serv|` 部分定义了将在循环中公开的变量,并将设置为每个索引处的数组值。循环在第一个 `&lt;% end %&gt;` 调用处结束。 (2认同)