summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Lint/TemplateHelper.py
blob: 9d05516f18caa04dfccc203f45b32132e6ed5d53 (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
""" ``bcfg2-lint`` plugin for :ref:`TemplateHelper
<server-plugins-connectors-templatehelper>` """

import sys
import imp
from Bcfg2.Server.Lint import ServerPlugin
from Bcfg2.Server.Plugins.TemplateHelper import HelperModule, MODULE_RE, \
    safe_module_name


class TemplateHelper(ServerPlugin):
    """ ``bcfg2-lint`` plugin to ensure that all :ref:`TemplateHelper
    <server-plugins-connectors-templatehelper>` modules are valid.
    This can check for:

    * A TemplateHelper module that cannot be imported due to syntax or
      other compile-time errors;
    * A TemplateHelper module that does not have an ``__export__``
      attribute, or whose ``__export__`` is not a list;
    * Bogus symbols listed in ``__export__``, including symbols that
      don't exist, that are reserved, or that start with underscores.
    """
    __serverplugin__ = 'TemplateHelper'

    def __init__(self, *args, **kwargs):
        ServerPlugin.__init__(self, *args, **kwargs)
        # we instantiate a dummy helper to discover which keywords and
        # defaults are reserved
        dummy = HelperModule("foo.py")
        self.reserved_keywords = dir(dummy)
        self.reserved_defaults = dummy.reserved_defaults

    def Run(self):
        for helper in self.core.plugins['TemplateHelper'].entries.values():
            if self.HandlesFile(helper.name):
                self.check_helper(helper.name)

    def check_helper(self, helper):
        """ Check a single helper module.

        :param helper: The filename of the helper module
        :type helper: string
        """
        module_name = MODULE_RE.search(helper).group(1)

        try:
            module = imp.load_source(safe_module_name(module_name), helper)
        except:  # pylint: disable=W0702
            err = sys.exc_info()[1]
            self.LintError("templatehelper-import-error",
                           "Failed to import %s: %s" %
                           (helper, err))
            return

        if not hasattr(module, "__export__"):
            self.LintError("templatehelper-no-export",
                           "%s has no __export__ list" % helper)
            return
        elif not isinstance(module.__export__, list):
            self.LintError("templatehelper-nonlist-export",
                           "__export__ is not a list in %s" % helper)
            return

        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))
            if sym in getattr(module, "__default__", []):
                self.LintError("templatehelper-export-and-default",
                               "%s: %s is listed in both __default__ and "
                               "__export__" % (helper, sym))

        for sym in getattr(module, "__default__", []):
            if sym in self.reserved_defaults:
                self.LintError("templatehelper-reserved-default",
                               "%s: default symbol %s is reserved" %
                               (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-reserved-default": "error",
                "templatehelper-underscore-export": "warning",
                "templatehelper-export-and-default": "warning"}