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
|
"""This plugin provides automatic dependency handling."""
__revision__ = '$Revision$'
import lxml.etree
import Bcfg2.Server.Plugin
class DNode(Bcfg2.Server.Plugin.INode):
"""DNode provides supports for single predicate types for dependencies."""
raw = {'Group': "lambda m, e:'%(name)s' in m.groups and predicate(m, e)"}
containers = ['Group']
def __init__(self, data, idict, parent=None):
self.data = data
self.contents = {}
if parent == None:
self.predicate = lambda x, d: True
else:
predicate = parent.predicate
if data.tag in list(self.raw.keys()):
self.predicate = eval(self.raw[data.tag] %
{'name': data.get('name')},
{'predicate': predicate})
else:
raise Exception
mytype = self.__class__
self.children = []
for item in data.getchildren():
if item.tag in self.containers:
self.children.append(mytype(item, idict, self))
else:
data = [(child.tag, child.get('name'))
for child in item.getchildren()]
try:
self.contents[item.tag][item.get('name')] = data
except KeyError:
self.contents[item.tag] = {item.get('name'): data}
class DepXMLSrc(Bcfg2.Server.Plugin.XMLSrc):
__node__ = DNode
class Deps(Bcfg2.Server.Plugin.PrioDir,
Bcfg2.Server.Plugin.StructureValidator):
name = 'Deps'
__version__ = '$Id$'
__author__ = 'bcfg-dev@mcs.anl.gov'
__child__ = DepXMLSrc
# 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, datastore):
Bcfg2.Server.Plugin.PrioDir.__init__(self, core, datastore)
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)
gdata = list(metadata.groups)
gdata.sort()
gdata = tuple(gdata)
# Check to see if we have cached the prereqs already
if (entries, gdata) in self.cache:
prereqs = self.cache[(entries, gdata)]
else:
prereqs = self.calculate_prereqs(metadata, entries)
self.cache[(entries, gdata)] = prereqs
newstruct = lxml.etree.Element("Independent")
for tag, name in prereqs:
try:
lxml.etree.SubElement(newstruct, tag, name=name)
except:
self.logger.error("Failed to add dep entry for %s:%s" % (tag, name))
structures.append(newstruct)
def calculate_prereqs(self, metadata, entries):
"""Calculate the prerequisites defined in Deps for the passed
set of entries.
"""
prereqs = []
[src.Cache(metadata) for src in self.entries.values()]
toexamine = list(entries[:])
while toexamine:
entry = toexamine.pop()
matching = [src for src in list(self.entries.values())
if src.cache and entry[0] in src.cache[1]
and entry[1] in src.cache[1][entry[0]]]
if len(matching) > 1:
prio = [int(src.priority) for src in matching]
if prio.count(max(prio)) > 1:
self.logger.error("Found conflicting %s sources with same priority for %s, pkg %s" %
(entry[0].lower(), metadata.hostname, entry[1]))
raise Bcfg2.Server.Plugin.PluginExecutionError
index = prio.index(max(prio))
matching = [matching[index]]
elif len(matching) == 1:
for prq in matching[0].cache[1][entry[0]][entry[1]]:
# XML comments seem to show up in the cache as a
# tuple with item 0 being callable. The logic
# below filters them out. Would be better to
# exclude them when we load the cache in the first
# place.
if prq not in prereqs and prq not in entries and not callable(prq[0]):
toexamine.append(prq)
prereqs.append(prq)
else:
continue
return prereqs
|