Iterating through all IP addresses in a puppet ERB template

Sometimes, it's relevant to iterate through all ip adrresses on a host to emit configuration for each one. Facter has a useful bunch of facts for this:

  • interfaces => lo,eth0,eth0_0,eth1
  • ipaddress_lo => 127.0.0.1
  • ipaddress_eth0 => 192.168.0.200
  • ipaddress_...
  • ... and so on

This is nice, but how can you use this in an ERB template?

Answer: use scope.lookupvar()

Here's a snippet for emitting one "Allow" line for each of the server's IP addresses in an Apache vhost:

<%- interfaces.split(',').each do |int|
  if has_variable?("ipaddress_#{int}") then %>
               Allow from <%= scope.lookupvar("ipaddress_#{int}") %>
<%- end
end %>

if you're wondering why I'm using has_variable?(), too, that's because sometimes interfaces are not assigned any IP address. In such cases, there is no corresponding $ipaddress_* fact, so it would be an error to lookup a value for that.

Comments

When I was implementing this I ran into errors when the template was applied to hosts with only one interface, the checking for comma in the string worked to resolve the issue:

<%
if @interfaces.count(',') > 0 then
    @interfaces.split(',').each do |int|
        if has_variable?("ipaddress_#{int}") then
            -%>  Allow from <%= scope.lookupvar("ipaddress_#{int}") %><%
        end
    end
else
    interface = @interfaces
    if has_variable?("ipaddress_#{interface}") then
        -%>  Allow from <%= scope.lookupvar("ipaddress_#{interface}") %><%
    end
end
-%>
gabster's picture

oh, thanks for pointing this out. I must admit that I rarely encounter hosts with only one interface, and I was expecting split() to just build a list of one element if there were no commas present..

I'll have to test this out with a VM.

Sadly, though, the ruby documentation doesn't describe what happens in the case where the pattern is not found in the string: http://ruby-doc.org/core-1.9.3/String.html#split-method

gabster's picture

well this is weird. I just tested out string.split() with iRuby, and it behaved exactly as I had expected. I'm using ruby 1.8.7 :

irb(main):001:0> s = 'allo'
=> "allo"
irb(main):002:0> s.split('l')
=> ["a", "", "o"]
irb(main):003:0> s.split(',')
=> ["allo"]

Olafur, can you reply by pasting the error you get on single-interface machines?

I got no error in the erb template, it just did not execute the steps within the do loop, but when I tried this again today I got no error and it returned a single value as expected.

I really don't know what I might be doing different since before the weekend, maybe I just needed the extra rest? ;)

So, you can just ignore that original comment since it seems to work just fine.

/Oli

P.S. This comment system really needs an email or some notification method...

gabster's picture

ah! then I'm glad you got this to work. :)

re: to p.s.: yess! I know, it's annoying not to have e-mail notifications! I've asked colleagues about how to implement this with drupal and they told me about some options. So it's in my todo list.

Add new comment

Markdown

  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
1
j
6
D
u
6
Q
D
X
d
Enter the code without spaces.