summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Lint/Cfg.py
blob: 13b04a6b878665e8e540be257201015375f0f352 (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
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
""" ``bcfg2-lint`` plugin for :ref:`Cfg
<server-plugins-generators-cfg>` """

import os
import Bcfg2.Options
from fnmatch import fnmatch
from Bcfg2.Server.Lint import ServerPlugin
from Bcfg2.Server.Plugins.Cfg import CfgGenerator


class Cfg(ServerPlugin):
    """ warn about Cfg issues """
    __serverplugin__ = 'Cfg'

    def Run(self):
        for basename, entry in list(self.core.plugins['Cfg'].entries.items()):
            self.check_pubkey(basename, entry)
        self.check_missing_files()
        self.check_conflicting_handlers()

    @classmethod
    def Errors(cls):
        return {"no-pubkey-xml": "warning",
                "unknown-cfg-files": "error",
                "extra-cfg-files": "error",
                "multiple-global-handlers": "error"}

    def check_conflicting_handlers(self):
        """ Check that a single entryset doesn't have multiple
        non-specific (i.e., 'all') handlers. """
        cfg = self.core.plugins['Cfg']
        for eset in cfg.entries.values():
            alls = [e for e in eset.entries.values()
                    if (e.specific.all and
                        issubclass(e.__class__, CfgGenerator))]
            if len(alls) > 1:
                self.LintError("multiple-global-handlers",
                               "%s has multiple global handlers: %s" %
                               (eset.path, ", ".join(os.path.basename(e.name)
                                                     for e in alls)))

    def check_pubkey(self, basename, entry):
        """ check that privkey.xml files have corresponding pubkey.xml
        files """
        if "privkey.xml" not in entry.entries:
            return
        privkey = entry.entries["privkey.xml"]
        if not self.HandlesFile(privkey.name):
            return

        pubkey = basename + ".pub"
        if pubkey not in self.core.plugins['Cfg'].entries:
            self.LintError("no-pubkey-xml",
                           "%s has no corresponding pubkey.xml at %s" %
                           (basename, pubkey))
        else:
            pubset = self.core.plugins['Cfg'].entries[pubkey]
            if "pubkey.xml" not in pubset.entries:
                self.LintError("no-pubkey-xml",
                               "%s has no corresponding pubkey.xml at %s" %
                               (basename, pubkey))

    def _list_path_components(self, path):
        """ Get a list of all components of a path.  E.g.,
        ``self._list_path_components("/foo/bar/foobaz")`` would return
        ``["foo", "bar", "foo", "baz"]``.  The list is not guaranteed
        to be in order."""
        rv = []
        remaining, component = os.path.split(path)
        while component != '':
            rv.append(component)
            remaining, component = os.path.split(remaining)
        return rv

    def check_missing_files(self):
        """ check that all files on the filesystem are known to Cfg """
        cfg = self.core.plugins['Cfg']

        # first, collect ignore patterns from handlers
        ignore = set()
        for hdlr in Bcfg2.Options.setup.cfg_handlers:
            ignore.update(hdlr.__ignore__)

        # next, get a list of all non-ignored files on the filesystem
        all_files = set()
        for root, _, files in os.walk(cfg.data):
            for fname in files:
                fpath = os.path.join(root, fname)
                # check against the handler ignore patterns and the
                # global FAM ignore list
                if (not any(fname.endswith("." + i) for i in ignore) and
                    not any(fnmatch(fpath, p)
                            for p in Bcfg2.Options.setup.ignore_files) and
                    not any(fnmatch(c, p)
                            for p in Bcfg2.Options.setup.ignore_files
                            for c in self._list_path_components(fpath))):
                    all_files.add(fpath)

        # next, get a list of all files known to Cfg
        cfg_files = set()
        for root, eset in cfg.entries.items():
            cfg_files.update(os.path.join(cfg.data, root.lstrip("/"), fname)
                             for fname in eset.entries.keys())

        # finally, compare the two
        unknown_files = all_files - cfg_files
        extra_files = cfg_files - all_files
        if unknown_files:
            self.LintError(
                "unknown-cfg-files",
                "Files on the filesystem could not be understood by Cfg: %s" %
                "; ".join(unknown_files))
        if extra_files:
            self.LintError(
                "extra-cfg-files",
                "Cfg has entries for files that do not exist on the "
                "filesystem: %s\nThis is probably a bug." %
                "; ".join(extra_files))