I''m sure some will see this as downright sinful and ugly and
un-scalable, all of which are probably true, but I''ve found this to be
a really handy hack lately and thought I''d share in case it scratches
anyone else''s itch. I started to explain all the reasons why I did
this, but it started looking very "TL; DR" :). The short version is
that in our puppet-controlled EC2-hosted clusters, often a templated
config file on one node needs Facter facts from all other nodes (or a
subset of them), such as their randomly-ec2-assigned ipaddress_eth0''s,
or the ec2_instance_id, etc.
There are probably more efficient ways to implement this, but it works
for me right now. This technique makes a hostclass-scoped variable
available named "rfacts", which is a hash by hostname of the Facter
facts of all other nodes in the environment (using the same
puppetmaster), which stays up to date as all of the clients do their
puppet runs. It does this by parsing up all the facts data available
on the master from the yaml files in /var/lib/puppet/yaml/facts/*.
First, a new Ruby DSL manifest which defines a magic hostclass named
"rfbase": -----
manifests/rfbase.rb: ----
class RFacter
@@rfcache = {}
def self.get_rfacts
path = ''/var/lib/puppet/yaml/facts''
rfacts = {}
Dir.foreach(path) {|fn|
m = fn.match(/(.*)\.yaml$/) or next
hn = m[1]
fullpath = path + ''/'' + fn
mtime = File.stat(fullpath).mtime
if(@@rfcache.key?(hn) &&
@@rfcache[hn][''mtime''] == mtime) then
rfacts[hn] = @@rfcache[hn][''data'']
else
rfacts[hn] = YAML.load_file(fullpath).values
@@rfcache[hn] = { ''mtime'' => mtime,
''data'' => rfacts[hn] }
end
}
return rfacts
end
end
hostclass :rfbase do
scope.setvar(''rfacts'', RFacter.get_rfacts())
end
Next, all of my role/location-specific hostclasses inherent (directly
or indirectly) from rfbase, e.g.:
manifests/node_classes.pp: ---
# Low-level magic for cross-node fact access via $rfacts hash
import "rfbase.rb"
class node_foo inherits rfbase {
# ....
}
And finally, my erb config templates can now across cross-node facts
via the rfacts hash. For example, this one-liner erb template defines
a file stored at /etc/nodes/instancemap on all nodes, and that file
contains a hostname:instance_id map of all nodes in the cluster (for
other administrative scripts to work from):
templates/etc/nodes/instancemap.erb: ---
<% imap = rfacts.sort.map{|x|
x[1][''hostname'']+'':''+x[1][''ec2_instance_id'']}.join("\n")
%><%= imap %>
A much uglier and more complex example that pulls out the
ipaddress_eth0 and a custom fact of ours called server_tokens from all
nodes whose hostnames begin with the string "apps", and uses this to
form some configuration lines for a custom proxy daemon:
<%
svrs = []
nprocs = Integer(node_nprocs)
sport = Integer(node_start_port)
rfacts.select{|k,v| k.match(/^apps/)}.sort.each{|x|
ip = x[1][''ipaddress_eth0'']
stokens = x[1][''server_tokens''].split(/,/)
i = 0
while i < nprocs do
svrs.push(sprintf(''%s:%u/%s'', ip, sport + i,
stokens[i]))
i += 1
end
}
servers = svrs.join("\n")
%><%= servers %>
-- Brandon
--
You received this message because you are subscribed to the Google Groups
"Puppet Users" group.
To post to this group, send email to puppet-users@googlegroups.com.
To unsubscribe from this group, send email to
puppet-users+unsubscribe@googlegroups.com.
For more options, visit this group at
http://groups.google.com/group/puppet-users?hl=en.