Puppet:如何设置文件内的行顺序?

Puppet:如何设置文件内的行顺序?

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

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

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

search domain.local
nameserver 1.1.1.1
nameserver 2.2.2.2

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

proxy73

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

search domain.local
nameserver 2.2.2.2
nameserver 1.1.1.1

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

我编写的清单的相关部分如下所示(请参阅下一部分,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"],
          }
      }
    }

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

这是关于设置新服务器的全部内容,但是如何设置已安装服务器的行顺序?

编辑#2:

sysadmin1183,我按照您说的添加了“do”,现在的错误是这样的:

[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

第27行是:

<% end %>

尝试将其编辑为:

<% end -%>

但得到相同的输出...

编辑#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}" ]
        }
    }

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

search <%= @search_dom %>
<% dns_list.each do |serv| -%>
nameserver <%= serv %>
<% end -%>

奔跑木偶:

[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

答案1

在这种情况下,使用模板可能会更好。例如...

# 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'),
}

ERB 模板如下所示:

new_dns/templates/resolv.conf.erb

search <%= @search_dom %>
<% @dns_order.each do |serv| %>
nameserver <%= serv %>
<% end -%>

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

另一种选择是仅在模块中编码 DNS 服务器列表,并使用 ERB 模板中的 ruby​​ 代码来确定是向下遍历数组 (each) 还是向上遍历数组 (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')
}

使用更复杂的 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 -%>

如果你以前没有做过模板,标记的关键大致如下:

<%   : 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.

模板将逐行评估。第一行很简单,删除了 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')
}

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

dns_new/templates/resolv.conf.erb

search <%= @search_domain %>
<% dns_list.each do |serv| -%>
nameserver <%= serv %>
<% end -%>

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

答案2

“before”元参数实际上只说明了资源的执行顺序,而不是文件中行的顺序。

如果我是你,我会尝试使用一流的 puppet 构造来管理 resolv.conf:一个将其作为文件资源进行管理的单独模块(puppet forge 上可能有几个),或者编写你自己的小模板,明确地对指定的名称服务器参数进行排序。

另一个选择是在一个 file_line 资源中指定两个名称服务器,并使用\n以下方式将它们分开:

file_line { "ny4dns2 first":
  ensure => present,
  line   => "${::dns_new::params::ny4dns1}\n${::dns_new::params::ny4dns2}",
  path   => "/etc/resolv.conf",
  require => File_line["ny4 search domain"],
  after =>  "${::dns_new::params::searchdomny4}"
 }

如果它们的顺序错误,它将不会重新排序,但至少会以正确的顺序将它们添加到搜索语句的正下方(可能列出 4 个名称服务器,超过 3 个(resolv.h 中的 MAXNS),但仅使用 file_line 资源来避免这种情况可能很难或不可能)。

此外,该after参数特定于 file_line 资源,提示在何处插入行,并且before是一个谈论资源排序的一般资源参数。

相关内容