From e43040b084d66702efe1887a9d953b9154732512 Mon Sep 17 00:00:00 2001 From: "Chris St. Pierre" Date: Thu, 14 Jun 2012 20:08:47 -0400 Subject: made bcfg2-lint load lint plugins from server plugins where appropriate --- src/lib/Bcfg2/Server/Lint/Bundles.py | 54 --------------------- src/lib/Bcfg2/Server/Lint/Deltas.py | 25 ---------- src/lib/Bcfg2/Server/Lint/GroupPatterns.py | 35 -------------- src/lib/Bcfg2/Server/Lint/InfoXML.py | 6 ++- src/lib/Bcfg2/Server/Lint/Pkgmgr.py | 38 --------------- src/lib/Bcfg2/Server/Lint/TemplateHelper.py | 64 ------------------------- src/lib/Bcfg2/Server/Plugins/Bundler.py | 50 +++++++++++++++++++ src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py | 21 ++++++++ src/lib/Bcfg2/Server/Plugins/GroupPatterns.py | 32 +++++++++++++ src/lib/Bcfg2/Server/Plugins/Pkgmgr.py | 44 ++++++++++++++++- src/lib/Bcfg2/Server/Plugins/TemplateHelper.py | 66 ++++++++++++++++++++++++++ src/sbin/bcfg2-lint | 57 ++++++++++++++-------- 12 files changed, 252 insertions(+), 240 deletions(-) delete mode 100644 src/lib/Bcfg2/Server/Lint/Bundles.py delete mode 100644 src/lib/Bcfg2/Server/Lint/Deltas.py delete mode 100644 src/lib/Bcfg2/Server/Lint/GroupPatterns.py delete mode 100644 src/lib/Bcfg2/Server/Lint/Pkgmgr.py delete mode 100644 src/lib/Bcfg2/Server/Lint/TemplateHelper.py diff --git a/src/lib/Bcfg2/Server/Lint/Bundles.py b/src/lib/Bcfg2/Server/Lint/Bundles.py deleted file mode 100644 index e6b6307f2..000000000 --- a/src/lib/Bcfg2/Server/Lint/Bundles.py +++ /dev/null @@ -1,54 +0,0 @@ -import lxml.etree -import Bcfg2.Server.Lint - -class Bundles(Bcfg2.Server.Lint.ServerPlugin): - """ Perform various bundle checks """ - def Run(self): - """ run plugin """ - if 'Bundler' in self.core.plugins: - self.missing_bundles() - for bundle in self.core.plugins['Bundler'].entries.values(): - if self.HandlesFile(bundle.name): - if (not Bcfg2.Server.Plugins.Bundler.have_genshi or - type(bundle) is not - Bcfg2.Server.Plugins.SGenshi.SGenshiTemplateFile): - self.bundle_names(bundle) - - @classmethod - def Errors(cls): - return {"bundle-not-found":"error", - "inconsistent-bundle-name":"warning"} - - def missing_bundles(self): - """ find bundles listed in Metadata but not implemented in Bundler """ - if self.files is None: - # when given a list of files on stdin, this check is - # useless, so skip it - groupdata = self.metadata.groups_xml.xdata - ref_bundles = set([b.get("name") - for b in groupdata.findall("//Bundle")]) - - allbundles = self.core.plugins['Bundler'].entries.keys() - for bundle in ref_bundles: - xmlbundle = "%s.xml" % bundle - genshibundle = "%s.genshi" % bundle - if (xmlbundle not in allbundles and - genshibundle not in allbundles): - self.LintError("bundle-not-found", - "Bundle %s referenced, but does not exist" % - bundle) - - def bundle_names(self, bundle): - """ verify bundle name attribute matches filename """ - try: - xdata = lxml.etree.XML(bundle.data) - except AttributeError: - # genshi template - xdata = lxml.etree.parse(bundle.template.filepath).getroot() - - fname = bundle.name.split('Bundler/')[1].split('.')[0] - bname = xdata.get('name') - if fname != bname: - self.LintError("inconsistent-bundle-name", - "Inconsistent bundle name: filename is %s, bundle name is %s" % - (fname, bname)) diff --git a/src/lib/Bcfg2/Server/Lint/Deltas.py b/src/lib/Bcfg2/Server/Lint/Deltas.py deleted file mode 100644 index 114f2e348..000000000 --- a/src/lib/Bcfg2/Server/Lint/Deltas.py +++ /dev/null @@ -1,25 +0,0 @@ -import Bcfg2.Server.Lint -from Bcfg2.Server.Plugins.Cfg import CfgFilter - -class Deltas(Bcfg2.Server.Lint.ServerPlugin): - """ Warn about usage of .cat and .diff files """ - - def Run(self): - """ run plugin """ - if 'Cfg' in self.core.plugins: - cfg = self.core.plugins['Cfg'] - for basename, entry in list(cfg.entries.items()): - self.check_entry(basename, entry) - - @classmethod - def Errors(cls): - return {"cat-file-used":"warning", - "diff-file-used":"warning"} - - def check_entry(self, basename, entry): - for fname, processor in entry.entries.items(): - if self.HandlesFile(fname) and isinstance(processor, CfgFilter): - extension = fname.split(".")[-1] - self.LintError("%s-file-used" % extension, - "%s file used on %s: %s" % - (extension, basename, fname)) diff --git a/src/lib/Bcfg2/Server/Lint/GroupPatterns.py b/src/lib/Bcfg2/Server/Lint/GroupPatterns.py deleted file mode 100644 index 431ba4056..000000000 --- a/src/lib/Bcfg2/Server/Lint/GroupPatterns.py +++ /dev/null @@ -1,35 +0,0 @@ -import sys -import Bcfg2.Server.Lint -from Bcfg2.Server.Plugins.GroupPatterns import PatternMap - -class GroupPatterns(Bcfg2.Server.Lint.ServerPlugin): - """ Check Genshi templates for syntax errors """ - - def Run(self): - """ run plugin """ - if 'GroupPatterns' in self.core.plugins: - cfg = self.core.plugins['GroupPatterns'].config - for entry in cfg.xdata.xpath('//GroupPattern'): - groups = [g.text for g in entry.findall('Group')] - self.check(entry, groups, ptype='NamePattern') - self.check(entry, groups, ptype='NameRange') - - @classmethod - def Errors(cls): - return {"pattern-fails-to-initialize":"error"} - - def check(self, entry, groups, ptype="NamePattern"): - if ptype == "NamePattern": - pmap = lambda p: PatternMap(p, None, groups) - else: - pmap = lambda p: PatternMap(None, p, groups) - - for el in entry.findall(ptype): - pat = el.text - try: - pmap(pat) - except: - err = sys.exc_info()[1] - self.LintError("pattern-fails-to-initialize", - "Failed to initialize %s %s for %s: %s" % - (ptype, pat, entry.get('pattern'), err)) diff --git a/src/lib/Bcfg2/Server/Lint/InfoXML.py b/src/lib/Bcfg2/Server/Lint/InfoXML.py index db6aeea73..3884c1ed4 100644 --- a/src/lib/Bcfg2/Server/Lint/InfoXML.py +++ b/src/lib/Bcfg2/Server/Lint/InfoXML.py @@ -6,8 +6,10 @@ from Bcfg2.Server.Plugins.Cfg.CfgInfoXML import CfgInfoXML class InfoXML(Bcfg2.Server.Lint.ServerPlugin): """ ensure that all config files have an info.xml file""" def Run(self): - if 'Cfg' in self.core.plugins: - for filename, entryset in self.core.plugins['Cfg'].entries.items(): + for plugin in ['Cfg', 'TCheetah', 'TGenshi']: + if plugin not in self.core.plugins: + continue + for filename, entryset in self.core.plugins[plugin].entries.items(): infoxml_fname = os.path.join(entryset.path, "info.xml") if self.HandlesFile(infoxml_fname): found = False diff --git a/src/lib/Bcfg2/Server/Lint/Pkgmgr.py b/src/lib/Bcfg2/Server/Lint/Pkgmgr.py deleted file mode 100644 index ceb46238a..000000000 --- a/src/lib/Bcfg2/Server/Lint/Pkgmgr.py +++ /dev/null @@ -1,38 +0,0 @@ -import glob -import lxml.etree -import Bcfg2.Server.Lint - -class Pkgmgr(Bcfg2.Server.Lint.ServerlessPlugin): - """ find duplicate Pkgmgr entries with the same priority """ - def Run(self): - pset = set() - for pfile in glob.glob("%s/Pkgmgr/*.xml" % self.config['repo']): - if self.HandlesFile(pfile): - xdata = lxml.etree.parse(pfile).getroot() - # get priority, type, group - priority = xdata.get('priority') - ptype = xdata.get('type') - for pkg in xdata.xpath("//Package"): - if pkg.getparent().tag == 'Group': - grp = pkg.getparent().get('name') - if (type(grp) is not str and - grp.getparent().tag == 'Group'): - pgrp = grp.getparent().get('name') - else: - pgrp = 'none' - else: - grp = 'none' - pgrp = 'none' - ptuple = (pkg.get('name'), priority, ptype, grp, pgrp) - # check if package is already listed with same - # priority, type, grp - if ptuple in pset: - self.LintError("duplicate-package", - "Duplicate Package %s, priority:%s, type:%s" % - (pkg.get('name'), priority, ptype)) - else: - pset.add(ptuple) - - @classmethod - def Errors(cls): - return {"duplicate-packages":"error"} diff --git a/src/lib/Bcfg2/Server/Lint/TemplateHelper.py b/src/lib/Bcfg2/Server/Lint/TemplateHelper.py deleted file mode 100644 index be270a59c..000000000 --- a/src/lib/Bcfg2/Server/Lint/TemplateHelper.py +++ /dev/null @@ -1,64 +0,0 @@ -import sys -import imp -import glob -import Bcfg2.Server.Lint -from Bcfg2.Server.Plugins.TemplateHelper import HelperModule - -class TemplateHelper(Bcfg2.Server.Lint.ServerlessPlugin): - """ find duplicate Pkgmgr entries with the same priority """ - def __init__(self, *args, **kwargs): - Bcfg2.Server.Lint.ServerlessPlugin.__init__(self, *args, **kwargs) - hm = HelperModule("foo.py", None, None) - self.reserved_keywords = dir(hm) - - def Run(self): - for helper in glob.glob("%s/TemplateHelper/*.py" % self.config['repo']): - if not self.HandlesFile(helper): - continue - - match = HelperModule._module_name_re.search(helper) - if match: - module_name = match.group(1) - else: - module_name = helper - - try: - module = imp.load_source(module_name, helper) - except: - err = sys.exc_info()[1] - self.LintError("templatehelper-import-error", - "Failed to import %s: %s" % - (helper, err)) - continue - - if not hasattr(module, "__export__"): - self.LintError("templatehelper-no-export", - "%s has no __export__ list" % helper) - continue - elif not isinstance(module.__export__, list): - self.LintError("templatehelper-nonlist-export", - "__export__ is not a list in %s" % helper) - continue - - for sym in module.__export__: - if not hasattr(module, sym): - self.LintError("templatehelper-nonexistent-export", - "%s: exported symbol %s does not exist" % - (helper, sym)) - elif sym in self.reserved_keywords: - self.LintError("templatehelper-reserved-export", - "%s: exported symbol %s is reserved" % - (helper, sym)) - elif sym.startswith("_"): - self.LintError("templatehelper-underscore-export", - "%s: exported symbol %s starts with underscore" % - (helper, sym)) - - @classmethod - def Errors(cls): - return {"templatehelper-import-error":"error", - "templatehelper-no-export":"error", - "templatehelper-nonlist-export":"error", - "templatehelper-nonexistent-export":"error", - "templatehelper-reserved-export":"error", - "templatehelper-underscore-export":"warning"} diff --git a/src/lib/Bcfg2/Server/Plugins/Bundler.py b/src/lib/Bcfg2/Server/Plugins/Bundler.py index cbaa85089..6289439c6 100644 --- a/src/lib/Bcfg2/Server/Plugins/Bundler.py +++ b/src/lib/Bcfg2/Server/Plugins/Bundler.py @@ -8,6 +8,7 @@ import re import sys import Bcfg2.Server import Bcfg2.Server.Plugin +import Bcfg2.Server.Lint try: import genshi.template.base @@ -91,3 +92,52 @@ class Bundler(Bcfg2.Server.Plugin.Plugin, self.logger.error("Bundler: Unexpected bundler error for %s" % bundlename, exc_info=1) return bundleset + +class BundlerLint(Bcfg2.Server.Lint.ServerPlugin): + """ Perform various bundle checks """ + def Run(self): + """ run plugin """ + self.missing_bundles() + for bundle in self.core.plugins['Bundler'].entries.values(): + if (self.HandlesFile(bundle.name) and + (not have_genshi or type(bundle) is not SGenshiTemplateFile)): + self.bundle_names(bundle) + + @classmethod + def Errors(cls): + return {"bundle-not-found":"error", + "inconsistent-bundle-name":"warning"} + + def missing_bundles(self): + """ find bundles listed in Metadata but not implemented in Bundler """ + if self.files is None: + # when given a list of files on stdin, this check is + # useless, so skip it + groupdata = self.metadata.groups_xml.xdata + ref_bundles = set([b.get("name") + for b in groupdata.findall("//Bundle")]) + + allbundles = self.core.plugins['Bundler'].entries.keys() + for bundle in ref_bundles: + xmlbundle = "%s.xml" % bundle + genshibundle = "%s.genshi" % bundle + if (xmlbundle not in allbundles and + genshibundle not in allbundles): + self.LintError("bundle-not-found", + "Bundle %s referenced, but does not exist" % + bundle) + + def bundle_names(self, bundle): + """ verify bundle name attribute matches filename """ + try: + xdata = lxml.etree.XML(bundle.data) + except AttributeError: + # genshi template + xdata = lxml.etree.parse(bundle.template.filepath).getroot() + + fname = bundle.name.split('Bundler/')[1].split('.')[0] + bname = xdata.get('name') + if fname != bname: + self.LintError("inconsistent-bundle-name", + "Inconsistent bundle name: filename is %s, " + "bundle name is %s" % (fname, bname)) diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py index af6e1c7b1..15fd71644 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py @@ -11,6 +11,7 @@ import lxml.etree import Bcfg2.Options import Bcfg2.Server.Plugin from Bcfg2.Bcfg2Py3k import u_str +import Bcfg2.Server.Lint logger = logging.getLogger(__name__) @@ -412,3 +413,23 @@ class Cfg(Bcfg2.Server.Plugin.GroupSpool, return self.entries[new_entry.get('name')].write_update(specific, new_entry, log) + +class CfgLint(Bcfg2.Server.Lint.ServerPlugin): + def Run(self): + # about usage of .cat and .diff files + self.check_deltas() + + @classmethod + def Errors(cls): + return {"cat-file-used":"warning", + "diff-file-used":"warning"} + + def check_entry(self, basename, entry): + cfg = self.core.plugins['Cfg'] + for basename, entry in list(cfg.entries.items()): + for fname, processor in entry.entries.items(): + if self.HandlesFile(fname) and isinstance(processor, CfgFilter): + extension = fname.split(".")[-1] + self.LintError("%s-file-used" % extension, + "%s file used on %s: %s" % + (extension, basename, fname)) diff --git a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py index 58b4d4afb..e883143ec 100644 --- a/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py +++ b/src/lib/Bcfg2/Server/Plugins/GroupPatterns.py @@ -1,6 +1,8 @@ import re +import sys import logging import lxml.etree +import Bcfg2.Server.Lint import Bcfg2.Server.Plugin class PackedDigitRange(object): @@ -122,3 +124,33 @@ class GroupPatterns(Bcfg2.Server.Plugin.Plugin, def get_additional_groups(self, metadata): return self.config.process_patterns(metadata.hostname) + + +class GroupPatternsLint(Bcfg2.Server.Lint.ServerPlugin): + def Run(self): + """ run plugin """ + cfg = self.core.plugins['GroupPatterns'].config + for entry in cfg.xdata.xpath('//GroupPattern'): + groups = [g.text for g in entry.findall('Group')] + self.check(entry, groups, ptype='NamePattern') + self.check(entry, groups, ptype='NameRange') + + @classmethod + def Errors(cls): + return {"pattern-fails-to-initialize":"error"} + + def check(self, entry, groups, ptype="NamePattern"): + if ptype == "NamePattern": + pmap = lambda p: PatternMap(p, None, groups) + else: + pmap = lambda p: PatternMap(None, p, groups) + + for el in entry.findall(ptype): + pat = el.text + try: + pmap(pat) + except: + err = sys.exc_info()[1] + self.LintError("pattern-fails-to-initialize", + "Failed to initialize %s %s for %s: %s" % + (ptype, pat, entry.get('pattern'), err)) diff --git a/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py index 8d6d85ef2..fcf2045a7 100644 --- a/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py +++ b/src/lib/Bcfg2/Server/Plugins/Pkgmgr.py @@ -1,9 +1,12 @@ '''This module implements a package management scheme for all images''' -import logging import re +import glob +import logging +import lxml.etree import Bcfg2.Server.Plugin -import lxml +import Bcfg2.Server.Lint + try: set except NameError: @@ -169,3 +172,40 @@ class Pkgmgr(Bcfg2.Server.Plugin.PrioDir): def HandleEntry(self, entry, metadata): self.BindEntry(entry, metadata) + + +class PkgmgrLint(Bcfg2.Server.Lint.ServerlessPlugin): + """ find duplicate Pkgmgr entries with the same priority """ + def Run(self): + pset = set() + for pfile in glob.glob(os.path.join(self.config['repo'], 'Pkgmgr', + '*.xml')): + if self.HandlesFile(pfile): + xdata = lxml.etree.parse(pfile).getroot() + # get priority, type, group + priority = xdata.get('priority') + ptype = xdata.get('type') + for pkg in xdata.xpath("//Package"): + if pkg.getparent().tag == 'Group': + grp = pkg.getparent().get('name') + if (type(grp) is not str and + grp.getparent().tag == 'Group'): + pgrp = grp.getparent().get('name') + else: + pgrp = 'none' + else: + grp = 'none' + pgrp = 'none' + ptuple = (pkg.get('name'), priority, ptype, grp, pgrp) + # check if package is already listed with same + # priority, type, grp + if ptuple in pset: + self.LintError("duplicate-package", + "Duplicate Package %s, priority:%s, type:%s" % + (pkg.get('name'), priority, ptype)) + else: + pset.add(ptuple) + + @classmethod + def Errors(cls): + return {"duplicate-packages":"error"} diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py index 2c0ee03e0..3712506d6 100644 --- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py +++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py @@ -1,7 +1,9 @@ import re import imp import sys +import glob import logging +import Bcfg2.Server.Lint import Bcfg2.Server.Plugin logger = logging.getLogger(__name__) @@ -81,3 +83,67 @@ class TemplateHelper(Bcfg2.Server.Plugin.Plugin, def get_additional_data(self, metadata): return dict([(h._module_name, h) for h in list(self.helpers.entries.values())]) + + +class TemplateHelperLint(Bcfg2.Server.Lint.ServerlessPlugin): + """ find duplicate Pkgmgr entries with the same priority """ + def __init__(self, *args, **kwargs): + Bcfg2.Server.Lint.ServerlessPlugin.__init__(self, *args, **kwargs) + hm = HelperModule("foo.py", None, None) + self.reserved_keywords = dir(hm) + + def Run(self): + for helper in glob.glob(os.path.join(self.config['repo'], + "TemplateHelper", + "*.py")): + if not self.HandlesFile(helper): + continue + self.check_helper(helper) + + def check_helper(self, helper): + match = HelperModule._module_name_re.search(helper) + if match: + module_name = match.group(1) + else: + module_name = helper + + try: + module = imp.load_source(module_name, helper) + except: + err = sys.exc_info()[1] + self.LintError("templatehelper-import-error", + "Failed to import %s: %s" % + (helper, err)) + continue + + if not hasattr(module, "__export__"): + self.LintError("templatehelper-no-export", + "%s has no __export__ list" % helper) + continue + elif not isinstance(module.__export__, list): + self.LintError("templatehelper-nonlist-export", + "__export__ is not a list in %s" % helper) + continue + + for sym in module.__export__: + if not hasattr(module, sym): + self.LintError("templatehelper-nonexistent-export", + "%s: exported symbol %s does not exist" % + (helper, sym)) + elif sym in self.reserved_keywords: + self.LintError("templatehelper-reserved-export", + "%s: exported symbol %s is reserved" % + (helper, sym)) + elif sym.startswith("_"): + self.LintError("templatehelper-underscore-export", + "%s: exported symbol %s starts with underscore" % + (helper, sym)) + + @classmethod + def Errors(cls): + return {"templatehelper-import-error":"error", + "templatehelper-no-export":"error", + "templatehelper-nonlist-export":"error", + "templatehelper-nonexistent-export":"error", + "templatehelper-reserved-export":"error", + "templatehelper-underscore-export":"warning"} diff --git a/src/sbin/bcfg2-lint b/src/sbin/bcfg2-lint index 4b1fa8686..423c63ba3 100755 --- a/src/sbin/bcfg2-lint +++ b/src/sbin/bcfg2-lint @@ -68,11 +68,28 @@ def load_server(setup): core.fam.handle_events_in_interval(4) return core +def load_plugin(module, obj_name=None): + parts = module.split(".") + if obj_name is None: + obj_name = parts[-1] + + try: + mod = __import__(module) + except ImportError: + err = sys.exc_info()[1] + logger.error("Failed to load plugin %s: %s" % (obj_name, err)) + raise + + for p in parts[1:]: + mod = getattr(mod, p) + return getattr(mod, obj_name) + if __name__ == '__main__': optinfo = dict(config=Bcfg2.Options.LINT_CONFIG, showerrors=Bcfg2.Options.LINT_SHOW_ERRORS, stdin=Bcfg2.Options.LINT_FILES_ON_STDIN, - schema=Bcfg2.Options.SCHEMA_PATH) + schema=Bcfg2.Options.SCHEMA_PATH, + plugins=Bcfg2.Options.SERVER_PLUGINS) optinfo.update(Bcfg2.Options.CLI_COMMON_OPTIONS) optinfo.update(Bcfg2.Options.SERVER_COMMON_OPTIONS) setup = Bcfg2.Options.OptionParser(optinfo) @@ -89,36 +106,36 @@ if __name__ == '__main__': # get list of plugins to run if setup['args']: - allplugins = setup['args'] + plugin_list = setup['args'] elif "bcfg2-repo-validate" in sys.argv[0]: - allplugins = 'Duplicates,RequiredAttrs,Validate'.split(',') + plugin_list = 'Duplicates,RequiredAttrs,Validate'.split(',') else: try: - allplugins = config.get('lint', 'plugins').split(',') + plugin_list = config.get('lint', 'plugins').split(',') except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): - allplugins = Bcfg2.Server.Lint.__all__ + plugin_list = Bcfg2.Server.Lint.__all__ if setup['stdin']: files = [s.strip() for s in sys.stdin.readlines()] else: files = None - # load plugins - serverplugins = {} - serverlessplugins = {} - for plugin_name in allplugins: + # load plugins specified in the config first + allplugins = dict() + for plugin in plugin_list: + allplugins[plugin] = load_plugin("Bcfg2.Server.Lint." + plugin) + + # load lint plugins bundled with bcfg2-server plugins + for plugin in setup['plugins']: try: - mod = getattr(__import__("Bcfg2.Server.Lint.%s" % - (plugin_name)).Server.Lint, plugin_name) - except ImportError: - try: - mod = __import__(plugin_name) - except Exception: - err = sys.exc_info()[1] - logger.error("Failed to load plugin %s: %s" % (plugin_name, - err)) - raise SystemExit(1) - plugin = getattr(mod, plugin_name) + allplugins[plugin] = load_plugin("Bcfg2.Server.Plugins." + plugin, + obj_name=plugin + "Lint") + except AttributeError: + pass + + serverplugins = dict() + serverlessplugins = dict() + for plugin_name, plugin in allplugins.items(): if [c for c in inspect.getmro(plugin) if c == Bcfg2.Server.Lint.ServerPlugin]: serverplugins[plugin_name] = plugin -- cgit v1.2.3-1-g7c22