]> Frank Brehm's Git Trees - pixelpark/puppet-tools.git/commitdiff
Extending DpxPuppetConfig in dpx_puppettools.config for reading mail configuration...
authorFrank Brehm <frank.brehm@pixelpark.com>
Mon, 6 Feb 2023 12:41:13 +0000 (13:41 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Mon, 6 Feb 2023 12:41:13 +0000 (13:41 +0100)
lib/dpx_puppettools/config.py

index 513faaef50565b52fa3bfbdf1989952c66d4d105..1c62fe1506b7555906af9075631babe0fdf96bb8 100644 (file)
@@ -25,12 +25,12 @@ from fb_tools import MailAddress
 
 # Own modules
 
+from . import __version__ as GLOBAL_VERSION
 from .errors import PuppetToolsError
-
 from .xlate import XLATOR
 
 CONFIG_DIR = 'pixelpark'
-__version__ = '0.1.0'
+__version__ = '0.2.0'
 LOG = logging.getLogger(__name__)
 VALID_MAIL_METHODS = ('smtp', 'sendmail')
 DEFAULT_DOMAIN = 'pixelpark.com'
@@ -50,7 +50,8 @@ class DpxPuppetConfigError(PuppetToolsError, MultiConfigError):
 
 # =============================================================================
 class DpxPuppetConfig(BaseMultiConfig):
-    """Base class for reading and providing configuration."""
+    """Class for reading and providing configuration for all
+    application classes in this package."""
 
     default_mail_recipients = [
         'solution@pixelpark.com'
@@ -83,6 +84,8 @@ class DpxPuppetConfig(BaseMultiConfig):
     valid_mail_methods = VALID_MAIL_METHODS
     default_mail_method = 'smtp'
 
+    re_whitespace = re.compile(r'(?:[,;]+|\s*[,;]*\s+)+')
+
     # -------------------------------------------------------------------------
     def __init__(
         self, appname=None, verbose=0, version=__version__, base_dir=None,
@@ -98,17 +101,44 @@ class DpxPuppetConfig(BaseMultiConfig):
         self.mail_server = self.default_mail_server
         self.smtp_port = self.default_smtp_port
 
+        add_stems = []
+        if additional_stems:
+            if is_sequence(additional_stems):
+                for stem in additional_stems:
+                    add_stems.append(stem)
+            else:
+                add_stems.append(additional_stems)
+
+        for stem in ('mail', 'hooks'):
+            if stem not in add_stems:
+                add_stems.append(stem)
+
+        self._mail_cc_configured = False
+
         super(DpxPuppetConfig, self).__init__(
             appname=appname, verbose=verbose, version=version, base_dir=base_dir,
             append_appname_to_stems=append_appname_to_stems, config_dir=config_dir,
-            additional_stems=additional_stems, additional_cfgdirs=additional_cfgdirs,
+            additional_stems=add_stems, additional_cfgdirs=additional_cfgdirs,
             encoding=encoding, additional_config_file=additional_config_file,
             ensure_privacy=ensure_privacy, use_chardet=use_chardet,
             raise_on_error=raise_on_error, initialized=False)
 
+        self.xmailer = "{a} (Admin Tools version {v})".format(
+            a=self.appname, v=GLOBAL_VERSION)
+
         if initialized:
             self.initialized = True
 
+    # -----------------------------------------------------------
+    @property
+    def mail_cc_configured(self):
+        """Is the mail-CC array already configured?"""
+        return self._mail_cc_configured
+
+    @mail_cc_configured.setter
+    def mail_cc_configured(self, value):
+        self._mail_cc_configured = to_bool(value)
+
     # -------------------------------------------------------------------------
     def as_dict(self, short=True):
         """
@@ -123,21 +153,225 @@ class DpxPuppetConfig(BaseMultiConfig):
 
         res = super(DpxPuppetConfig, self).as_dict(short=short)
 
-        res['default_mail_recipients'] = self.default_mail_recipients
-        res['default_mail_cc'] = self.default_mail_cc
-        res['default_reply_to'] = self.default_reply_to
-        res['default_mail_server'] = self.default_mail_server
-        res['default_smtp_port'] = self.default_smtp_port
-        res['current_user_name'] = self.current_user_name
         res['current_user_gecos'] = self.current_user_gecos
-        res['default_mail_from'] = self.default_mail_from
+        res['current_user_name'] = self.current_user_name
         res['default_domain'] = self.default_domain
-        res['default_mail_from_complete'] = self.default_mail_from_complete
+        res['default_mail_cc'] = self.default_mail_cc
         res['default_mail_method'] = self.default_mail_method
+        res['default_mail_recipients'] = self.default_mail_recipients
+        res['default_mail_from'] = self.default_mail_from
+        res['default_mail_from_complete'] = self.default_mail_from_complete
+        res['default_mail_server'] = self.default_mail_server
+        res['default_reply_to'] = self.default_reply_to
+        res['default_smtp_port'] = self.default_smtp_port
+        res['mail_cc_configured'] = self.mail_cc_configured
         res['valid_mail_methods'] = self.valid_mail_methods
 
         return res
 
+    # -------------------------------------------------------------------------
+    def eval(self):
+
+        self.mail_recipients = []
+        self.mail_cc = []
+
+        super(DpxPuppetConfig, self).eval()
+
+        if not self.mail_recipients:
+            self.mail_recipients = copy.copy(self.default_mail_recipients)
+
+        if not self.mail_cc and not self.mail_cc_configured:
+            self.mail_cc = copy.copy(self.default_mail_cc)
+
+    # -------------------------------------------------------------------------
+    def eval_section(self, section_name):
+
+        super(DpxPuppetConfig, self).eval_section(section_name)
+        sn = section_name.lower()
+
+        if sn == 'mail':
+            section = self.cfg[section_name]
+            return self._eval_mail(section_name, section)
+
+    # -------------------------------------------------------------------------
+    def _eval_mail(self, section_name, section):
+
+        if self.verbose > 2:
+            msg = _("Evaluating config section {!r}:").format(section_name)
+            LOG.debug(msg + '\n' + pp(section))
+
+        self._eval_mail_from(section_name, section)
+        self._eval_mail_rcpt(section_name, section)
+        self._eval_mail_cc(section_name, section)
+        self._eval_mail_reply_to(section_name, section)
+        self._eval_mail_method(section_name, section)
+        self._eval_mail_server(section_name, section)
+        self._eval_smtp_port(section_name, section)
+
+    # -------------------------------------------------------------------------
+    def _split_mailaddress_tokens(self, value, what=None):
+
+        result = []
+
+        tokens = self.whitespace_re.split(value)
+        for token in tokens:
+            if MailAddress.valid_address(token):
+                result.append(token)
+            else:
+                msg = _("Found invalid {what} {addr!r} in configuration.")
+                LOG.error(msg.format(what=what, addr=token))
+
+        return result
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_from(self, section_name, section):
+
+        re_from = re.compile(r'^\s*(mail[_-]?)?from\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_from.search(key):
+                continue
+
+            val = section[key]
+
+            if is_sequence(val):
+                if not len(val):
+                    continue
+                val = val[0]
+
+            if MailAddress.valid_address(val):
+                self.mail_from = val
+            else:
+                msg = _("Found invalid {what} {addr!r} in configuration.")
+                LOG.error(msg.format(what=_("from address"), addr=val))
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_rcpt(self, section_name, section):
+
+        re_rcpt = re.compile(r'^\s*(mail[_-]?)?(recipients?|rcpt)\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_rcpt.search(key):
+                continue
+
+            val = section[key]
+            if not val:
+                continue
+            if is_sequence(val):
+                for v in val:
+                    result = self._split_mailaddress_tokens(v, _("recipient mail address"))
+                    if result:
+                        self.mail_recipients.expand(result)
+            else:
+                result = self._split_mailaddress_tokens(val, _("recipient mail address"))
+                if result:
+                    self.mail_recipients.expand(result)
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_cc(self, section_name, section):
+
+        re_cc = re.compile(r'^\s*(mail[_-]?)?cc\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+
+            self.mail_cc_configured = True
+            if not re_cc.search(key):
+                continue
+
+            val = section[key]
+            if not val:
+                continue
+            if is_sequence(val):
+                for v in val:
+                    result = self._split_mailaddress_tokens(v, _("cc mail address"))
+                    if result:
+                        self.mail_cc.expand(result)
+            else:
+                result = self._split_mailaddress_tokens(val, _("cc mail address"))
+                if result:
+                    self.mail_cc.expand(result)
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_reply_to(self, section_name, section):
+
+        re_reply = re.compile(r'^\s*(mail[_-]?)?reply([-_]?to)?\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_reply.search(key):
+                continue
+
+            val = section[key]
+
+            if is_sequence(val):
+                if not len(val):
+                    continue
+                val = val[0]
+
+            if MailAddress.valid_address(val):
+                self.reply_to = val
+            else:
+                msg = _("Found invalid {what} {addr!r} in configuration.")
+                LOG.error(msg.format(what=_("reply to address"), addr=val))
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_method(self, section_name, section):
+
+        re_method = re.compile(r'^\s*(mail[_-]?)?method\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_method.search(key):
+                continue
+
+            val = section[key].strip().lower()
+            if not val:
+                continue
+
+            if val not in self.valid_mail_methods:
+                msg = _("Found invalid mail method {!r} in configuration.")
+                LOG.error(msg.format(section[key]))
+                continue
+
+            self.mail_method = val
+
+    # -------------------------------------------------------------------------
+    def _eval_mail_server(self, section_name, section):
+
+        re_server = re.compile(r'^\s*(mail[_-]?)?server\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_server.search(key):
+                continue
+
+            val = section[key].strip().lower()
+            if not val:
+                continue
+
+            self.mail_server = val
+
+    # -------------------------------------------------------------------------
+    def _eval_smtp_port(self, section_name, section):
+
+        re_server = re.compile(r'^\s*(smtp[_-]?)?port\s*$', re.IGNORECASE)
+
+        for key in section.keys():
+            if not re_server.search(key):
+                continue
+
+            val = section[key]
+            try:
+                port = int(val)
+            except (ValueError, TypeError) as e:
+                msg = _("Value {!r} for SMTP port is invalid:").format(val)
+                msg += ' ' + str(e)
+                LOG.error(msg)
+                continue
+            if port <= 0 or port > MAX_PORT_NUMBER:
+                msg = _("Found invalid SMTP port number {} in configuration.").format(port)
+                LOG.error(msg)
+                continue
+
+            self.smtp_port = port
+
 
 # =============================================================================