summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/GroupLogic.py
blob: 24547949bd93c1ef323d8b5aa053097b612ea1ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
""" GroupLogic is a connector plugin that lets you use an XML Genshi
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
try:
    from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile
except ImportError:
    # BundleTemplateFile missing means that genshi is missing.  we
    # import genshi to get the _real_ error
    import genshi  # pylint: disable=W0611


class GroupLogicConfig(BundleTemplateFile):
    """ Representation of the GroupLogic groups.xml file """
    create = lxml.etree.Element("GroupLogic",
                                nsmap=dict(py="http://genshi.edgewall.org/"))

    def __init__(self, name, fam):
        BundleTemplateFile.__init__(self, name,
                                    Bcfg2.Server.Plugin.Specificity(), None)
        self.fam = fam
        self.should_monitor = True
        self.fam.AddMonitor(self.name, self)

    def _match(self, item, metadata):
        if item.tag == 'Group' and not len(item.getchildren()):
            return [item]
        return BundleTemplateFile._match(self, item, metadata)


class GroupLogic(Bcfg2.Server.Plugin.Plugin,
                 Bcfg2.Server.Plugin.Connector):
    """ 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"),
                                       core.fam)
        self._local = local()

    def get_additional_groups(self, metadata):
        if not hasattr(self._local, "building"):
            # 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()
        if metadata.hostname in self._local.building:
            return []
        self._local.building.add(metadata.hostname)
        rv = []
        for el in self.config.get_xml_value(metadata).xpath("//Group"):
            if el.get("category"):
                rv.append(MetadataGroup(el.get("name"),
                                        category=el.get("category")))
            else:
                rv.append(el.get("name"))
        self._local.building.discard(metadata.hostname)
        return rv