Part 4 - Allowing per-host customizations
Contents
In Part 3 we copied a single file from the server to all our clients. This is already useful information and something that you could easily apply to many types of files. Our file though - /etc/hosts - isn't going to be the same on all nodes and you don't have a way yet to deal with the changing content between machines. ' In order to do this there needs to be some unique information about each host, you could use the fully qualified domain name which in this example is dev1.devco.net or simply the hostname that will be dev1, you could even define unique things such as an arbitrary role description.
About Facts
When you installed Puppet you might have noticed that a tool called facter also got installed, log into your client node and run it, you should see something like this:
dev1# facter domain => devco.net fqdn => dev1.devco.net hostname => dev1
there will be a lot more than just these few values but we'll focus on them first. When your client node checks in with the master it will send all the facts known to it over to the puppet master. These facts can then be used in your puppet manifests.
Manifests gets built on the master and executed on the nodes, this impose some restrictions on what information can easily be obtained about the node as at compile time the nodes file system is not available - it is being compiled on the master. The only way for the node to supply information to the master is via facts. You can add your own using the Ruby programming language, we might touch on that later.
To use an existing fact in a manifest you simply use $factname so in our example the variable $fqdn would have dev1.devco.net in it.
Using facts
Have a look at our hosts.pp file, you will notice right now it is just copying one file, always the same file, to all hosts, lets look at improving on that.
I will not always reference the full paths going forward, please review parts 1, 2 and 3 to see how we're structuring this example.
class hosts {
file {"/etc/hosts":
owner => root,
group => root,
mode => 644,
source => ["puppet://puppet/files/etc/hosts.$hostname",
"puppet://puppet/files/etc/hosts"]
}
}
Only the source parameter has changed - we passed it an Array. How does this effect behavior of the file type? The documentation says:
If you specify multiple file sources for a file, then the first source that exists will be used. This allows you to specify what amount to search paths for files
So in practical terms, if you create a file /srv/puppet/fileserver/etc/hosts.dev1 with different contents then only nodes with $hostnames of dev1 will get that file, everyone else will get /srv/puppet/fileserver/etc/hosts.
Instead of $hostname in the above example you could have used $fqdn or $domain you would need to then adjust the filename in /srv/puppet/fileserver accordingly.
Create a file as described above, this is mine, I added 1 line extra:
127.0.0.1 localhost.localdomain localhost 192.168.1.10 dev1.devco.net dev1 192.168.1.5 puppet.devco.net puppet 192.168.1.100 something.devco.net
Testing your changes
Previously you waited 30 minutes for your change to happen, this is not great while developing and testing changes. Lets see how to run puppet in a way to speed the process up.
dev1# puppetd --test
notice: Ignoring cache
info: Caching catalog at /var/lib/puppet/localconfig.yaml
notice: Starting catalog run
3a4
> 192.168.1.100 something.devco.net
info: Filebucket[/var/lib/puppet/clientbucket]: Adding /etc/hosts(489e8aaa49fb001e9330b733bf0073a8)
info: //Node[dev1.devco.net]/hosts/File[/etc/hosts]: Filebucketed to with sum 489e8aaa49fb001e9330b733bf0073a8
notice: //Node[dev1.devco.net]/hosts/File[/etc/hosts]/source: replacing from source puppet://puppet/files/etc/hosts.dev1 with contents {md5}28ebea5d31e7d1c4e34a0be86175f4e1
notice: Finished catalog run in 0.40 seconds
If you go through this output you will see that it shows you a diff of the changes it was about to make - showing the 1 line it is changing - and it is also showing that it is replacing /etc/hosts, check the file on the node and you'll see the changes got applied. If you delete /srv/puppet/fileserver/etc/hosts.dev1 your node will revert back to the default.
So running puppetd with --test runs the puppet network client immediately and shows you output of what changes it is making. Using --test is equivalent to using --onetime --verbose --ignorecache --no-usecacheonfailure. When you installed the RPMs a man page were also installed, look at that for more options.
Defining a node variable
You may recall I said we could define a arbitrary variable, something like a role that identifies a group of servers. You do that in the node file, ours is stored in dev1.devco.net.pp
This is only a quick intro to variables, they are pretty complex and as in other programming language there are many scoping issues. Read more in the Language Tutorial
A very important thing to note is that the Puppet language is a declarative language, this means you cannot re-assign values to a variable within the current scope. The Language Tutorial has a sample and more information on this.
node "dev1.devco.net" {
$role = "develdesktop"
include hosts
}
You now have a new variable - $role - to use in your manifests, if you adjust your hosts class to look like this:
class hosts {
file {"/etc/hosts":
owner => root,
group => root,
mode => 644,
source => ["puppet://puppet/files/etc/hosts.$hostname",
"puppet://puppet/files/etc/hosts.$role",
"puppet://puppet/files/etc/hosts"]
}
}
You've added a 3rd search path to locate our file, you can therefore create /srv/puppet/fileserver/etc/hosts.develdesktop to put the same hosts file on all your development desktops while retaining the ability to provide a specific one for a host - using $hostname - or to fall back to a default one if none of these exist.
You should also realise that if you do create a hosts.dev1 file AND a hosts.develdesktop file only the hosts.dev1 file will be created on the node. The first match wins.
Conclusion
We've covered quite a bit of ground in this part you are now ready to push out any file to any number of clients, you have the ability to select which files to create on which nodes in an arbitrary fashion and you can create your own variables to use in your manifests.
There is a key problem with this approach, you will ultimately end up with a lot of duplication in files. If you wish to have host entries on all your development desktops and have the ability to push a custom file to just one of them, then that custom file should also have the general development entries and when you change it you might need to do the change in the node specific ones and the role based one, this will be a problem once you have 100s of nodes! We will fix this in the next parts.
You should be able to answer these questions, if you cannot please ask on the #puppet irc channel:
- What's the syntax for creating an array?
- What happens when you specify an array to the source parameter of the file type?
- What are facts and where do they come from?
- Where does manifests get compiled? Does this impose any restrictions?
As before you should be able to answer these questions on your own without referring to the answers, the use of arrays in file types is essential information and you should understand them 100%. Ask on the #puppet irc channel if anything is not clear.
- [1, 2, 3]
- The Array acts as a search path, by default the first file that matches will be put on your node
Facts are information specific and possibly unique to each node, the Facter tool creates them
- Manifests are compiled on the master, you can therefore in general not make decisions based on the filesystem of the node in question - unless you use the facts system.
Move on to Part 5 now.
