]> Frank Brehm's Git Trees - pixelpark/admin-tools.git/commitdiff
Adding properties and evaluating configuration
authorFrank Brehm <frank.brehm@pixelpark.com>
Thu, 9 Nov 2017 16:36:33 +0000 (17:36 +0100)
committerFrank Brehm <frank.brehm@pixelpark.com>
Thu, 9 Nov 2017 16:36:33 +0000 (17:36 +0100)
pp_lib/deploy_zones_from_pdns.py

index 6dfe1a3061ad485521c0ca1a2b2a3ded19d1096d..b06f7e974480994561f73d0101844ed3fb468caf 100644 (file)
@@ -12,7 +12,11 @@ import os
 import logging
 import logging.config
 import textwrap
+import re
 import shlex, subprocess
+import copy
+import datetime
+import socket
 
 from subprocess import Popen, TimeoutExpired, PIPE
 
@@ -34,7 +38,7 @@ from .pdns_app import PDNSApiNotFoundError, PDNSApiValidationError
 from .pdns_zone import PdnsApiZone
 from .pdns_record import compare_rrsets
 
-__version__ = '0.1.2'
+__version__ = '0.2.1'
 LOG = logging.getLogger(__name__)
 
 
@@ -50,11 +54,78 @@ class PpDeployZonesApp(PpPDNSApplication):
     of the BIND named daemon.
     """
 
+    default_pidfile = '/run/dns-deploy-zones.pid'
+
+    default_named_conf_dir = '/etc'
+    default_named_zones_cfg_file = 'named.zones.conf'
+
+    zone_masters_local = [
+        '217.66.53.87',
+    ]
+
+    zone_masters_public = [
+        '217.66.53.97',
+    ]
+
+    default_cmd_checkconf = '/usr/sbin/named-checkconf'
+    default_cmd_reload = '/usr/sbin/rndc reload'
+    default_cmd_status = '/usr/bin/systemctl status named.service'
+    default_cmd_start = '/usr/bin/systemctl start named.service'
+    default_cmd_restart = '/usr/bin/systemctl restart named.service'
+
+    re_ipv4_zone = re.compile(r'^((?:\d+\.)+)in-addr\.arpa\.$')
+    re_ipv6_zone = re.compile(r'^((?:[\da-f]\.)+)ip6\.arpa\.$')
+
+    re_block_comment = re.compile(r'/\*.*?\*/', re.MULTILINE | re.DOTALL)
+    re_line_comment = re.compile(r'(?://|#).*$', re.MULTILINE)
+
+    re_split_addresses = re.compile(r'[,;\s]+')
+    re_integer = re.compile(r'^\s*(\d+)\s*$')
+
+    open_args = {}
+    if six.PY3:
+        open_args = {
+            'encoding': 'utf-8',
+            'errors': 'surrogateescape',
+        }
+
     # -------------------------------------------------------------------------
     def __init__(self, appname=None, base_dir=None, version=__version__):
 
         self.zones = []
 
+        self._show_simulate_opt = True
+
+        self.is_internal = False
+        self.pidfile_name = self.default_pidfile
+
+        # Configuration files and directories
+        self.named_conf_dir = self.default_named_conf_dir
+        self._named_zones_cfg_file = self.default_named_zones_cfg_file
+
+        self.zone_masters = copy.copy(self.zone_masters_public)
+        self.masters_configured = False
+
+        self.tempdir = None
+        self.temp_zones_cfg_file = None
+        self.keep_tempdir = False
+
+        self.backup_suffix = (
+            '.' + datetime.datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S') + '.bak')
+
+        self.reload_necessary = False
+        self.restart_necessary = False
+
+        self.cmd_checkconf = self.default_cmd_checkconf
+        self.cmd_reload = self.default_cmd_reload
+        self.cmd_status = self.default_cmd_status
+        self.cmd_start = self.default_cmd_start
+        self.cmd_restart = self.default_cmd_restart
+
+        self.files2replace = {}
+        self.moved_files = {}
+
+
         description = textwrap.dedent('''\
             Generation of the BIND9 configuration file for slave zones.
             ''')
@@ -66,6 +137,132 @@ class PpDeployZonesApp(PpPDNSApplication):
 
         self.initialized = True
 
+    # -------------------------------------------
+    @property
+    def named_zones_cfg_file(self):
+        """The file for configuration of all own zones."""
+        return os.path.join(self.named_conf_dir, self._named_zones_cfg_file)
+
+    # -------------------------------------------------------------------------
+    def init_arg_parser(self):
+
+        super(PpDeployZonesApp, self).init_arg_parser()
+
+        self.arg_parser.add_argument(
+            '-K', '--keep-tempdir', dest='keep_tempdir', action='store_true',
+            help=(
+                "Keeping the temporary directory instead of removing it at the end "
+                "(e.g. for debugging purposes)"),
+        )
+
+    # -------------------------------------------------------------------------
+    def perform_arg_parser(self):
+        """
+        Public available method to execute some actions after parsing
+        the command line parameters.
+        """
+
+        super(PpDeployZonesApp, self).perform_arg_parser()
+
+        if self.args.keep_tempdir:
+            self.keep_tempdir = True
+
+    # -------------------------------------------------------------------------
+    def perform_config(self):
+
+        super(PpDeployZonesApp, self).perform_config()
+
+        for section_name in self.cfg.keys():
+
+            if self.verbose > 3:
+                LOG.debug("Checking config section {!r} ...".format(section_name))
+
+            section = self.cfg[section_name]
+
+            if section_name.lower() == 'app':
+                self._check_path_config(section, section_name, 'pidfile', 'pidfile_name', True)
+
+            if section_name.lower() == 'named':
+                self.set_named_options(section, section_name)
+
+        if not self.masters_configured:
+            if self.environment == 'local':
+                self.zone_masters = copy.copy(self.zone_masters_local)
+            else:
+                self.zone_masters = copy.copy(self.zone_masters_public)
+
+    # -------------------------------------------------------------------------
+    def set_named_options(self, section, section_name):
+
+        if self.verbose > 2:
+            LOG.debug("Evaluating config section {n!r}:\n{s}".format(
+                n=section_name, s=pp(section)))
+
+        # Configuration files and directories
+        self._check_path_config(
+            section, section_name, 'config_dir', 'named_conf_dir', True)
+        self._check_path_config(
+            section, section_name, 'zones_cfg_file', '_named_zones_cfg_file', False)
+
+        if 'masters' in section:
+            self._get_masters_from_cfg(section['masters'], section_name)
+
+        for item in ('cmd_checkconf', 'cmd_reload', 'cmd_status', 'cmd_start', 'cmd_restart'):
+            if item in section and section[item].strip():
+                setattr(self, item, section[item].strip())
+
+    # -------------------------------------------------------------------------
+    def _get_masters_from_cfg(self, value, section_name):
+
+        value = value.strip()
+        if not value:
+            msg = "No masters given in [{}]/masters.".format(section_name)
+            LOG.error(msg)
+            self.config_has_errors = True
+            return
+
+        masters = []
+
+        for m in self.re_split_addresses.split(value):
+            if m:
+                m = m.strip().lower()
+                try:
+                    addr_info = socket.getaddrinfo(
+                        m, 53, proto=socket.IPPROTO_TCP, family=socket.AF_INET)
+                except socket.gaierror as e:
+                    msg = (
+                        "Invalid hostname or address {!r} found in "
+                        "[{}]/masters: {}").format(m, section_name, e)
+                    LOG.error(msg)
+                    self.config_has_errors = True
+                    m = None
+            if m:
+                masters.append(m)
+        if masters:
+            if self.verbose > 2:
+                LOG.debug("Using configured masters: {}".format(pp(masters)))
+            self.zone_masters = masters
+            self.masters_configured = True
+        else:
+            LOG.warn("No valid masters found in configuration.")
+
+    # -------------------------------------------------------------------------
+    def pre_run(self):
+        """
+        Dummy function to run before the main routine.
+        Could be overwritten by descendant classes.
+
+        """
+
+        super(PpDeployZonesApp, self).pre_run()
+
+        if self.environment == 'global':
+            LOG.error(
+                "Using the global DNS master is not supported, "
+                "please use 'local' or 'public'")
+            self.exit(1)
+
+
 
 # =============================================================================