diff options
Diffstat (limited to 'src/lib/Bcfg2')
-rw-r--r-- | src/lib/Bcfg2/Server/Lint/Metadata.py | 38 | ||||
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/GroupLogic.py | 20 |
2 files changed, 49 insertions, 9 deletions
diff --git a/src/lib/Bcfg2/Server/Lint/Metadata.py b/src/lib/Bcfg2/Server/Lint/Metadata.py index bf19c25fc..248b1610c 100644 --- a/src/lib/Bcfg2/Server/Lint/Metadata.py +++ b/src/lib/Bcfg2/Server/Lint/Metadata.py @@ -106,15 +106,35 @@ class Metadata(ServerPlugin): "client") def duplicate_groups(self): - """ Check for groups that are defined more than once. We - count a group tag as a definition if it a) has profile or - public set; or b) has any children.""" - allgroups = [ - g - for g in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + - self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group") - if g.get("profile") or g.get("public") or g.getchildren()] - self.duplicate_entries(allgroups, "group") + """ Check for groups that are defined more than once. There + are two ways this can happen: + + 1. The group is listed twice with contradictory options. + 2. The group is listed with no options *first*, and then with + options later. + + In this context, 'first' refers to the order in which groups + are parsed; see the loop condition below and + _handle_groups_xml_event above for details. """ + groups = dict() + duplicates = dict() + for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \ + self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"): + grpname = grp.get("name") + if grpname in duplicates: + duplicates[grpname].append(grp) + elif len(grp.attrib) > 1: # group has options + if grpname in groups: + duplicates[grpname] = [grp, groups[grpname]] + else: + groups[grpname] = grp + else: # group has no options + groups[grpname] = grp + for grpname, grps in duplicates.items(): + self.LintError("duplicate-group", + "Group %s is defined multiple times:\n%s" % + (grpname, + "\n".join(self.RenderXML(g) for g in grps))) def duplicate_entries(self, allentries, etype): """ Generic duplicate entry finder. diff --git a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py index 1da7c8fec..ebcab1a6b 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupLogic.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupLogic.py @@ -3,6 +3,7 @@ template to dynamically set additional groups for clients. """ import os import lxml.etree +from threading import local import Bcfg2.Server.Plugin from Bcfg2.Server.Plugins.Metadata import MetadataGroup @@ -30,14 +31,32 @@ class GroupLogic(Bcfg2.Server.Plugin.Plugin, """ GroupLogic is a connector plugin that lets you use an XML Genshi template to dynamically set additional groups for clients. """ + # perform grouplogic later than other Connector plugins, so it can + # use groups set by them + sort_order = 1000 def __init__(self, core, datastore): Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore) Bcfg2.Server.Plugin.Connector.__init__(self) self.config = GroupLogicConfig(os.path.join(self.data, "groups.xml"), should_monitor=True) + self._local = local() + # building is a thread-local set that tracks which machines + # GroupLogic is getting additional groups for. If a + # get_additional_groups() is called twice for a machine before + # the first call has completed, the second call returns an + # empty list. This is for infinite recursion protection; + # without this check, it'd be impossible to use things like + # metadata.query.in_group() in GroupLogic, since that requires + # building all metadata, which requires running + # GroupLogic.get_additional_groups() for all hosts, which + # requires building all metadata... + self._local.building = set() def get_additional_groups(self, metadata): + if metadata.hostname in self._local.building: + return [] + self._local.building.add(metadata.hostname) rv = [] for el in self.config.XMLMatch(metadata).findall("Group"): if el.get("category"): @@ -45,4 +64,5 @@ class GroupLogic(Bcfg2.Server.Plugin.Plugin, category=el.get("category"))) else: rv.append(el.get("name")) + self._local.building.discard(metadata.hostname) return rv |