summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2012-11-16 14:40:42 -0500
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2012-11-19 13:21:10 -0500
commit255046b3f3484219bbfa6646c88bbc4bbc1b5256 (patch)
tree221a3d6ad4cf08c601a8dd228541c5fc21457d17
parentf4da37aa0a360add3f5c40f37cd3cc010ef8788f (diff)
downloadbcfg2-255046b3f3484219bbfa6646c88bbc4bbc1b5256.tar.gz
bcfg2-255046b3f3484219bbfa6646c88bbc4bbc1b5256.tar.bz2
bcfg2-255046b3f3484219bbfa6646c88bbc4bbc1b5256.zip
Templating updates:
* Added "repo" variable to all template formats * Made variables available in Genshi and Cheetah template more similar * Improved docs
-rw-r--r--doc/server/plugins/generators/cfg.txt85
-rw-r--r--doc/server/plugins/structures/bundler/index.txt55
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bundler.py15
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py4
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py6
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgCheetahGenerator.py11
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgGenshiGenerator.py30
7 files changed, 116 insertions, 90 deletions
diff --git a/doc/server/plugins/generators/cfg.txt b/doc/server/plugins/generators/cfg.txt
index 25986a413..7d0e0acff 100644
--- a/doc/server/plugins/generators/cfg.txt
+++ b/doc/server/plugins/generators/cfg.txt
@@ -106,26 +106,13 @@ Genshi templates allow you to use the `Genshi
the deprecated :ref:`server-plugins-generators-tgenshi-index` plugin.
Genshi templates should be named with a ``.genshi`` extension, e.g.::
- % ls Cfg/etc/motd
+ % ls Cfg/etc/motd
info.xml motd.genshi
See the genshi `documentation
<http://genshi.edgewall.org/wiki/Documentation>`_ for examples of
Genshi syntax.
-Inside Genshi Templates
-~~~~~~~~~~~~~~~~~~~~~~~
-
-Several variables are pre-defined inside templates:
-
-* **metadata** is the client's :ref:`metadata
- <server-plugins-grouping-metadata-clientmetadata>`
-* **name** is the path name specified in Bcfg2
-* **path** is the path to the TGenshi template. It starts with a
- 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``
-
Troubleshooting
~~~~~~~~~~~~~~~
@@ -138,7 +125,8 @@ E.g.::
bcfg2-info buildfile /etc/foo.conf foo.example.com
-To generate a file with an altsrc attribute, you can run::
+To generate a file with an :ref:`altsrc
+<server-plugins-structures-altsrc>` attribute, you can run::
bcfg2-info buildfile /etc/foo/foo.conf --altsrc=/etc/foo.conf \
foo.example.com
@@ -149,13 +137,13 @@ 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 Genshi
Templates`_ for examples.
Then, run::
-
+
import os, Bcfg2.Options
from genshi.template import TemplateLoader, NewTextTemplate
name = os.path.dirname(path[path.find('/', 1):])
@@ -230,19 +218,9 @@ Cheetah templates allow you to use the `cheetah templating system
the deprecated :ref:`server-plugins-generators-tcheetah` plugin.
Cheetah templates should be named with a ``.cheetah`` extension, e.g.::
- % ls Cfg/etc/motd
+ % ls Cfg/etc/motd
info.xml motd.cheetah
-Inside Cheetah Templates
-~~~~~~~~~~~~~~~~~~~~~~~~
-
-Several variables are pre-defined inside templates:
-
-* **self.metadata** is the client's :ref:`metadata
- <server-plugins-grouping-metadata-clientmetadata>`
-* **self.path** is the path name specified in Bcfg2
-* **self.source_path** is the path to the Genshi template on the filesystem.
-
Examples
~~~~~~~~
@@ -265,12 +243,48 @@ comment to appear in the final config file.::
# This is a comment in my template which will be stripped when it's processed through Cheetah
\# This comment will appear in the generated config file.
+Inside Templates
+----------------
+
+Several variables are pre-defined inside templates:
+
++-------------+--------------------------------------------------------+
+| Name | Description |
++=============+========================================================+
+| metadata | :ref:`Client metadata |
+| | <server-plugins-grouping-metadata-clientmetadata>` |
++-------------+--------------------------------------------------------+
+| name | The value of the ``name`` attribute as specified in |
+| | the Path entry in Bcfg2. If an :ref:`altsrc |
+| | <server-plugins-structures-altsrc>` attribute is used, |
+| | then ``name`` will be the value of that attribute. |
++-------------+--------------------------------------------------------+
+| source_path | The path to the template file on the filesystem |
++-------------+--------------------------------------------------------+
+| repo | The path to the Bcfg2 repository on the filesystem |
++-------------+--------------------------------------------------------+
+| path | In Genshi templates, ``path`` is a synonym for |
+| | ``source_path``. In Cheetah templates, it's a synonym |
+| | for ``name``. For this reason, use of ``path`` is |
+| | discouraged, and it may be deprecated in a future |
+| | release. |
++-------------+--------------------------------------------------------+
+
+To access these variables in a Genshi template, you can simply use the
+name, e.g.::
+
+ Path to this file: ${name}
+
+In a Cheetah template, the variables are properties of ``self``,
+e.g.::
+
+ Path to this file: $self.name
Notes on Using Templates
------------------------
Templates can be host and group specific as well. Deltas will not be
-processed for any genshi or cheetah base file.
+processed for any Genshi or Cheetah base file.
.. note::
@@ -278,19 +292,22 @@ processed for any genshi or cheetah base file.
or group-specific files, you will need to ensure that the ``.genshi``
or ``.cheetah`` extension is at the **end** of the filename. Using the
examples from above for *host.example.com* and group *server* you would
- have the following (using genshi only)::
+ have the following::
Cfg/etc/fstab/fstab.H_host.example.com.genshi
- Cfg/etc/fstab/fstab.G50_server.genshi
+ Cfg/etc/fstab/fstab.G50_server.cheetah
Genshi templates take precence over cheetah templates. For example, if
-two files exist named
+two files exist named::
Cfg/etc/fstab/fstab.genshi
Cfg/etc/fstab/fstab.cheetah
-the cheetah template is ignored. But you can mix genshi and cheetah when
-using different host-specific or group-specific files. For example:
+The Cheetah template is ignored. Exploiting this fact is probably a
+pretty bad idea in practice.
+
+You can mix Genshi and Cheetah when using different host-specific or
+group-specific files. For example:
Cfg/etc/fstab/fstab.H_host.example.com.genshi
Cfg/etc/fstab/fstab.G50_server.cheetah
diff --git a/doc/server/plugins/structures/bundler/index.txt b/doc/server/plugins/structures/bundler/index.txt
index 528df79db..1cc287ebd 100644
--- a/doc/server/plugins/structures/bundler/index.txt
+++ b/doc/server/plugins/structures/bundler/index.txt
@@ -113,40 +113,31 @@ demonstrates how group entries can be used in bundles)
Genshi templates
================
-Genshi xml templates can be specified one of two ways:
+Genshi XML templates allow you to use the `Genshi
+<http://genshi.edgewall.org>`_ templating system to dynamically
+generate a bundle. Genshi templates can be specified one of two ways:
-1. Add an xml-style genshi template to the Bundler directory with a
+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
+2. Simply add the appropriate namespace attribute to your existing XML
bundle.
-The namespace attribute in this case should look like the following::
+The top-level Bundle tag should look like the following::
- xmlns:py="http://genshi.edgewall.org/"
+ <Bundle name="foo" xmlns:py="http://genshi.edgewall.org/">
-Motivation
-----------
+Several variables are pre-defined inside templates:
-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.
++-------------+--------------------------------------------------------+
+| Name | Description |
++=============+========================================================+
+| metadata | :ref:`Client metadata |
+| | <server-plugins-grouping-metadata-clientmetadata>` |
++-------------+--------------------------------------------------------+
+| repo | The path to the Bcfg2 repository on the filesystem |
++-------------+--------------------------------------------------------+
-The `Genshi templating system`_ is used internally.
-
-.. _Genshi templating system: http://genshi.edgewall.org/
-
-Use
----
-
-.. warning::
+.. note::
``<Group>`` and ``<Client>`` tags are allowed inside of Genshi
templates as of Bcfg2 1.2. However, they do not behave the same
@@ -154,7 +145,7 @@ Use
<py:if test="'groupname' in metadata.groups">
</py:if>
-
+
The conditional is evaluated when the template is rendered, so
code inside the conditional is not executed if the conditional
fails. A ``<Group>`` tag is evaluated *after* the template is
@@ -163,16 +154,6 @@ Use
groups, you *must* use a Genshi conditional, not a ``<Group>``
tag. The same caveats apply to ``<Client>`` tags.
-Bcfg2 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 available 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
---------------
diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py
index 7933fe9be..b200346bc 100644
--- a/src/lib/Bcfg2/Server/Plugins/Bundler.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py
@@ -19,6 +19,9 @@ except ImportError:
HAS_GENSHI = False
+SETUP = None
+
+
class BundleFile(Bcfg2.Server.Plugin.StructFile):
""" Representation of a bundle XML file """
def get_xml_value(self, metadata):
@@ -49,7 +52,8 @@ if HAS_GENSHI:
msg = "No parsed template information for %s" % self.name
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
- stream = self.template.generate(metadata=metadata).filter(
+ stream = self.template.generate(metadata=metadata,
+ repo=SETUP['repo']).filter(
Bcfg2.Server.Plugins.TGenshi.removecomment)
data = lxml.etree.XML(stream.render('xml',
strip_whitespace=False),
@@ -93,8 +97,13 @@ class Bundler(Bcfg2.Server.Plugin.Plugin,
self.data,
self.core.fam)
except OSError:
- self.logger.error("Failed to load Bundle repository")
- raise Bcfg2.Server.Plugin.PluginInitError
+ err = sys.exc_info()[1]
+ msg = "Failed to load Bundle repository %s: %s" % (self.data, err)
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginInitError(msg)
+
+ global SETUP
+ SETUP = core.setup
def template_dispatch(self, name, _):
""" Add the correct child entry type to Bundler depending on
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
index 8ebd8d921..724164cf5 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgCheetahGenerator.py
@@ -3,7 +3,7 @@
:ref:`server-plugins-generators-cfg` files. """
from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
try:
from Cheetah.Template import Template
@@ -37,7 +37,9 @@ class CfgCheetahGenerator(CfgGenerator):
template = Template(self.data.decode(self.encoding),
compilerSettings=self.settings)
template.metadata = metadata
+ template.name = entry.get('realname', entry.get('name'))
template.path = entry.get('realname', entry.get('name'))
template.source_path = self.name
+ template.repo = SETUP['repo']
return template.respond()
get_data.__doc__ = CfgGenerator.get_data.__doc__
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index df0c30c09..48f64ac7f 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -6,7 +6,7 @@ import re
import sys
import traceback
from Bcfg2.Server.Plugin import PluginExecutionError
-from Bcfg2.Server.Plugins.Cfg import CfgGenerator
+from Bcfg2.Server.Plugins.Cfg import CfgGenerator, SETUP
try:
import genshi.core
@@ -74,7 +74,9 @@ class CfgGenshiGenerator(CfgGenerator):
stream = \
self.template.generate(name=fname,
metadata=metadata,
- path=self.name).filter(removecomment)
+ path=self.name,
+ source_path=self.name,
+ repo=SETUP['repo']).filter(removecomment)
try:
try:
return stream.render('text', encoding=self.encoding,
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgCheetahGenerator.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgCheetahGenerator.py
index 1832e5e03..fc5d5e53d 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgCheetahGenerator.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgCheetahGenerator.py
@@ -31,9 +31,18 @@ if HAS_CHEETAH or can_skip:
ccg.data = "data"
entry = lxml.etree.Element("Path", name="/test.txt")
metadata = Mock()
+ Bcfg2.Server.Plugins.Cfg.CfgCheetahGenerator.SETUP = MagicMock()
self.assertEqual(ccg.get_data(entry, metadata),
mock_Template.return_value.respond.return_value)
+ Bcfg2.Server.Plugins.Cfg.CfgCheetahGenerator.SETUP.__getitem__.assert_called_with("repo")
mock_Template.assert_called_with("data".decode(ccg.encoding),
compilerSettings=ccg.settings)
- mock_Template.return_value.respond.assert_called_with()
+ tmpl = mock_Template.return_value
+ tmpl.respond.assert_called_with()
+ self.assertEqual(tmpl.metadata, metadata)
+ self.assertEqual(tmpl.name, entry.get("name"))
+ self.assertEqual(tmpl.path, entry.get("name"))
+ self.assertEqual(tmpl.source_path, ccg.name)
+ self.assertEqual(tmpl.repo,
+ Bcfg2.Server.Plugins.Cfg.CfgCheetahGenerator.SETUP.__getitem__.return_value)
diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgGenshiGenerator.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgGenshiGenerator.py
index 4a849c11a..385f8df77 100644
--- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgGenshiGenerator.py
+++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgGenshiGenerator.py
@@ -51,15 +51,24 @@ if can_skip or HAS_GENSHI:
entry = lxml.etree.Element("Path", name="/test.txt")
metadata = Mock()
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP = MagicMock()
+
def reset():
cgg.template.reset_mock()
cgg._handle_genshi_exception.reset_mock()
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.reset_mock()
+
+ template_vars = dict(
+ name=entry.get("name"),
+ metadata=metadata,
+ path=cgg.name,
+ source_path=cgg.name,
+ repo=Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.__getitem__.return_value)
self.assertEqual(cgg.get_data(entry, metadata),
stream.render.return_value)
- cgg.template.generate.assert_called_with(name=entry.get("name"),
- metadata=metadata,
- path=cgg.name)
+ cgg.template.generate.assert_called_with(**template_vars)
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.__getitem__.assert_called_with("repo")
fltr.filter.assert_called_with(removecomment)
stream.render.assert_called_with("text", encoding=cgg.encoding,
strip_whitespace=False)
@@ -71,9 +80,8 @@ if can_skip or HAS_GENSHI:
stream.render.side_effect = render
self.assertEqual(cgg.get_data(entry, metadata),
stream.render.return_value)
- cgg.template.generate.assert_called_with(name=entry.get("name"),
- metadata=metadata,
- path=cgg.name)
+ cgg.template.generate.assert_called_with(**template_vars)
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.__getitem__.assert_called_with("repo")
fltr.filter.assert_called_with(removecomment)
self.assertEqual(stream.render.call_args_list,
[call("text", encoding=cgg.encoding,
@@ -84,9 +92,8 @@ if can_skip or HAS_GENSHI:
stream.render.side_effect = UndefinedError("test")
self.assertRaises(UndefinedError,
cgg.get_data, entry, metadata)
- cgg.template.generate.assert_called_with(name=entry.get("name"),
- metadata=metadata,
- path=cgg.name)
+ cgg.template.generate.assert_called_with(**template_vars)
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.__getitem__.assert_called_with("repo")
fltr.filter.assert_called_with(removecomment)
stream.render.assert_called_with("text", encoding=cgg.encoding,
strip_whitespace=False)
@@ -96,9 +103,8 @@ if can_skip or HAS_GENSHI:
cgg._handle_genshi_exception.side_effect = ValueError
self.assertRaises(ValueError,
cgg.get_data, entry, metadata)
- cgg.template.generate.assert_called_with(name=entry.get("name"),
- metadata=metadata,
- path=cgg.name)
+ cgg.template.generate.assert_called_with(**template_vars)
+ Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.SETUP.__getitem__.assert_called_with("repo")
fltr.filter.assert_called_with(removecomment)
stream.render.assert_called_with("text", encoding=cgg.encoding,
strip_whitespace=False)