From cfa769abf8e464acee43e1cac359b0f7e5fc5e4b Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 23 Jan 2013 14:13:51 -0500 Subject: added genshi support to StructFile --- src/lib/Bcfg2/Server/Lint/GroupNames.py | 4 +- src/lib/Bcfg2/Server/Lint/RequiredAttrs.py | 15 +---- src/lib/Bcfg2/Server/Plugin/helpers.py | 52 +++++++++++++++- src/lib/Bcfg2/Server/Plugins/Bundler.py | 96 ++---------------------------- 4 files changed, 58 insertions(+), 109 deletions(-) (limited to 'src/lib/Bcfg2') diff --git a/src/lib/Bcfg2/Server/Lint/GroupNames.py b/src/lib/Bcfg2/Server/Lint/GroupNames.py index 4ce12eae7..e41ed867e 100644 --- a/src/lib/Bcfg2/Server/Lint/GroupNames.py +++ b/src/lib/Bcfg2/Server/Lint/GroupNames.py @@ -3,7 +3,6 @@ import os import re import Bcfg2.Server.Lint -from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile class GroupNames(Bcfg2.Server.Lint.ServerPlugin): @@ -38,8 +37,7 @@ class GroupNames(Bcfg2.Server.Lint.ServerPlugin): def check_bundles(self): """ Check groups used in the Bundler plugin for validity """ for bundle in self.core.plugins['Bundler'].entries.values(): - if (self.HandlesFile(bundle.name) and - not isinstance(bundle, BundleTemplateFile)): + if self.HandlesFile(bundle.name) and bundle.template is None: self.check_entries(bundle.xdata.xpath("//Group"), bundle.name) diff --git a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py index bf72d26d0..60525d5a1 100644 --- a/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py +++ b/src/lib/Bcfg2/Server/Lint/RequiredAttrs.py @@ -3,12 +3,10 @@ verified with an XML schema alone""" import os import re -import lxml.etree import Bcfg2.Server.Lint import Bcfg2.Client.Tools.POSIX import Bcfg2.Client.Tools.VCS from Bcfg2.Server.Plugins.Packages import Apt, Yum -from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile # format verifying functions @@ -178,16 +176,9 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin): return for bundle in self.core.plugins['Bundler'].entries.values(): - if (self.HandlesFile(bundle.name) and - not isinstance(bundle, BundleTemplateFile)): - try: - xdata = lxml.etree.XML(bundle.data) - except (lxml.etree.XMLSyntaxError, AttributeError): - xdata = \ - lxml.etree.parse(bundle.template.filepath).getroot() - - for path in \ - xdata.xpath("//*[substring(name(), 1, 5) = 'Bound']"): + if self.HandlesFile(bundle.name) and bundle.template is None: + for path in bundle.xdata.xpath( + "//*[substring(name(), 1, 5) = 'Bound']"): self.check_entry(path, bundle.name) def check_entry(self, entry, filename): diff --git a/src/lib/Bcfg2/Server/Plugin/helpers.py b/src/lib/Bcfg2/Server/Plugin/helpers.py index c85253be6..9d76337e0 100644 --- a/src/lib/Bcfg2/Server/Plugin/helpers.py +++ b/src/lib/Bcfg2/Server/Plugin/helpers.py @@ -5,6 +5,7 @@ import re import sys import copy import time +import genshi import logging import operator import lxml.etree @@ -17,7 +18,6 @@ from Bcfg2.Server.Plugin.base import Debuggable, Plugin from Bcfg2.Server.Plugin.interfaces import Generator from Bcfg2.Server.Plugin.exceptions import SpecificityError, \ PluginExecutionError -import genshi.core try: import Bcfg2.Encryption @@ -590,9 +590,31 @@ class StructFile(XMLFileBacked): def __init__(self, filename, should_monitor=False): XMLFileBacked.__init__(self, filename, should_monitor=should_monitor) self.setup = Bcfg2.Options.get_option_parser() + self.encoding = self.setup['encoding'] + self.template = None def Index(self): XMLFileBacked.Index(self) + if (self.name.endswith('.genshi') or + ('py' in self.xdata.nsmap and + self.xdata.nsmap['py'] == 'http://genshi.edgewall.org/')): + try: + loader = genshi.template.TemplateLoader() + self.template = loader.load(self.name, + cls=genshi.template.MarkupTemplate, + encoding=self.encoding) + except LookupError: + err = sys.exc_info()[1] + LOGGER.error('Genshi lookup error in %s: %s' % (self.name, + err)) + except genshi.template.TemplateError: + err = sys.exc_info()[1] + LOGGER.error('Genshi template error in %s: %s' % (self.name, + err)) + except genshi.input.ParseError: + err = sys.exc_info()[1] + LOGGER.error('Genshi parse error in %s: %s' % (self.name, err)) + if self.encryption and HAS_CRYPTO: strict = self.xdata.get( "decrypt", @@ -644,6 +666,21 @@ class StructFile(XMLFileBacked): else: return True + def _render(self, metadata): + """ Render the template for the given client metadata + + :param metadata: Client metadata to match against. + :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata + :returns: lxml.etree._Element object representing the rendered + XML data + """ + stream = self.template.generate( + metadata=metadata, + repo=self.setup['repo']).filter(removecomment) + return lxml.etree.XML(stream.render('xml', + strip_whitespace=False), + parser=Bcfg2.Server.XMLParser) + def _match(self, item, metadata): """ recursive helper for Match() """ if self._include_element(item, metadata): @@ -677,7 +714,13 @@ class StructFile(XMLFileBacked): :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata :returns: list of lxml.etree._Element objects """ rv = [] - for child in self.entries: + if self.template is None: + entries = self.entries + else: + entries = self._render(metadata).getchildren() + print "rendered: %s" % lxml.etree.tostring(self._render(metadata), + pretty_print=True) + for child in entries: rv.extend(self._match(child, metadata)) return rv @@ -713,7 +756,10 @@ class StructFile(XMLFileBacked): :param metadata: Client metadata to match against. :type metadata: Bcfg2.Server.Plugins.Metadata.ClientMetadata :returns: lxml.etree._Element """ - rv = copy.deepcopy(self.xdata) + if self.template is None: + rv = copy.deepcopy(self.xdata) + else: + rv = self._render(metadata) for child in rv.iterchildren(): self._xml_match(child, metadata) return rv diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index 3907794ae..051443e22 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -4,101 +4,35 @@ import os import re import sys import copy -import logging import lxml.etree import Bcfg2.Server import Bcfg2.Server.Plugin import Bcfg2.Server.Lint -from Bcfg2.Options import get_option_parser - -import genshi.input -from genshi.template import TemplateLoader, MarkupTemplate, TemplateError +from genshi.template import TemplateError class BundleFile(Bcfg2.Server.Plugin.StructFile): """ Representation of a bundle XML file """ def get_xml_value(self, metadata): """ get the XML data that applies to the given client """ - bundlename = os.path.splitext(os.path.basename(self.name))[0] - bundle = lxml.etree.Element('Bundle', name=bundlename) + bundle = lxml.etree.Element('Bundle', name=self.xdata.get("name")) for item in self.Match(metadata): bundle.append(copy.copy(item)) return bundle -class BundleTemplateFile(Bcfg2.Server.Plugin.StructFile): - """ Representation of a Genshi-templated bundle XML file """ - - def __init__(self, name, encoding): - Bcfg2.Server.Plugin.StructFile.__init__(self, name) - self.encoding = encoding - self.logger = logging.getLogger(name) - self.template = None - - def HandleEvent(self, event=None): - """Handle all fs events for this template.""" - if event and event.code2str() == 'deleted': - return - try: - loader = TemplateLoader() - self.template = loader.load(self.name, cls=MarkupTemplate, - encoding=self.encoding) - except LookupError: - err = sys.exc_info()[1] - self.logger.error('Genshi lookup error in %s: %s' % - (self.name, err)) - except TemplateError: - err = sys.exc_info()[1] - self.logger.error('Genshi template error in %s: %s' % - (self.name, err)) - except genshi.input.ParseError: - err = sys.exc_info()[1] - self.logger.error('Genshi parse error in %s: %s' % - (self.name, err)) - - def get_xml_value(self, metadata): - """ get the rendered XML data that applies to the given - client """ - if not hasattr(self, 'template'): - 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, - repo=get_option_parser()['repo'] - ).filter(Bcfg2.Server.Plugin.removecomment) - data = lxml.etree.XML(stream.render('xml', - strip_whitespace=False), - parser=Bcfg2.Server.XMLParser) - bundlename = os.path.splitext(os.path.basename(self.name))[0] - bundle = lxml.etree.Element('Bundle', name=bundlename) - for item in self.Match(metadata, data): - bundle.append(copy.deepcopy(item)) - return bundle - - def Match(self, metadata, xdata): # pylint: disable=W0221 - """Return matching fragments of parsed template.""" - rv = [] - for child in xdata.getchildren(): - rv.extend(self._match(child, metadata)) - self.logger.debug("File %s got %d match(es)" % (self.name, - len(rv))) - return rv - - class Bundler(Bcfg2.Server.Plugin.Plugin, Bcfg2.Server.Plugin.Structure, Bcfg2.Server.Plugin.XMLDirectoryBacked): """ The bundler creates dependent clauses based on the bundle/translation scheme from Bcfg1. """ __author__ = 'bcfg-dev@mcs.anl.gov' + __child__ = BundleFile patterns = re.compile('^(?P.*)\.(xml|genshi)$') def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) Bcfg2.Server.Plugin.Structure.__init__(self) - self.encoding = core.setup['encoding'] - self.__child__ = self.template_dispatch try: Bcfg2.Server.Plugin.XMLDirectoryBacked.__init__(self, self.data) except OSError: @@ -107,19 +41,6 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, self.logger.error(msg) raise Bcfg2.Server.Plugin.PluginInitError(msg) - def template_dispatch(self, name, _): - """ Add the correct child entry type to Bundler depending on - whether the XML file in question is a plain XML file or a - templated bundle """ - bundle = lxml.etree.parse(name, - parser=Bcfg2.Server.XMLParser) - nsmap = bundle.getroot().nsmap - if (name.endswith('.genshi') or - ('py' in nsmap and nsmap['py'] == 'http://genshi.edgewall.org/')): - return BundleTemplateFile(name, self.encoding) - else: - return BundleFile(name) - def BuildStructures(self, metadata): """Build all structures for client (metadata).""" bundleset = [] @@ -156,8 +77,7 @@ class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): """ run plugin """ self.missing_bundles() for bundle in self.core.plugins['Bundler'].entries.values(): - if (self.HandlesFile(bundle.name) and - not isinstance(bundle, BundleTemplateFile)): + if self.HandlesFile(bundle.name): self.bundle_names(bundle) @classmethod @@ -186,14 +106,8 @@ class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): def bundle_names(self, bundle): """ verify bundle name attribute matches filename """ - try: - xdata = lxml.etree.XML(bundle.data) - except AttributeError: - # genshi template - xdata = lxml.etree.parse(bundle.template.filepath).getroot() - fname = os.path.splitext(os.path.basename(bundle.name))[0] - bname = xdata.get('name') + bname = bundle.xdata.get('name') if fname != bname: self.LintError("inconsistent-bundle-name", "Inconsistent bundle name: filename is %s, " -- cgit v1.2.3-1-g7c22