1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
|
.. -*- mode: rst -*-
.. _server-plugins-structures-bundler-index:
=======
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. Configuration entries nested inside of Group elements
only apply to clients who are a member of those groups; multiple
nested groups must all apply. Also, groups may be negated; entries
included in such groups will only apply to clients who are not a
member of said group. The same applies to Client elements.
The following is an annotated copy of a bundle:
.. code-block:: xml
<Bundle name='ssh' version='2.0'>
<Path name='/etc/ssh/ssh_host_dsa_key'/>
<Path name='/etc/ssh/ssh_host_rsa_key'/>
<Path name='/etc/ssh/ssh_host_dsa_key.pub'/>
<Path name='/etc/ssh/ssh_host_rsa_key.pub'/>
<Path name='/etc/ssh/ssh_host_key'/>
<Path name='/etc/ssh/ssh_host_key.pub'/>
<Path name='/etc/ssh/sshd_config'/>
<Path name='/etc/ssh/ssh_config'/>
<Path name='/etc/ssh/ssh_known_hosts'/>
<Group name='rpm'>
<Package name='openssh'/>
<Package name='openssh-askpass'/>
<Service name='sshd'/>
<Group name='fedora' >
<Group name='fc14' negate='true'>
<Package name='openssh-clients'/>
</Group>
<Package name='openssh-server'/>
</Group>
</Group>
<Group name='deb'>
<Package name='ssh'/>
<Service name='ssh'/>
</Group>
<Client name='trust.example.com'>
<Path name='/etc/ssh/shosts.equiv'/>
</Client>
</Bundle>
In this bundle, most of the entries are common to all systems. Clients
in group **deb** get one extra package and service, while clients in
group **rpm** get two extra packages and an extra service. In
addition, clients in group **fedora** *and* group **rpm** get one
extra package entries, unless they are not in the **fc14** group, in
which case, they get an extra package. The client
**trust.example.com** gets one extra file that is not distributed to
any other clients. Notice that this file doesn't describe which
versions of these entries that clients should get, only that they
should get them. (Admittedly, this example is slightly contrived, but
demonstrates how group entries can be used in bundles)
+----------------------------+-------------------------------+
| Group/Hostname | Entry |
+============================+===============================+
| all | /etc/ssh/ssh_host_dsa_key |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_host_rsa_key |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_host_dsa_key.pub |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_host_rsa_key.pub |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_host_key |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_host_key.pub |
+----------------------------+-------------------------------+
| all | /etc/ssh/sshd_config |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_config |
+----------------------------+-------------------------------+
| all | /etc/ssh/ssh_known_hosts |
+----------------------------+-------------------------------+
| rpm | Package openssh |
+----------------------------+-------------------------------+
| rpm | Package openssh-askpass |
+----------------------------+-------------------------------+
| rpm | Service sshd |
+----------------------------+-------------------------------+
| rpm and fedora | Package openssh-server |
+----------------------------+-------------------------------+
| rpm and fedora and not fc4 | Package openssh-clients |
+----------------------------+-------------------------------+
| deb | Package ssh |
+----------------------------+-------------------------------+
| deb | Service ssh |
+----------------------------+-------------------------------+
| trust.example.com | /etc/ssh/shosts.equiv |
+----------------------------+-------------------------------+
Genshi templates
================
Genshi xml templates can be specified one of two ways:
1. Add an xml-style genshi template to the Bundler directory with a
``.genshi`` and the associated namespace attribute.
2. Simply add the appropriate namespace attribute to your existing xml
bundle.
The namespace attribute in this case should look like the following::
xmlns:py="http://genshi.edgewall.org/"
Motivation
----------
Static Bundles have served us relatively well, but have a relatively
weak set of interal per-client differentiation mechanisms. In static
Bundles, the group hierarchy (from the perspective of the current
client) is available for use via boolean constraints with negation. This
notion does not mesh well with the use of Probes, which can result in
freeform data. In the presence of probe results, clients can have a wide
array of data, and rendering down to a boolean logic may not always
be desirable. Moreover, while static Bundles allow entry inclusion or
exclusion based on group memberships, they do not allow programatic entry
rendering. Hence, Genshi templates not only provide more control options,
but it also provide the ability to perform new entry rendering operations.
The `Genshi templating system`_ is used internally.
.. _Genshi templating system: http://genshi.edgewall.org/
Use
---
.. warning::
Group tags are not used inside of Genshi templates. You can get the
same logic (and more) using Genshi conditionals.
.. code-block:: xml
<py:if test="groupname in metadata.groups">
</py:if>
Bcfg uses the Genshi API for templates, and performs a XML format
stream rendering of the template into an lxml entry, which is included
in the client configuration. :ref:`Client metadata <client-metadata>`
is avilable inside of the template using the 'metadata' name. Note that
only the markup Genshi template format can be used, as the target output
format is XML.
A Genshi template looks much like a Bundler file, except the Bundle tag
has an additional `xmlns:py` attribute. See the examples.
Troubleshooting
---------------
There is no :ref:`bcfg2-info <server-bcfg2-info>` command like
``buildfile`` for Bundler templates, so if you want to generate a
Bundler template for a given client, you have to do so manually by
first invoking ``bcfg2-info debug``, then run::
metadata = self.build_metadata("<hostname>")
path = "<full path to template file>"
from genshi.template import TemplateLoader, MarkupTemplate
template = TemplateLoader().load(path, cls=MarkupTemplate)
print template.generate(metadata=metadata).render('xml')
``path`` needs to be the full path to the template file on the
filesystem, not just within the Bcfg2 repo.
If you are making changes to your template and you want to tell your
filemonitor to process any pending changes, you can do the following::
self.fam.handle_events_in_interval(0.1)
Altsrc
======
.. toctree::
:maxdepth: 1
../altsrc
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.
.. code-block:: xml
<Bundle name='foo' 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 :ref:`server-plugins-generators-cfg`,
:ref:`server-plugins-generators-tcheetah`, or
:ref:`server-plugins-generators-tgenshi-index`, or can be mapped to a
single entry by using the :ref:`server-plugins-structures-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.
.. code-block:: xml
<Bundle name='networkinterfaces' 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:
.. code-block:: xml
<Bundle name='bacula' 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:
.. code-block:: xml
<Bundle name='bacula' 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>
The latter form is preferred if the if block contains multiple
files. While this example is simple, the test in the if block can in
fact be any python statement.
.. _server-plugins-structures-bundler-index-examples:
Other examples
==============
Some simple examples of Bundles can be found in the `Bcfg2 example repository`_.
.. _Bcfg2 example repository: https://github.com/solj/bcfg2-repo
In addition to the example repository, the following is a list of some
more complex example Bundles.
.. toctree::
:maxdepth: 1
kernel
moab
nagios
ntp
snmpd
torque
yp
|