This page documents the TGenshi plugin. This plugin works with version 0.4 and newer of the genshi library.
The TGenshi plugin allows you to use the Genshi templating system to create files, instead of the various diff-based methods offered by the Cfg plugin. It also allows you to include the results of probes executed on the client in the created files.
To begin, you will need to download and install the Genshi templating engine.
To install on CentOS or RHEL 5, run:
sudo yum install python-genshi
Once it is installed, you can enable it by adding TGenshi to the generators line in /etc/bcfg2.conf on your Bcfg server. For example:
plugins = Base,Bundler,Cfg,...,TGenshi
The TGenshi plugin makes use of a Cfg-like directory structure located in in a TGenshi subdirectory of your repository, usually /var/lib/bcfg2/TGenshi. Each file has a directory containing two file types, template and info. Templates are named according to the genshi format used; template.txt uses the genshi text format, and template.xml uses the XML format.
If used with Genshi 0.5 or later the plugin also supports the new style text template format for files named template.newtxt. One of the advantages of the new format is that it does not use # as a command delimiter, making it easier to utilize for configuration files that use # as a comment character.
Only one template format may be used per file served. Info files are identical to those used in Cfg, and info.xml files are supported.
metadata is the client’s metadata
metadata.Properties is an xml document of unstructured data (only available when used in conjunction with the Properties plugin)
name is the path name specified in bcfg
leading slash, and is relative to the Bcfg2 specification root. E.g., /Cfg/etc/foo.conf/foo.conf.genshi or /TGenshi/etc/foo.conf/template.newtxt.H_foo.example.com
See the genshi documentation for examples of Genshi syntax.
Genshi’s web pages recommend against using this syntax, as it may disappear from future releases.
Templates are also useful for cases where more sophisticated boolean operations than those supported by Cfg are needed. For example, the template:
#if "ypbound" in metadata.groups and "workstation" in metadata.groups
client is ypbound workstation
#end
#if "ubuntu" not in metadata.groups and "desktop" in metadata.groups
client is a desktop, but not an ubuntu desktop
#end
Produces:
<Path type="file" name="/bar.conf" owner="root" perms="0644" group="root">client is ypbound workstation
client is a desktop, but not an ubuntu desktop
</Path>
This flexibility provides the ability to build much more compact and succinct definitions of configuration contents than Cfg can.
When developing a template, you can see what the template would generate on a client with bcfg2-info:
bcfg2-info buildfile <path> <hostname>
E.g.:
bcfg2-info buildfile /etc/foo.conf foo.example.com
To generate a file with an altsrc attribute, you can run:
bcfg2-info buildfile /etc/foo/foo.conf --altsrc=/etc/foo.conf \
foo.example.com
Sometimes, it’s useful to be able to do more in-depth troubleshooting by running the template manually. To do this, run bcfg2-info debug, and, once in the Python interpreter, run:
metadata = self.build_metadata("<hostname>")
path = "<relative path to template (see note below)>"
path should be set to the path to the template file with a leading slash, relative to the Bcfg2 specification root. See Inside of Templates for examples.
Then, run:
import os, Bcfg2.Options
from genshi.template import TemplateLoader, NewTextTemplate
name = os.path.dirname(path[path.find('/', 1):])
setup = Bcfg2.Options.OptionParser({'repo':
Bcfg2.Options.SERVER_REPOSITORY})
setup.parse('--')
template = TemplateLoader().load(setup['repo'] + path, cls=NewTextTemplate)
print template.generate(metadata=metadata, path=path, name=name).render()
This gives you more fine-grained control over how your template is rendered.
You can also use this approach to render templates that depend on altsrc tags by setting path to the path to the template, and setting name to the path to the file to be generated, e.g.:
metadata = self.build_metadata("foo.example.com")
path = "/Cfg/etc/sysconfig/network-scripts/ifcfg-template/ifcfg-template.genshi"
name = "/etc/sysconfig/network-scripts/ifcfg-bond0"
File permissions for entries handled by TGenshi are controlled via the use of Info files. Note that you cannot use both a Permissions entry and a Path entry to handle the same file.
Situations may arise where a templated file cannot be generated due to missing or incomplete information. A TemplateError can be raised to force a bind failure and prevent sending an incomplete file to the client. For example, this template:
{% python
from genshi.template import TemplateError
grp = None
for g in metadata.groups:
if g.startswith('ganglia-gmond-'):
grp = g
break
else:
raise TemplateError, "Missing group"
%}\
will fail to bind if the client is not a member of a group starting with “ganglia-gmond-”. The syslogs on the server will contain this message:
bcfg2-server[5957]: Genshi template error: Missing group
bcfg2-server[5957]: Failed to bind entry: Path /etc/ganglia/gmond.conf
indicating the bind failure and message raised with the TemplateError.
Question
How do I escape the $ (dollar sign) in a TGenshi text template? For example, if I want to include SVN (subversion) keywords like $Id$ or $HeadURL$ in TGenshi-generated files, or am templating a bourne shell (sh/bash) script or Makefile (make).
Answer
Use $$ (double dollar sign) to output a literal $ (dollarsign) in a TGenshi text template. So instead of $Id$, you’d use $$Id$$. See also Genshi tickets #282: Document $$ escape convention and #283: Allow for redefinition of template syntax per-file.