Edit: I have come up with what I think is the best way to go about this. Thanks to the people answered my question - it helped along the way. I'm going to post my solution so that others can find it if they want.
Original Question:
I would like to use puppet to auto configure a number of our NFS filesystems. However, I'd like the manifest to make sure it has an IP on the right network before doing so.
Puppet helpfully provides facts about the IPs the machine has assigned through the
interfaces
andipaddress_***
facts. The issue is that I need to check all of the interfaces (I'd rather not make assumptions about how they are connected).Question 1: Can Puppet loop through a fact list? For example:
for $if in $interfaces { //do something }
Question 2: If Puppet could do that, is there a way to evaluate a string as a variable.
interfaces
provides a really nice way of knowing how manyipaddress_***
andnetwork_***
variables there are, but even if I could loop through it's values, I'd need to do something like:for $if in $interfaces { $net_var = eval("network_${if}") if $net_var = /^192\.168\.2\.0$/ { //do something } }
Is this possible? Am I going about this completely wrong (kinda new to Puppet)?
What I ended up doing:
Puppet allows you to define custom parser functions for a module. These custom functions are just ruby, so you can effectively do whatever you want inside of them, including looping.
You place them in <module_dir>/lib/puppet/parser/functions/
. In order to do what I wanted, I created <module_dir>/lib/puppet/parser/functions/has_network.rb
with the following contents:
#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
This adds a new function that I can use in my manifests called has_network. It returns true if one of the machine's IP addresses is on the network provided to the function. Now I can do things like the following:
if has_network("192.168.1.0/24"){
//do something
}
A couple of notes about custom functions
- These run on the master server. Note the use of
lookupvar
to do operations on Facter facts. You cannot do things like create files on the client using one of these. - When you change the function, you must restart the puppetmaster. If you do not, the puppetmaster will never load the updated function and you'll be really confused.