summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lib/Server/Lint/Bundles.py13
-rw-r--r--src/lib/Server/Lint/Comments.py14
-rw-r--r--src/lib/Server/Lint/Duplicates.py9
-rw-r--r--src/lib/Server/Lint/InfoXML.py16
-rw-r--r--src/lib/Server/Lint/Pkgmgr.py5
-rw-r--r--src/lib/Server/Lint/RequiredAttrs.py6
-rw-r--r--src/lib/Server/Lint/Validate.py30
-rw-r--r--src/lib/Server/Lint/__init__.py115
-rwxr-xr-xsrc/sbin/bcfg2-lint85
9 files changed, 197 insertions, 96 deletions
diff --git a/src/lib/Server/Lint/Bundles.py b/src/lib/Server/Lint/Bundles.py
index 417f76c2d..e90159f7c 100644
--- a/src/lib/Server/Lint/Bundles.py
+++ b/src/lib/Server/Lint/Bundles.py
@@ -33,7 +33,8 @@ class Bundles(Bcfg2.Server.Lint.ServerPlugin):
genshibundle = "%s.genshi" % bundle
if (xmlbundle not in allbundles and
genshibundle not in allbundles):
- self.LintError("Bundle %s referenced, but does not exist" %
+ self.LintError("bundle-not-found",
+ "Bundle %s referenced, but does not exist" %
bundle)
def bundle_names(self, bundle):
@@ -47,8 +48,9 @@ class Bundles(Bcfg2.Server.Lint.ServerPlugin):
fname = bundle.name.split('Bundler/')[1].split('.')[0]
bname = xdata.get('name')
if fname != bname:
- self.LintWarning("Inconsistent bundle name: filename is %s, bundle name is %s" %
- (fname, bname))
+ self.LintError("inconsistent-bundle-name",
+ "Inconsistent bundle name: filename is %s, bundle name is %s" %
+ (fname, bname))
def sgenshi_groups(self, bundle):
""" ensure that Genshi Bundles do not include <Group> tags,
@@ -57,5 +59,6 @@ class Bundles(Bcfg2.Server.Lint.ServerPlugin):
groups = [self.RenderXML(g)
for g in xdata.getroottree().findall("//Group")]
if groups:
- self.LintWarning("<Group> tag is not allowed in SGenshi Bundle:\n%s" %
- "\n".join(groups))
+ self.LintError("group-tag-not-allowed",
+ "<Group> tag is not allowed in SGenshi Bundle:\n%s" %
+ "\n".join(groups))
diff --git a/src/lib/Server/Lint/Comments.py b/src/lib/Server/Lint/Comments.py
index 8c83545b3..8e86cc564 100644
--- a/src/lib/Server/Lint/Comments.py
+++ b/src/lib/Server/Lint/Comments.py
@@ -143,12 +143,14 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
unexpanded = [keyword for (keyword, status) in found.items()
if status is None]
if unexpanded:
- self.LintWarning("%s: Required keywords(s) found but not expanded: %s" %
- (filename, ", ".join(unexpanded)))
+ self.LintError("unexpanded-keywords",
+ "%s: Required keywords(s) found but not expanded: %s" %
+ (filename, ", ".join(unexpanded)))
missing = [keyword for (keyword, status) in found.items()
if status is False]
if missing:
- self.LintError("%s: Required keywords(s) not found: $%s$" %
+ self.LintError("keywords-not-found",
+ "%s: Required keywords(s) not found: $%s$" %
(filename, "$, $".join(missing)))
# next, check for required comments. found is just
@@ -163,7 +165,8 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
missing = [comment for (comment, status) in found.items()
if status is False]
if missing:
- self.LintError("%s: Required comments(s) not found: %s" %
+ self.LintError("comments-not-found",
+ "%s: Required comments(s) not found: %s" %
(filename, ", ".join(missing)))
def has_all_xincludes(self, mfile):
@@ -177,7 +180,8 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
xdata = lxml.etree.parse(path)
for el in xdata.findall('./{http://www.w3.org/2001/XInclude}include'):
if not self.has_all_xincludes(el.get('href')):
- self.LintWarning("Broken XInclude chain: could not include %s" % path)
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: could not include %s" % path)
return False
return True
diff --git a/src/lib/Server/Lint/Duplicates.py b/src/lib/Server/Lint/Duplicates.py
index c8b542025..517f0dd7b 100644
--- a/src/lib/Server/Lint/Duplicates.py
+++ b/src/lib/Server/Lint/Duplicates.py
@@ -49,7 +49,8 @@ class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
if el.get('name') not in seen:
seen[el.get('name')] = el
else:
- self.LintError("Duplicate %s '%s':\n%s\n%s" %
+ self.LintError("duplicate-%s" % etype,
+ "Duplicate %s '%s':\n%s\n%s" %
(etype, el.get('name'),
self.RenderXML(seen[el.get('name')]),
self.RenderXML(el)))
@@ -59,7 +60,8 @@ class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
default_groups = [g for g in self.groups_xdata.findall('.//Group')
if g.get('default') == 'true']
if len(default_groups) > 1:
- self.LintError("Multiple default groups defined: %s" %
+ self.LintError("multiple-default-groups",
+ "Multiple default groups defined: %s" %
",".join(default_groups))
def has_all_xincludes(self, mfile):
@@ -73,7 +75,8 @@ class Duplicates(Bcfg2.Server.Lint.ServerPlugin):
xdata = lxml.etree.parse(path)
for el in xdata.findall('./{http://www.w3.org/2001/XInclude}include'):
if not self.has_all_xincludes(el.get('href')):
- self.LintWarning("Broken XInclude chain: could not include %s" % path)
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: could not include %s" % path)
return False
return True
diff --git a/src/lib/Server/Lint/InfoXML.py b/src/lib/Server/Lint/InfoXML.py
index 25f609902..7725ad748 100644
--- a/src/lib/Server/Lint/InfoXML.py
+++ b/src/lib/Server/Lint/InfoXML.py
@@ -14,9 +14,9 @@ class InfoXML(Bcfg2.Server.Lint.ServerPlugin):
if (hasattr(entryset, "infoxml") and
entryset.infoxml is not None):
self.check_infoxml(entryset.infoxml.pnode.data)
- elif ("require" in self.config and
- self.config["require"].lower != "false"):
- self.LintError("No info.xml found for %s" % filename)
+ else:
+ self.LintError("no-infoxml",
+ "No info.xml found for %s" % filename)
def check_infoxml(self, xdata):
for info in xdata.getroottree().findall("//Info"):
@@ -26,18 +26,18 @@ class InfoXML(Bcfg2.Server.Lint.ServerPlugin):
missing = [attr for attr in required if info.get(attr) is None]
if missing:
- self.LintError("Required attribute(s) %s not found in %s:%s" %
+ self.LintError("required-infoxml-attrs-missing",
+ "Required attribute(s) %s not found in %s:%s" %
(",".join(missing), infoxml_fname,
self.RenderXML(info)))
- if ("require_paranoid" in self.config and
- self.config["require_paranoid"].lower() == "true" and
- (Bcfg2.Options.MDATA_PARANOID.value and
+ if ((Bcfg2.Options.MDATA_PARANOID.value and
info.get("paranoid") is not None and
info.get("paranoid").lower() == "false") or
(not Bcfg2.Options.MDATA_PARANOID.value and
(info.get("paranoid") is None or
info.get("paranoid").lower() != "true"))):
- self.LintError("Paranoid must be true in %s:%s" %
+ self.LintError("paranoid-false",
+ "Paranoid must be true in %s:%s" %
(infoxml_fname, self.RenderXML(info)))
diff --git a/src/lib/Server/Lint/Pkgmgr.py b/src/lib/Server/Lint/Pkgmgr.py
index f2eb2a5f6..39c601617 100644
--- a/src/lib/Server/Lint/Pkgmgr.py
+++ b/src/lib/Server/Lint/Pkgmgr.py
@@ -6,7 +6,7 @@ class Pkgmgr(Bcfg2.Server.Lint.ServerPlugin):
@Bcfg2.Server.Lint.returnErrors
def Run(self):
if 'Pkgmgr' not in self.core.plugins:
- self.LintWarning("Pkgmgr server plugin is not enabled, skipping Pkgmgr lint checks")
+ self.logger.info("Pkgmgr server plugin is not enabled, skipping Pkgmgr lint checks")
return
pset = set()
@@ -31,7 +31,8 @@ class Pkgmgr(Bcfg2.Server.Lint.ServerPlugin):
# check if package is already listed with same
# priority, type, grp
if ptuple in pset:
- self.LintWarning("Duplicate Package %s, priority:%s, type:%s" %
+ self.LintError("duplicate-package",
+ "Duplicate Package %s, priority:%s, type:%s" %
(pkg.get('name'), priority, ptype))
else:
pset.add(ptuple)
diff --git a/src/lib/Server/Lint/RequiredAttrs.py b/src/lib/Server/Lint/RequiredAttrs.py
index 70ce4fe0a..cbb4395c4 100644
--- a/src/lib/Server/Lint/RequiredAttrs.py
+++ b/src/lib/Server/Lint/RequiredAttrs.py
@@ -53,7 +53,8 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
try:
required_attrs = set(self.required_attrs[pathtype] + ['type'])
except KeyError:
- self.LintError("Unknown path type %s: %s" %
+ self.LintError("unknown-path-type",
+ "Unknown path type %s: %s" %
(pathtype, self.RenderXML(entry)))
if 'dev_type' in required_attrs:
@@ -62,7 +63,8 @@ class RequiredAttrs(Bcfg2.Server.Lint.ServerPlugin):
# check if major/minor are specified
required_attrs |= set(['major', 'minor'])
if not pathset.issuperset(required_attrs):
- self.LintError("The required attributes %s are missing for %s %sin %s:\n%s" %
+ self.LintError("required-attrs-missing",
+ "The required attributes %s are missing for %s %sin %s:\n%s" %
(",".join([attr
for attr in
required_attrs.difference(pathset)]),
diff --git a/src/lib/Server/Lint/Validate.py b/src/lib/Server/Lint/Validate.py
index bb5af93f4..c7a77a4fb 100644
--- a/src/lib/Server/Lint/Validate.py
+++ b/src/lib/Server/Lint/Validate.py
@@ -44,8 +44,9 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
schema = lxml.etree.XMLSchema(lxml.etree.parse(schemaname %
schemadir))
except:
- self.LintWarning("Failed to process schema %s",
- schemaname % schemadir)
+ self.LintError("schema-failed-to-parse",
+ "Failed to process schema %s",
+ schemaname % schemadir)
continue
for filename in filelist:
self.validate(filename, schemaname % schemadir,
@@ -55,19 +56,13 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
def check_properties(self):
""" check Properties files against their schemas """
- alert = self.logger.debug
- if "properties_schema" in self.config:
- if self.config['properties_schema'].lower().startswith('warn'):
- alert = self.LintWarning
- elif self.config['properties_schema'].lower().startswith('require'):
- alert = self.LintError
-
for filename in self.filelists['props']:
schemafile = "%s.xsd" % os.path.splitext(filename)[0]
if os.path.exists(schemafile):
self.validate(filename, schemafile)
else:
- alert("No schema found for %s" % filename)
+ self.LintError("properties-schema-not-found",
+ "No schema found for %s" % filename)
def validate(self, filename, schemafile, schema=None):
"""validate a file against the given lxml.etree.Schema.
@@ -77,19 +72,22 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
try:
schema = lxml.etree.XMLSchema(lxml.etree.parse(schemafile))
except:
- self.LintWarning("Failed to process schema %s" % schemafile)
+ self.LintError("schema-failed-to-parse",
+ "Failed to process schema %s" % schemafile)
return False
try:
datafile = lxml.etree.parse(filename)
except SyntaxError:
lint = Popen(["xmllint", filename], stdout=PIPE, stderr=STDOUT)
- self.LintError("%s fails to parse:\n%s" % (filename,
+ self.LintError("xml-failed-to-parse",
+ "%s fails to parse:\n%s" % (filename,
lint.communicate()[0]))
lint.wait()
return False
except IOError:
- self.LintError("Failed to open file %s" % filename)
+ self.LintError("xml-failed-to-read",
+ "Failed to open file %s" % filename)
return False
if not schema.validate(datafile):
@@ -100,7 +98,8 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
lint = Popen(cmd, stdout=PIPE, stderr=STDOUT)
output = lint.communicate()[0]
if lint.wait():
- self.LintError("%s fails to verify:\n%s" % (filename, output))
+ self.LintError("xml-failed-to-verify",
+ "%s fails to verify:\n%s" % (filename, output))
return False
return True
@@ -141,7 +140,8 @@ class Validate(Bcfg2.Server.Lint.ServerlessPlugin):
for fname in all_metadata:
if (fname not in self.filelists['metadata:groups'] and
fname not in self.filelists['metadata:clients']):
- self.LintWarning("Broken XInclude chain: Could not determine file type of %s" % fname)
+ self.LintError("broken-xinclude-chain",
+ "Broken XInclude chain: Could not determine file type of %s" % fname)
def get_metadata_list(self, mtype):
""" get all metadata files for the specified type (clients or
diff --git a/src/lib/Server/Lint/__init__.py b/src/lib/Server/Lint/__init__.py
index 4e6d03fb5..3b89d1f9e 100644
--- a/src/lib/Server/Lint/__init__.py
+++ b/src/lib/Server/Lint/__init__.py
@@ -16,21 +16,19 @@ import Bcfg2.Logger
def returnErrors(fn):
""" Decorator for Run method that returns error counts """
- def run(self, *args, **kwargs):
- fn(self, *args, **kwargs)
- return (self.error_count, self.warning_count)
-
- return run
+ return fn
class Plugin (object):
""" base class for ServerlessPlugin and ServerPlugin """
- def __init__(self, config, files=None):
+
+ def __init__(self, config, errorhandler=None, files=None):
self.files = files
- self.error_count = 0
- self.warning_count = 0
self.config = config
- Bcfg2.Logger.setup_logging('bcfg2-info', to_syslog=False)
self.logger = logging.getLogger('bcfg2-lint')
+ if errorhandler is None:
+ self.errorHandler = ErrorHandler()
+ else:
+ self.errorHandler = errorhandler
def Run(self):
""" run the plugin. must be overloaded by child classes """
@@ -45,21 +43,10 @@ class Plugin (object):
os.path.abspath(fname) in self.files or
os.path.abspath(os.path.join(self.config['repo'],
fname)) in self.files)
-
- def LintError(self, msg):
- """ log an error condition """
- self.error_count += 1
- lines = msg.splitlines()
- self.logger.error("ERROR: %s" % lines.pop())
- [self.logger.error(" %s" % l) for l in lines]
-
- def LintWarning(self, msg):
- """ log a warning condition """
- self.warning_count += 1
- lines = msg.splitlines()
- self.logger.warning("WARNING: %s" % lines.pop())
- [self.logger.warning(" %s" % l) for l in lines]
+ def LintError(self, err, msg):
+ self.errorHandler.dispatch(err, msg)
+
def RenderXML(self, element):
"""render an XML element for error output -- line number
prefixed, no children"""
@@ -74,17 +61,95 @@ class Plugin (object):
xml = lxml.etree.tostring(element).strip()
return " line %s: %s" % (element.sourceline, xml)
+
+class ErrorHandler (object):
+ # how to handle different errors by default
+ _errors = {"no-infoxml":"warning",
+ "paranoid-false":"warning",
+ "bundle-not-found":"error",
+ "inconsistent-bundle-name":"warning",
+ "group-tag-not-allowed":"error",
+ "unexpanded-keywords":"warning",
+ "keywords-not-found":"warning",
+ "comments-not-found":"warning",
+ "broken-xinclude-chain":"warning",
+ "duplicate-client":"error",
+ "duplicate-group":"error",
+ "duplicate-package":"error",
+ "multiple-default-groups":"error",
+ "required-infoxml-attrs-missing":"error",
+ "unknown-path-type":"error",
+ "required-attrs-missing":"error",
+ "schema-failed-to-parse":"warning",
+ "properties-schema-not-found":"warning",
+ "xml-failed-to-parse":"error",
+ "xml-failed-to-read":"error",
+ "xml-failed-to-verify":"error",}
+
+ def __init__(self, config=None):
+ self.errors = 0
+ self.warnings = 0
+
+ self.logger = logging.getLogger('bcfg2-lint')
+
+ self._handlers = {}
+ if config is not None:
+ for err, action in config.items():
+ if "warn" in action:
+ self._handlers[err] = self.warn
+ elif "err" in action:
+ self._handlers[err] = self.error
+ else:
+ self._handlers[err] = self.debug
+
+ for err, action in self._errors.items():
+ if err not in self._handlers:
+ if "warn" in action:
+ self._handlers[err] = self.warn
+ elif "err" in action:
+ self._handlers[err] = self.error
+ else:
+ self._handlers[err] = self.debug
+
+ def dispatch(self, err, msg):
+ if err in self._handlers:
+ self._handlers[err](msg)
+ self.logger.debug(" (%s)" % err)
+ else:
+ self.logger.info("Unknown error %s" % err)
+
+ def error(self, msg):
+ """ log an error condition """
+ self.errors += 1
+ lines = msg.splitlines()
+ self.logger.error("ERROR: %s" % lines.pop())
+ [self.logger.error(" %s" % l) for l in lines]
+
+ def warn(self, msg):
+ """ log a warning condition """
+ self.warnings += 1
+ lines = msg.splitlines()
+ self.logger.warning("WARNING: %s" % lines.pop())
+ [self.logger.warning(" %s" % l) for l in lines]
+
+ def debug(self, msg):
+ """ log a silent/debug condition """
+ lines = msg.splitlines()
+ [self.logger.debug("%s" % l) for l in lines]
+
+
class ServerlessPlugin (Plugin):
""" base class for plugins that are run before the server starts
up (i.e., plugins that check things that may prevent the server
from starting up) """
pass
+
class ServerPlugin (Plugin):
""" base class for plugins that check things that require the
running Bcfg2 server """
- def __init__(self, lintCore, config, files=None):
- Plugin.__init__(self, config, files=files)
+ def __init__(self, lintCore, config, **kwargs):
+ Plugin.__init__(self, config, **kwargs)
self.core = lintCore
self.logger = self.core.logger
self.metadata = self.core.metadata
diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint
index 18632e316..6bc34433e 100755
--- a/src/sbin/bcfg2-lint
+++ b/src/sbin/bcfg2-lint
@@ -27,33 +27,28 @@ class Parser(ConfigParser.ConfigParser):
except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
return default
-def run_serverless_plugins(plugins, config=None, setup=None):
+def run_serverless_plugins(plugins, config=None, setup=None, errorhandler=None):
logger.debug("Running serverless plugins")
- errors = (0, 0)
for plugin_name, plugin in list(plugins.items()):
- plugin_errors = run_plugin(plugin, plugin_name,
- setup=setup, config=config, files=files)
- errors = [errors[n] + plugin_errors[n]
- for n in range(0, len(errors))]
- return errors
+ run_plugin(plugin, plugin_name, errorhandler=errorhandler,
+ setup=setup, config=config, files=files)
-def run_server_plugins(plugins, config=None, setup=None):
+def run_server_plugins(plugins, config=None, setup=None, errorhandler=None):
core = load_server(setup)
logger.debug("Running server plugins")
- errors = (0, 0)
for plugin_name, plugin in list(plugins.items()):
- plugin_errors = run_plugin(plugin, plugin_name, args=[core],
- setup=setup, config=config, files=files)
- errors = [errors[n] + plugin_errors[n]
- for n in range(0, len(errors))]
- return errors
-
-def run_plugin(plugin, plugin_name, setup=None, args=None, config=None,
- files=None):
+ run_plugin(plugin, plugin_name, args=[core], errorhandler=errorhandler,
+ setup=setup, config=config, files=files)
+
+def run_plugin(plugin, plugin_name, setup=None, errorhandler=None,
+ args=None, config=None, files=None):
logger.debug(" Running %s" % plugin_name)
if args is None:
args = []
+ if errorhandler is None:
+ errorhandler = get_errorhandler(config)
+
if config is not None and config.has_section(plugin_name):
args.append(dict(config.items(plugin_name), **setup))
else:
@@ -62,10 +57,18 @@ def run_plugin(plugin, plugin_name, setup=None, args=None, config=None,
# older versions of python do not support mixing *-magic and
# non-*-magic (e.g., "plugin(*args, files=files)", so we do this
# all with *-magic
- kwargs = dict(files=files)
+ kwargs = dict(files=files, errorhandler=errorhandler)
return plugin(*args, **kwargs).Run()
+def get_errorhandler(config):
+ """ get a Bcfg2.Server.Lint.ErrorHandler object """
+ if config.has_section("errors"):
+ conf = dict(config.items("errors"))
+ else:
+ conf = None
+ return Bcfg2.Server.Lint.ErrorHandler(config=conf)
+
def load_server(setup):
""" load server """
core = Bcfg2.Server.Core.Core(setup['repo'], setup['plugins'],
@@ -104,7 +107,10 @@ if __name__ == '__main__':
'/etc/bcfg2-lint.conf',
cmd='--lint-config',
odesc='<conffile>',
- long_arg = True),
+ long_arg=True),
+ 'showerrors': Bcfg2.Options.Option('Show error handling', False,
+ cmd='--list-errors',
+ long_arg=True),
})
setup = Bcfg2.Options.OptionParser(optinfo)
setup.parse(sys.argv[1:])
@@ -117,6 +123,21 @@ if __name__ == '__main__':
config = Parser()
config.read(setup['config'])
+ if setup['showerrors']:
+ if config.has_section("errors"):
+ econf = dict(config.items("errors"))
+ else:
+ econf = dict()
+
+ print("%-35s %-35s" % ("Error name", "Handler (Default)"))
+ for err, default in Bcfg2.Server.Lint.ErrorHandler._errors.items():
+ if err in econf and econf[err] != default:
+ handler = "%s (%s)" % (econf[err], default)
+ else:
+ handler = default
+ print("%-35s %-35s" % (err, handler))
+ raise SystemExit(0)
+
# get list of plugins to run
if setup['args']:
allplugins = setup['args']
@@ -153,19 +174,21 @@ if __name__ == '__main__':
else:
serverlessplugins[plugin_name] = plugin
- # errors is a tuple of (errors, warnings)
- errors = run_serverless_plugins(serverlessplugins,
- config=config, setup=setup)
+ errorhandler = get_errorhandler(config)
+
+ run_serverless_plugins(serverlessplugins,
+ errorhandler=errorhandler,
+ config=config, setup=setup)
if serverplugins:
- perrors = run_server_plugins(serverplugins, config=config, setup=setup)
- errors = [errors[n] + perrors[n] for n in range(0, len(errors))]
-
- if errors[0] or errors[1] or setup['verbose']:
- print("%d errors" % errors[0])
- print("%d warnings" % errors[1])
-
- if errors[0]:
+ run_server_plugins(serverplugins, errorhandler=errorhandler,
+ config=config, setup=setup)
+
+ if errorhandler.errors or errorhandler.warnings or setup['verbose']:
+ print("%d errors" % errorhandler.errors)
+ print("%d warnings" % errorhandler.warnings)
+
+ if errorhandler.errors:
raise SystemExit(2)
- elif errors[1]:
+ elif errorhandler.warnings:
raise SystemExit(3)