From 5f93d780fc2dcd6ba35179acd61c05754d1e4fbc Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 15 Jun 2011 12:21:17 -0400 Subject: made StructFile.Match() work with Group/Client tags inside other tags --- src/lib/Server/Plugin.py | 80 +++++++++++++++++------------------- src/lib/Server/Plugins/Properties.py | 45 ++++---------------- 2 files changed, 46 insertions(+), 79 deletions(-) diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index 3a36baf8e..aff9af12b 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -457,51 +457,45 @@ class StructFile(XMLFileBacked): """This file contains a set of structure file formatting logic.""" def __init__(self, name): XMLFileBacked.__init__(self, name) - self.fragments = {} - - def Index(self): - """Build internal data structures.""" - try: - xdata = lxml.etree.XML(self.data) - except lxml.etree.XMLSyntaxError: - logger.error("Failed to parse file %s" % self.name) - return - self.fragments = {} - work = {lambda x: True: xdata.getchildren()} - while work: - (predicate, worklist) = work.popitem() - self.fragments[predicate] = \ - [item for item in worklist - if (item.tag != 'Group' and - item.tag != 'Client' and - not isinstance(item, - lxml.etree._Comment))] - for item in worklist: - cmd = None - if item.tag == 'Group': - if item.get('negate', 'false').lower() == 'true': - cmd = "lambda x:'%s' not in x.groups and predicate(x)" - else: - cmd = "lambda x:'%s' in x.groups and predicate(x)" - elif item.tag == 'Client': - if item.get('negate', 'false').lower() == 'true': - cmd = "lambda x:x.hostname != '%s' and predicate(x)" - else: - cmd = "lambda x:x.hostname == '%s' and predicate(x)" - # else, ignore item - if cmd is not None: - newpred = eval(cmd % item.get('name'), - {'predicate':predicate}) - work[newpred] = item.getchildren() - + self.matches = {} + + def _match(self, item, metadata): + """ recursive helper for Match() """ + if isinstance(item, lxml.etree._Comment): + return [] + elif item.tag == 'Group': + rv = [] + if ((item.get('negate', 'false').lower() == 'true' and + item.get('name') not in metadata.groups) or + item.get('name') in metadata.groups): + for child in item.iterchildren(): + rv.extend(self._match(child, metadata)) + return rv + elif item.tag == 'Client': + rv = [] + if ((item.get('negate', 'false').lower() == 'true' and + item.get('name') != metadata.hostname) or + item.get('name') == metadata.hostname): + for child in item.iterchildren(): + rv.extend(self._match(child, metadata)) + return rv + else: + rv = copy.deepcopy(item) + for child in rv.iterchildren(): + rv.remove(child) + for child in item.iterchildren(): + rv.extend(self._match(child, metadata)) + return [rv] + def Match(self, metadata): """Return matching fragments of independent.""" - matching = [frag for (pred, frag) in list(self.fragments.items()) - if pred(metadata)] - if matching: - return reduce(lambda x, y: x + y, matching) - logger.error("File %s got null match" % (self.name)) - return [] + rv = [] + if metadata.hostname not in self.matches: + for child in self.entries(): + rv.extend(self._match(child, metadata)) + if not rv: + logger.error("File %s got null match" % (self.name)) + return rv class INode: diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py index dea797a10..95565f2e4 100644 --- a/src/lib/Server/Plugins/Properties.py +++ b/src/lib/Server/Plugins/Properties.py @@ -7,42 +7,15 @@ import Bcfg2.Server.Plugin class PropertyFile(Bcfg2.Server.Plugin.StructFile): """Class for properties files.""" def Index(self): - """Build internal data structures.""" - if type(self.data) is not lxml.etree._Element: - try: - self.data = lxml.etree.XML(self.data) - except lxml.etree.XMLSyntaxError: - Bcfg2.Server.Plugin.logger.error("Failed to parse %s" % - self.name) - - self.fragments = {} - work = {lambda x: True: self.data.getchildren()} - while work: - (predicate, worklist) = work.popitem() - self.fragments[predicate] = \ - [item for item in worklist - if (item.tag != 'Group' and - item.tag != 'Client' and - not isinstance(item, - lxml.etree._Comment))] - for item in worklist: - cmd = None - if item.tag == 'Group': - if item.get('negate', 'false').lower() == 'true': - cmd = "lambda x:'%s' not in x.groups and predicate(x)" - else: - cmd = "lambda x:'%s' in x.groups and predicate(x)" - elif item.tag == 'Client': - if item.get('negate', 'false').lower() == 'true': - cmd = "lambda x:x.hostname != '%s' and predicate(x)" - else: - cmd = "lambda x:x.hostname == '%s' and predicate(x)" - # else, ignore item - if cmd is not None: - newpred = eval(cmd % item.get('name'), - {'predicate':predicate}) - work[newpred] = item.getchildren() - + """Build local data structures.""" + try: + xdata = XML(self.data) + except XMLSyntaxError: + self.label = None + self.entries = [] + return + self.label = xdata.attrib[self.__identifier__] + self.entries = xdata.getchildren() class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked): -- cgit v1.2.3-1-g7c22 From bc1a6b8d0a46e37a108a752a7b6f54e637ff804d Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 15 Jun 2011 13:22:19 -0400 Subject: bug fixes --- src/lib/Server/Plugin.py | 20 ++++++++++++-------- src/lib/Server/Plugins/Properties.py | 12 +----------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index aff9af12b..e535802c7 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -435,12 +435,13 @@ class XMLFileBacked(FileBacked): def Index(self): """Build local data structures.""" try: - xdata = XML(self.data) + self.xdata = XML(self.data) except XMLSyntaxError: logger.error("Failed to parse %s" % (self.name)) return - self.label = xdata.attrib[self.__identifier__] - self.entries = xdata.getchildren() + self.entries = self.xdata.getchildren() + if self.__identifier__ is not None: + self.label = self.xdata.attrib[self.__identifier__] def __iter__(self): return iter(self.entries) @@ -455,6 +456,8 @@ class SingleXMLFileBacked(XMLFileBacked): class StructFile(XMLFileBacked): """This file contains a set of structure file formatting logic.""" + __identifier__ = None + def __init__(self, name): XMLFileBacked.__init__(self, name) self.matches = {} @@ -489,13 +492,14 @@ class StructFile(XMLFileBacked): def Match(self, metadata): """Return matching fragments of independent.""" - rv = [] if metadata.hostname not in self.matches: - for child in self.entries(): + rv = [] + for child in self.entries: rv.extend(self._match(child, metadata)) - if not rv: - logger.error("File %s got null match" % (self.name)) - return rv + if not rv: + logger.error("File %s got null match" % (self.name)) + self.matches[metadata.hostname] = rv + return self.matches[metadata.hostname] class INode: diff --git a/src/lib/Server/Plugins/Properties.py b/src/lib/Server/Plugins/Properties.py index 95565f2e4..54c5def57 100644 --- a/src/lib/Server/Plugins/Properties.py +++ b/src/lib/Server/Plugins/Properties.py @@ -6,17 +6,7 @@ import Bcfg2.Server.Plugin class PropertyFile(Bcfg2.Server.Plugin.StructFile): """Class for properties files.""" - def Index(self): - """Build local data structures.""" - try: - xdata = XML(self.data) - except XMLSyntaxError: - self.label = None - self.entries = [] - return - self.label = xdata.attrib[self.__identifier__] - self.entries = xdata.getchildren() - + pass class PropDirectoryBacked(Bcfg2.Server.Plugin.DirectoryBacked): __child__ = PropertyFile -- cgit v1.2.3-1-g7c22 From eec6ed6c3295755d00abdd40d82b6cfd0a023c2e Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Wed, 15 Jun 2011 14:20:18 -0400 Subject: updated docs --- doc/server/plugins/connectors/properties.txt | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/doc/server/plugins/connectors/properties.txt b/doc/server/plugins/connectors/properties.txt index 1cbc4cf65..3329f48bd 100644 --- a/doc/server/plugins/connectors/properties.txt +++ b/doc/server/plugins/connectors/properties.txt @@ -38,7 +38,7 @@ Usage Specific property files can be referred to in templates as ``metadata.Properties[]``. The -data attribute is an LXML element object. (Documented +``xdata`` attribute is an LXML element object. (Documented `here `_) Currently, only one access method is defined for this data, ``Match``. @@ -56,10 +56,21 @@ more details on how Group and Client tags are parsed.) For instance:: As we formulate more common use cases, we will add them to the !PropertyFile class as methods. This will simplify templates. +You can also access the XML data that comprises a property file +directly in one of several ways: + +* ``metadata.Properties['prop-file'].xdata`` is an lxml.etree._Element + object representing the top-level element in the file. +* ``metadata.Properties['prop-file'].data`` is the raw contents of the + property file as a string. +* ``metadata.Properties['prop-file'].entries`` is a list of + lxml.etree._Element objects representing the direct children of the + top-level element. (I.e., everything directly under the + ```` tag.) + Accessing Properties contents from TGenshi ========================================== Access contents of ``Properties/auth.xml``:: - - ${metadata.Properties['auth.xml'].data.find('file').find('bcfg2.key').text} + ${metadata.Properties['auth.xml'].xdata.find('file').find('bcfg2.key').text} -- cgit v1.2.3-1-g7c22