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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
""" ``bcfg2-lint`` plugin for :ref:`Metadata
<server-plugins-grouping-metadata>` """
from Bcfg2.Server.Lint import ServerPlugin
class Metadata(ServerPlugin):
""" ``bcfg2-lint`` plugin for :ref:`Metadata
<server-plugins-grouping-metadata>`. This checks for several things:
* ``<Client>`` tags nested inside other ``<Client>`` tags;
* Deprecated options (like ``location="floating"``);
* Profiles that don't exist, or that aren't profile groups;
* Groups or clients that are defined multiple times;
* Multiple default groups or a default group that isn't a profile
group.
"""
def Run(self):
self.nested_clients()
self.deprecated_options()
self.bogus_profiles()
self.duplicate_groups()
self.duplicate_default_groups()
self.duplicate_clients()
self.default_is_profile()
@classmethod
def Errors(cls):
return {"nested-client-tags": "warning",
"deprecated-clients-options": "warning",
"nonexistent-profile-group": "error",
"non-profile-set-as-profile": "error",
"duplicate-group": "error",
"duplicate-client": "error",
"multiple-default-groups": "error",
"default-is-not-profile": "error"}
def deprecated_options(self):
""" Check for the ``location='floating'`` option, which has
been deprecated in favor of ``floating='true'``. """
if not hasattr(self.metadata, "clients_xml"):
# using metadata database
return
clientdata = self.metadata.clients_xml.xdata
for el in clientdata.xpath("//Client"):
loc = el.get("location")
if loc:
if loc == "floating":
floating = True
else:
floating = False
self.LintError("deprecated-clients-options",
"The location='%s' option is deprecated. "
"Please use floating='%s' instead:\n%s" %
(loc, floating, self.RenderXML(el)))
def nested_clients(self):
""" Check for a ``<Client/>`` tag inside a ``<Client/>`` tag,
which is either redundant or will never match. """
groupdata = self.metadata.groups_xml.xdata
for el in groupdata.xpath("//Client//Client"):
self.LintError("nested-client-tags",
"Client %s nested within Client tag: %s" %
(el.get("name"), self.RenderXML(el)))
def bogus_profiles(self):
""" Check for clients that have profiles that are either not
flagged as profile groups in ``groups.xml``, or don't exist. """
if not hasattr(self.metadata, "clients_xml"):
# using metadata database
return
for client in self.metadata.clients_xml.xdata.findall('.//Client'):
profile = client.get("profile")
if profile not in self.metadata.groups:
self.LintError("nonexistent-profile-group",
"%s has nonexistent profile group %s:\n%s" %
(client.get("name"), profile,
self.RenderXML(client)))
elif not self.metadata.groups[profile].is_profile:
self.LintError("non-profile-set-as-profile",
"%s is set as profile for %s, but %s is not a "
"profile group:\n%s" %
(profile, client.get("name"), profile,
self.RenderXML(client)))
def duplicate_default_groups(self):
""" Check for multiple default groups. """
defaults = []
for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \
self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"):
if grp.get("default", "false").lower() == "true":
defaults.append(self.RenderXML(grp))
if len(defaults) > 1:
self.LintError("multiple-default-groups",
"Multiple default groups defined:\n%s" %
"\n".join(defaults))
def duplicate_clients(self):
""" Check for clients that are defined more than once. """
if not hasattr(self.metadata, "clients_xml"):
# using metadata database
return
self.duplicate_entries(
self.metadata.clients_xml.xdata.xpath("//Client"),
"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")
def duplicate_entries(self, allentries, etype):
""" Generic duplicate entry finder.
:param allentries: A list of all entries to check for
duplicates.
:type allentries: list of lxml.etree._Element
:param etype: The entry type. This will be used to determine
the error name (``duplicate-<etype>``) and for
display to the end user.
:type etype: string
"""
entries = dict()
for el in allentries:
if el.get("name") in entries:
entries[el.get("name")].append(self.RenderXML(el))
else:
entries[el.get("name")] = [self.RenderXML(el)]
for ename, els in entries.items():
if len(els) > 1:
self.LintError("duplicate-%s" % etype,
"%s %s is defined multiple times:\n%s" %
(etype.title(), ename, "\n".join(els)))
def default_is_profile(self):
""" Ensure that the default group is a profile group. """
if (self.metadata.default and
not self.metadata.groups[self.metadata.default].is_profile):
xdata = \
self.metadata.groups_xml.xdata.xpath("//Group[@name='%s']" %
self.metadata.default)[0]
self.LintError("default-is-not-profile",
"Default group is not a profile group:\n%s" %
self.RenderXML(xdata))
|