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
|
"""This plugin provides automatic dependency handling."""
import lxml.etree
import Bcfg2.Server.Plugin
from Bcfg2.Server.Plugin import PluginExecutionError
class Deps(Bcfg2.Server.Plugin.PrioDir,
Bcfg2.Server.Plugin.StructureValidator):
# Override the default sort_order (of 500) so that this plugin
# gets handled after others running at the default. In particular,
# we want to run after Packages, so we can see the final set of
# packages that will be installed on the client.
sort_order = 750
def __init__(self, core):
Bcfg2.Server.Plugin.PrioDir.__init__(self, core)
Bcfg2.Server.Plugin.StructureValidator.__init__(self)
self.cache = {}
def HandleEvent(self, event):
self.cache = {}
Bcfg2.Server.Plugin.PrioDir.HandleEvent(self, event)
def validate_structures(self, metadata, structures):
"""Examine the passed structures and append any additional
prerequisite entries as defined by the files in Deps.
"""
entries = []
for structure in structures:
for entry in structure.getchildren():
tag = entry.tag
if tag.startswith('Bound'):
tag = tag[5:]
if ((tag, entry.get('name')) not in entries
and not isinstance(entry, lxml.etree._Comment)):
entries.append((tag, entry.get('name')))
entries.sort()
entries = tuple(entries)
groups = list(metadata.groups)
groups.sort()
groups = tuple(groups)
# Check to see if we have cached the prereqs already
if (entries, groups) in self.cache:
prereqs = self.cache[(entries, groups)]
else:
prereqs = self.calculate_prereqs(metadata, entries)
self.cache[(entries, groups)] = prereqs
newstruct = lxml.etree.Element("Independent",
name=self.__class__.__name__)
for tag, name in prereqs:
lxml.etree.SubElement(newstruct, tag, name=name)
structures.append(newstruct)
def calculate_prereqs(self, metadata, entries):
"""Calculate the prerequisites defined in Deps for the passed
set of entries.
"""
prereqs = []
toexamine = list(entries[:])
while toexamine:
entry = toexamine.pop()
# tuples of (PriorityStructFile, element) for each
# matching element and the structfile that contains it
matching = []
for deps in self.entries.values():
el = deps.find("/%s[name='%s']" % (entry.tag,
entry.get("name")))
if el:
matching.append((deps, el))
if len(matching) > 1:
prio = [int(m[0].priority) for m in matching]
if prio.count(max(prio)) > 1:
raise PluginExecutionError(
"Deps: Found conflicting dependencies with same "
"priority for %s:%s for %s: %s" %
(entry.tag, entry.get("name"),
metadata.hostname, [m[0].name for m in matching]))
index = prio.index(max(prio))
matching = [matching[index]]
if not matching:
continue
for prq in matching[0][1].getchildren():
if prq not in prereqs and prq not in entries:
toexamine.append(prq)
prereqs.append(prq)
return prereqs
|