summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Deps.py
blob: 1872e68af38a7afc5e11e0dc8f7d6ff7528c280c (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
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