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
|
""" ``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 """
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))
|