编辑:我想出了我认为最好的解决方法。感谢回答我问题的人 - 他们一路帮助了我。我将发布我的解决方案,以便其他人在需要时可以找到它。
原始问题:
我想使用 puppet 自动配置我们的多个 NFS 文件系统。但是,我希望清单在执行此操作之前确保它在正确的网络上具有 IP。
Puppet 很有帮助地提供了有关机器通过
interfaces
和ipaddress_***
事实分配的 IP 的信息。问题是我需要检查所有接口(我不想假设它们是如何连接的)。问题 1:Puppet 可以循环遍历事实列表吗?例如:
for $if in $interfaces { //do something }
问题 2:如果 Puppet 可以做到这一点,是否有办法将字符串作为变量进行评估。
interfaces
提供了一种非常好的方法来了解有多少个ipaddress_***
变量network_***
,但即使我可以循环遍历它的值,我也需要执行以下操作:for $if in $interfaces { $net_var = eval("network_${if}") if $net_var = /^192\.168\.2\.0$/ { //do something } }
这可能吗?我的做法是否完全错误(对 Puppet 来说有点陌生)?
我最终做了什么:
Puppet 允许您为模块定义自定义解析器函数。这些自定义函数只是 ruby,因此您可以在其中有效地执行任何您想做的事情,包括循环。
你把它们放在 中<module_dir>/lib/puppet/parser/functions/
。为了做我想做的事情,我创建了<module_dir>/lib/puppet/parser/functions/has_network.rb
以下内容:
#has_network.rb
require 'ipaddr'
module Puppet::Parser::Functions
newfunction(:has_network, :type => :rvalue) do |args|
retcon = ""
if not /^[0-9]{1,3}(\.[0-9]{1,3}){3}\/[0-2]?[0-9]$/.match(args[0]) then
raise Puppet::ParseError, "Network provided was not valid. Provide 192.168.0.0/16 format"
else
requested_network = IPAddr.new(args[0])
interfaces_fact = lookupvar('interfaces')
interfaces = interfaces_fact.split(",")
interfaces.each do |interface|
ip = IPAddr.new(lookupvar("ipaddress_#{interface}"))
if requested_network.include?(ip)
retcon = "true"
end
end
end
retcon
end
end
这添加了一个可以在清单中使用的新函数,名为 has_network。如果计算机的 IP 地址之一位于提供给该函数的网络上,则它返回 true。现在我可以执行以下操作:
if has_network("192.168.1.0/24"){
//do something
}
关于自定义函数的几点说明
- 这些在主服务器上运行。请注意使用来
lookupvar
对 Facter 事实执行操作。您不能使用其中一个在客户端上执行诸如创建文件之类的操作。 - 当你改变函数时,你必须重新启动 puppetmaster。如果不重新启动,puppetmaster 将永远不会加载更新的功能,您将会感到非常困惑。
答案1
正如@Zoredache提到的,Puppet中没有循环。但是你可以使用定义来解决这个问题,如下所示:
define show_ip {
$inf = "ipaddress_${name}"
$ip = inline_template('<%= scope.lookupvar(inf) %>')
if $ip =~ /192\.168\.3.*/ {
notify {"Found IP $ip": }
}
}
$ifs = split($interfaces, ',')
show_ip { $ifs: }
以下是直接调用时的输出:
# puppet manifests/defines/net.pp
warning: Implicit invocation of 'puppet apply' by passing files (or flags) directly
to 'puppet' is deprecated, and will be removed in the 2.8 series. Please
invoke 'puppet apply' directly in the future.
notice: Found IP 192.168.3.118
notice: /Stage[main]//Show_ip[eth1]/Notify[Found IP 192.168.3.118]/message: defined 'message' as 'Found IP 192.168.3.118'
notice: Finished catalog run in 0.07 seconds
答案2
puppet 中没有循环或迭代。
再玩了一下,这里有一个替代版本,它应该适用于单个接口上的任意数量的 ip,并且不使用 eval。 to_hash.keys
返回所有变量名称的数组,find_all
根据正则表达式过滤列表,map
使用 ping 查找变量的实际值,最后将结果合并回字符串。
$iplist=inline_template('<%= scope.to_hash.keys.find_all{|i| i =~ /ipaddress_.*/}.map{|j| scope.lookupvar(j)}.join(",")%>')
if $iplist =~ /10\.2\.93.*/ {
notify {"found $iplist": }
}
# on the client with a matching address we see "notice: found 10.2.93.5,127.0.0.1"
我有一个可行的方法,但我不确定它是否真的是最佳解决方案或安全方案。基本上我认为您可以使用 inline_template。在内联模板中,我们拆分了接口事实,即所有接口的列表。我们使用映射来构建所有接口的 IP 地址的新数组,最后我们将该数组重新合并为一个字符串。
我对这个想法最大的担忧是,我正在对客户端系统提供的东西进行评估。我不完全确定这有多邪恶。
如果接口有多个地址,或者接口没有地址,这也会中断。也许其他人可以提出一些改进建议。我刚刚开始学习 ruby。
$iplist=inline_template('<%= interfaces.split(",").map{|x| eval ("ipaddress_"+x)}.join(",")%>')
if $iplist =~ /10\.2\.93.*/ {
notify {"found $iplist": }
}
# on the client with a matching address we see "notice: found 10.2.93.5,127.0.0.1"