Puppet: Checking sets of variables

Solution 1:

As @Zoredache mentioned, there is no loop in Puppet. But you can get around this by using a definition, something like this:

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: }

Belows is the output when directly calling:

# 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

Source: https://blog.kumina.nl/tag/puppet-tips-and-tricks/

Solution 2:

There is no looping or iteration in puppet.

Played around a bit more, and here an alternate version, that should work for any number of ips on a single interfaces and doesn't use an eval. to_hash.keys returns an array of all the variables names, the find_all filters the list based on the regular expression, a mapping is used to lookup the actual value of the variable, and finally the results are merged back into a string.

$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"

I have something that works, but I am not sure it is really the best solution, or safe. Basically I figure you can use an inline_template. Within the inline template we split the interfaces fact which is a list of all the interfaces. We use a mapping to build up a new array of the ip addresses of all the interfaces, and finally we re-join the array into a string.

My biggest concern with this idea is that I am doing an eval, on something that is basically supplied by the client system. I am not entirely sure how evil this is.

This also will break if an interface has multiple addresses, or if an interface has no addresses. Maybe someone else can suggest some improvements. I am just learning 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"