diff options
author | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2013-10-29 16:35:27 -0400 |
---|---|---|
committer | Chris St. Pierre <chris.a.st.pierre@gmail.com> | 2013-10-30 06:37:46 -0400 |
commit | c4e1b49ff7d5b8f5860f5cc208476ff42159724e (patch) | |
tree | 84da32caf2072e617dd6b7be992ff5971b50095a /src/lib/Bcfg2/Server/Plugin | |
parent | 2161b4be08f2b295f68e2f7c0f9c791919542a39 (diff) | |
download | bcfg2-c4e1b49ff7d5b8f5860f5cc208476ff42159724e.tar.gz bcfg2-c4e1b49ff7d5b8f5860f5cc208476ff42159724e.tar.bz2 bcfg2-c4e1b49ff7d5b8f5860f5cc208476ff42159724e.zip |
Plugins: Added TemplateDataProvider plugin interface
This lets you provide variables to the top-level namespace of
templates in a more seamless way than through a Connector plugin.
It's mostly useful for TemplateHelper for now, but may find other uses
in the future.
Diffstat (limited to 'src/lib/Bcfg2/Server/Plugin')
-rw-r--r-- | src/lib/Bcfg2/Server/Plugin/helpers.py | 84 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugin/interfaces.py | 37 |
2 files changed, 106 insertions, 15 deletions
diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index 4cd87bd70..1cb5a7b3e 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -16,7 +16,7 @@ import Bcfg2.Server.FileMonitor from Bcfg2.Logger import Debuggable from Bcfg2.Compat import CmpMixin, wraps from Bcfg2.Server.Plugin.base import Plugin -from Bcfg2.Server.Plugin.interfaces import Generator +from Bcfg2.Server.Plugin.interfaces import Generator, TemplateDataProvider from Bcfg2.Server.Plugin.exceptions import SpecificityError, \ PluginExecutionError @@ -127,6 +127,82 @@ def default_path_metadata(): 'paranoid', 'sensitive']]) +class DefaultTemplateDataProvider(TemplateDataProvider): + """ A base + :class:`Bcfg2.Server.Plugin.interfaces.TemplateDataProvider` that + provides default data for text and XML templates. + + Note that, since Cheetah and Genshi text templates treat the + ``path`` variable differently, this is overridden, by + :class:`Bcfg2.Server.Plugins.Cfg.CfgCheetahGenerator.DefaultCheetahDataProvider` + and + :class:`Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator.DefaultGenshiDataProvider`, + respectively. """ + + def get_template_data(self, entry, metadata, template): + return dict(name=entry.get('realname', entry.get('name')), + metadata=metadata, + source_path=template, + repo=Bcfg2.Options.setup.repository) + + def get_xml_template_data(self, _, metadata): + return dict(metadata=metadata, + repo=Bcfg2.Options.setup.repository) + +_sentinel = object() # pylint: disable=C0103 + + +def _get_template_data(func_name, args, default=_sentinel): + """ Generic template data getter for both text and XML templates. + + :param func_name: The name of the function to call on + :class:`Bcfg2.Server.Plugin.interfaces.TemplateDataProvider` + objects to get data for this template type. + Should be one of either ``get_template_data`` + for text templates, or ``get_xml_template_data`` + for XML templates. + :type func_name: string + :param args: The arguments to pass to the data retrieval function + :type args: list + :param default: An object that provides a set of base values. If + this is not provided, an instance of + :class:`Bcfg2.Server.Plugin.helpers.DefaultTemplateDataProvider` + is used. This can be set to None to avoid setting + any base values at all. + :type default: Bcfg2.Server.Plugin.interfaces.TemplateDataProvider + """ + if default is _sentinel: + default = DefaultTemplateDataProvider() + providers = Bcfg2.Server.core.plugins_by_type(TemplateDataProvider) + if default is not None: + providers.insert(0, default) + + rv = dict() + source = dict() + for prov in providers: + pdata = getattr(prov, func_name)(*args) + for key, val in pdata.items(): + if key not in rv: + rv[key] = val + source[key] = prov + else: + LOGGER.warning("Duplicate template variable %s provided by " + "both %s and %s" % (key, prov, source[key])) + return rv + + +def get_template_data(entry, metadata, template, default=_sentinel): + """ Get all template variables for a text (i.e., Cfg) template """ + return _get_template_data("get_template_data", [entry, metadata, template], + default=default) + + +def get_xml_template_data(structfile, metadata, default=_sentinel): + """ Get all template variables for an XML template """ + return _get_template_data("get_xml_template_data", [structfile, metadata], + default=default) + + class DatabaseBacked(Plugin): """ Provides capabilities for a plugin to read and write to a database. The plugin must add an option to flag database use with @@ -741,10 +817,8 @@ class StructFile(XMLFileBacked): XML data """ stream = self.template.generate( - metadata=metadata, - repo=Bcfg2.Options.setup.repository).filter(removecomment) - return lxml.etree.XML(stream.render('xml', - strip_whitespace=False), + **get_xml_template_data(self, metadata)).filter(removecomment) + return lxml.etree.XML(stream.render('xml', strip_whitespace=False), parser=Bcfg2.Server.XMLParser) def _match(self, item, metadata, *args): diff --git a/src/lib/Bcfg2/Server/Plugin/interfaces.py b/src/lib/Bcfg2/Server/Plugin/interfaces.py index d51c199cd..622b69c79 100644 --- a/src/lib/Bcfg2/Server/Plugin/interfaces.py +++ b/src/lib/Bcfg2/Server/Plugin/interfaces.py @@ -12,6 +12,11 @@ from Bcfg2.Server.Plugin.base import Plugin from Bcfg2.Server.Plugin.exceptions import PluginInitError, \ MetadataRuntimeError, MetadataConsistencyError +# Since this file basically just contains abstract interface +# descriptions, just about every function declaration has unused +# arguments. Disable this pylint warning for the whole file. +# pylint: disable=W0613 + class Generator(object): """ Generator plugins contribute to literal client configurations. @@ -27,13 +32,12 @@ class Generator(object): generate the content. The callable will receive two arguments: the abstract entry (as an lxml.etree._Element object), and the client metadata object the entry is being generated for. - #. If the entry is not listed in ``Entries``, the Bcfg2 core calls :func:`HandlesEntry`; if that returns True, then it calls :func:`HandleEntry`. """ - def HandlesEntry(self, entry, metadata): # pylint: disable=W0613 + def HandlesEntry(self, entry, metadata): """ HandlesEntry is the slow path method for routing configuration binding requests. It is called if the ``Entries`` dict does not contain a method for binding the @@ -48,7 +52,7 @@ class Generator(object): """ return False - def HandleEntry(self, entry, metadata): # pylint: disable=W0613 + def HandleEntry(self, entry, metadata): """ HandleEntry is the slow path method for binding configuration binding requests. It is called if the ``Entries`` dict does not contain a method for binding the @@ -138,7 +142,6 @@ class Metadata(object): """ pass - # pylint: disable=W0613 def resolve_client(self, address, cleanup_cache=False): """ Resolve the canonical name of this client. If this method is not implemented, the hostname claimed by the client is @@ -156,7 +159,6 @@ class Metadata(object): :class:`Bcfg2.Server.Plugin.exceptions.MetadataConsistencyError` """ return address[1] - # pylint: enable=W0613 def AuthenticateConnection(self, cert, user, password, address): """ Authenticate the given client. @@ -219,7 +221,7 @@ class Connector(object): """ Connector plugins augment client metadata instances with additional data, additional groups, or both. """ - def get_additional_groups(self, metadata): # pylint: disable=W0613 + def get_additional_groups(self, metadata): """ Return a list of additional groups for the given client. Each group can be either the name of a group (a string), or a :class:`Bcfg2.Server.Plugins.Metadata.MetadataGroup` object @@ -250,7 +252,7 @@ class Connector(object): """ return list() - def get_additional_data(self, metadata): # pylint: disable=W0613 + def get_additional_data(self, metadata): """ Return arbitrary additional data for the given ClientMetadata object. By convention this is usually a dict object, but doesn't need to be. @@ -473,7 +475,7 @@ class ThreadedStatistics(Statistics, Threaded, threading.Thread): # Someone who understands these interfaces better needs to write docs # for PullSource and PullTarget class PullSource(object): - def GetExtra(self, client): # pylint: disable=W0613 + def GetExtra(self, client): return [] def GetCurrentEntry(self, client, e_type, e_name): @@ -630,7 +632,7 @@ class ClientACLs(object): """ ClientACLs are used to grant or deny access to different XML-RPC calls based on client IP or metadata. """ - def check_acl_ip(self, address, rmi): # pylint: disable=W0613 + def check_acl_ip(self, address, rmi): """ Check if the given IP address is authorized to make the named XML-RPC call. @@ -643,7 +645,7 @@ class ClientACLs(object): """ return True - def check_acl_metadata(self, metadata, rmi): # pylint: disable=W0613 + def check_acl_metadata(self, metadata, rmi): """ Check if the given client is authorized to make the named XML-RPC call. @@ -654,3 +656,18 @@ class ClientACLs(object): :returns: bool """ return True + + +class TemplateDataProvider(object): + """ TemplateDataProvider plugins provide variables to templates + for use in rendering. """ + + def get_template_data(self, entry, metadata, template): + """ Get a dict of variables that will be supplied to a Cfg + template for rendering """ + return dict() + + def get_xml_template_data(self, structfile, metadata): + """ Get a dict of variables that will be supplied to an XML + template (e.g., a bundle) for rendering """ + return dict() |