As part of my job I'm installing Bacula via Puppet. The server component of Bacula requires a definition for each client that it pulls data from. One way to manage this is to create a directory in the bacula configuration directory and parse every *.conf file within:
# include everything in /etc/bacula/clients
# this is where backup clients (fd or file daemons in bacula terminology) put their configuration
@|"find /etc/bacula/clients -name '*.conf' -type f -exec echo @{} \;"
To manage this in Puppet you would either have to manually use a template file for each client computer, which could work for smaller deployments. If you have more than a couple of clients you really need something more automated and elegant.
Enter Puppet's "Exported Resources". These nifty bits of code are a way of saying "for each computer that has puppet class X installed, do something". You need PuppetDB for this to work, as Puppet queries the stored data about each computer when the "collector" class is run.
First you have to define a resource to export. This doesn't get instantiated when you run the "client" module, so it won't create anything on the client. Here's a quick example:
# this resource can then be exported using the @@ directive
# and collected by the bacula_server class to actually create the
# required files on the server side
define bacula_client_config (
$client_hostname,
$dirs_to_backup,
$backup_schedule,
) {
file { "/etc/bacula/clients/${client_hostname}.conf":
ensure => present,
owner => "bacula",
group => "bacula",
mode => 0755,
content => template("example/bacula-client.conf.erb"),
}
}
The parameters in the definition are simply data to be filled in by the template.
Later in the client class we export this newly defined resource like so:
# export the above definition into the puppetdb for later collection by the bacula_server class
@@bacula_client_config { "${::fqdn}":
client_hostname => "${::fqdn}",
dirs_to_backup => $backup_directory_list,
backup_schedule => $backup_schedule_name,
}
Again, this is pretty straightforward stuff once you know what's happening. The "@@" means "export this defined type into PuppetDB for later reuse."
Now when we run that class on our puppet client computers, PuppetDB stores the defined type. Obviously this means that you have to have installed that class on every computer you want to be included in the server's configuration before you run the server configuration class in Puppet.
Now on the server Puppet module, we create a collector:
# collect the exported resources from any servers (i.e. nodes in puppet-speak)
# that have example::software::bacula_client installed
Example::Software::Bacula_client::Bacula_client_config <<||>> {
notify => Service['bacula-dir'],
}
This uses the "spaceship operator", <<||>>, a kind of "for each of this type of defined resource." In other words, for every bacula client, instantiate the bacula_client_config defined resouce and then notify (restart) the bacula server.
Now when we run the bacula server Puppet module, we get one file for each client saved in /etc/bacula/clients.
Puppet makes this sort of computer management fairly simple. Puppet uses exported resources to in its Nagios classes, but Puppetlabs' Nagios management code is fairly impenetrable as they use multiple external custom Ruby libraries to create the files. I hope the example above was informative!