Bundler¶
Bundler is used to describe groups of inter-dependent configuration
entries, such as the combination of packages, configuration files,
and service activations that comprise typical Unix daemons. Bundles are
used to add groups of configuration entries to the inventory of client
configurations, as opposed to describing particular versions of those
entries. For example, a bundle could say that the configuration file
/etc/passwd
should be included in a configuration, but will not
describe the particular version of /etc/passwd
that a given client
will receive.
Group and Client tags can be used inside of bundles to differentiate
which entries particular clients will recieve; this is useful for the
case where entries are named differently across systems; for example,
one Linux distro may have a package called openssh
while another
uses the name ssh
. See Group and Client tags for details
and a longer example.
A brief example:
<Bundle>
<Path name='/etc/ssh/ssh_config'/>
<Group name='rpm'>
<Service name='sshd'/>
<Package name='openssh-server'/>
</Group>
<Group name='deb'>
<Package name='ssh'/>
<Service name='ssh'/>
</Group>
</Bundle>
Note that we do not specify how a given entry should be managed, only that it should be. The concrete specification of each entry will be provided by a different plugin such as Cfg, Rules, or Packages.
Alternatively, you can use fully-bound entries in Bundler, which has various uses. For instance:
<Bundle>
<Path name='/etc/ssh/ssh_config'/>
<Group name='rpm'>
<BoundService name='sshd' type="chkconfig" status="on"/>
<BoundPackage name='openssh-server' version='5.8p2' type="yum" />
</Group>
<Group name='deb'>
<Package name='ssh'/>
<BoundService name='ssh' type="chkconfig" status="on"/>
</Group>
</Bundle>
In this example, both Service tags and one Package tag are fully bound – i.e., all information required by the client to manage those entries is provided in the bundle itself.
Bundle “Magic”¶
Bundles are collections of related entries. That point is very, very important, because a bundle performs certain “magic” actions when one or more entries in it are modified:
Service
entries whoserestart
attribute istrue
(the default) will be restarted.Action
entries whosewhen
attribute ismodified
will be run.
Because of these two magic actions, it’s extremely important to
structure your bundles around Service and Action entries, rather than
around some loose idea of which entries are related. For instance, in
order to manage a Bcfg2 server, a number of packages, paths, services,
etc. must be managed. But not all of these entries would require
bcfg2-server
to be restarted, so to limit restarts it’s wise to
split these entries into two bundles. See
Bcfg2 Server for an example
of this.
Disabling Magic¶
Disabling magic bundler actions can be done in one of two ways:
- On a per-entry basis. Set
restart="false"
on a Service to prevent it from being restarted when the bundle is modified. Setwhen="always"
on an Action to cause it to run every time, regardless of whether or not the bundle was modified. - On a per-bundle basis. Set
independent="true"
on the top-levelBundle
tag to signify that the bundle is a collection of independent (i.e., unrelated) entries, and to prevent any magic actions from being performed. (This is similar to theBase
plugin in older versions of Bcfg2.) This was added in Bcfg2 1.4.
Service entries in independent bundles are never restarted, and Action
entries in independent bundles are only executed if when="always"
.
(I.e., an Action entry in an independent bundle with
when="modified"
is useless.)
Genshi templates¶
Genshi XML templates allow you to use the Genshi templating system to dynamically generate a bundle. Genshi templates can be specified one of two ways:
- Add an XML-style genshi template to the Bundler directory with a
.genshi
and the associated namespace attribute. This is deprecated as of Bcfg2 1.4. - Add the Genshi namespace to your existing XML bundle.
See Genshi templating for details.
Troubleshooting¶
To render a bundle for a given client, you can run:
bcfg2-info buildbundle <bundle name> <hostname>
This will render the template; it will not fully bind all of the entries in the bundle.
See bcfg2-info for more details.
Dependencies¶
Dependencies on other bundles can be specified by adding a RequiredBundle tag that adds another bundle by name, e.g.:
<Bundle>
<RequiredBundle name="nfs-client"/>
...
</Bundle>
The dependent bundle is added to the list of bundles sent to the
client, not to the parent bundle itself. If you want to propagate
the modification flag from the required bundle, you can add
inherit_modification="true"
to the RequiredBundle tag.
An example:
nfs-client.xml
:
<Bundle>
<Package name="nfs-utils"/>
<Service name="nfslock"/>
<Service name="rpcbind"/>
<Service name="nfs"/>
</Bundle>
automount.xml
:
<Bundle>
<RequiredBundle name="nfs-client"/>
<Path name="/mnt/home"/>
<Path name="/etc/auto.master"/>
<Path name="/etc/auto.misc"/>
<Service name="autofs"/>
<Package name="automount"/>
</Bundle>
If a new nfs-utils
package was installed, the nfslock
,
rpcbind
, and nfs
services would be restarted, but not the
autofs
service. If you would add inherit_modification="true"
to the RequiredBundle tag, you would ensure the propagation of the
modification flag and the autofs
service would be restarted,
too. But if a new /etc/auto.misc
file was sent out, only the
autofs
service would be restarted, but the nfslock
,
rpcbind
, and nfs
services would not be restarted
(independent of the inherit_modification
flag).
Examples¶
In some cases, configuration files need to include the client’s hostname in their name. The following template produces such a config file entry.
<Bundle xmlns:py="http://genshi.edgewall.org/">
<Path name='/etc/package-${metadata.hostname}'/>
</Bundle>
Depending on the circumstance, these configuration files can either be handled by individual entries in Cfg, or can be mapped to a single entry by using the altsrc feature.
In this example, configuration file names are built using probed results from the client. getmac is a probe that gathers client MAC addresses and returns them in a newline delimited string.
<Bundle xmlns:py="http://genshi.edgewall.org/">
<?python
files = metadata.Probes["getmacs"].split("\n")
?>
<Path py:for="file in files"
name="/etc/sysconfig/network/ifcfg-eth-${file}"
altsrc="/etc/ifcfg-template"/>
</Bundle>
Note
- The use of the altsrc directive causes all ifcfg files to be handled by the same plugin and entry.
- The <?python ?> blocks have only been available in genshi since 0.4 (http://genshi.edgewall.org/ticket/84)
If you want a file to be only on a per-client basis, you can use an if declaration.
<Bundle xmlns:py="http://genshi.edgewall.org/">
<Path name="/etc/bacula/bconsole.conf"/>
<Path name="/etc/bacula/bacula-fd.conf"/>
<Path name="/etc/bacula/bacula-sd.conf"/>
<Path py:if="metadata.hostname == 'foo.bar.com'"
name="/etc/bacula/bacula-dir.conf"/>
</Bundle>
or alternately
<Bundle xmlns:py="http://genshi.edgewall.org/">
<Path name="/etc/bacula/bconsole.conf"/>
<Path name="/etc/bacula/bacula-fd.conf"/>
<Path name="/etc/bacula/bacula-sd.conf"/>
<py:if test="metadata.hostname == 'foo.bar.com'">
<Path name="/etc/bacula/bacula-dir.conf"/>
</py:if>
</Bundle>
or yet another way
<Bundle xmlns:py="http://genshi.edgewall.org/">
<Path name="/etc/bacula/bconsole.conf"/>
<Path name="/etc/bacula/bacula-fd.conf"/>
<Path name="/etc/bacula/bacula-sd.conf"/>
<Client name="foo.bar.com">
<Path name="/etc/bacula/bacula-dir.conf"/>
</Client>
</Bundle>
The final form is preferred if there is no code inside the block that would fail on other clients.
While these examples are simple, the test in the if block can in fact be any python statement.
Other examples¶
Some simple examples of Bundles can be found in the Bcfg2 example repository.
In addition to the example repository, the following is a list of some more complex example Bundles.