]> Frank Brehm's Git Trees - pixelpark/admin-tools.git/commitdiff
Implemented getting zones from API
authorFrank Brehm <frank.brehm@pixelpark.com>
Thu, 3 Aug 2017 16:04:21 +0000 (18:04 +0200)
committerFrank Brehm <frank.brehm@pixelpark.com>
Thu, 3 Aug 2017 16:04:21 +0000 (18:04 +0200)
pp_lib/config_named_app.py

index cdaf8be2efeeac145abeb7a3c6b1adb65df0e722..8cd320964b3b67c49e6ea229b311d3fb19647107 100644 (file)
@@ -23,13 +23,16 @@ import grp
 
 # Third party modules
 import six
+import requests
+
+from six.moves.urllib.parse import urlunsplit
 
 # Own modules
-from .common import pp, to_bool
+from .common import pp, to_bool, to_bytes
 
 from .cfg_app import PpCfgAppError, PpConfigApplication
 
-__version__ = '0.2.1'
+__version__ = '0.3.0'
 LOG = logging.getLogger(__name__)
 
 
@@ -46,7 +49,8 @@ class PpConfigNamedApp(PpConfigApplication):
 
     default_pdns_api_host = 'systemshare.pixelpark.com'
     default_pdns_api_port = 8081
-    default_pdns_api_root_path = '/api/v1/servers/localhost'
+    default_pdns_api_root_path = '/api/v1'
+    default_pdns_api_server_id = 'localhost'
     default_named_conf = '/etc/named.conf'
     default_named_zones_cfg_dir = '/etc/named'
     default_named_basedir = '/var/named'
@@ -63,12 +67,16 @@ class PpConfigNamedApp(PpConfigApplication):
     re_split_addresses = re.compile(r'[,;\s]+')
     re_integer = re.compile(r'^\s*(\d+)\s*$')
 
+    re_ipv4_zone = re.compile(r'^((?:\d+\.)+)in-addr\.arpa\.$')
+    re_ipv6_zone = re.compile(r'^((?:[\da-f]\.)+)ip6\.arpa\.$')
+
     # -------------------------------------------------------------------------
     def __init__(self, appname=None, version=__version__):
 
         self.pdns_api_host = self.default_pdns_api_host
         self.pdns_api_port = self.default_pdns_api_port
         self.pdns_api_root_path = self.default_pdns_api_root_path
+        self.pdns_api_server_id = self.default_pdns_api_server_id
         self.pdns_api_key = None
 
         self.is_internal = False
@@ -96,6 +104,8 @@ class PpConfigNamedApp(PpConfigApplication):
         self.named_show_bind_version = False
         self.named_version2show = self.default_named_version2show
 
+        self.zones = {}
+
         description = textwrap.dedent('''\
             Generation of configuration of named (the BIND 9 name daemon).
             ''').strip()
@@ -199,6 +209,9 @@ class PpConfigNamedApp(PpConfigApplication):
             section, section_name, 'root_path',
             'pdns_api_root_path', True, 'root path of the PowerDNS')
 
+        if 'server_id' in section and section['server_id'].strip():
+            self.pdns_api_server_id = section['server_id'].strip().lower()
+
         if 'key' in section:
             key = section['key'].strip()
             self.pdns_api_key = key
@@ -353,7 +366,130 @@ class PpConfigNamedApp(PpConfigApplication):
         if os.geteuid():
             LOG.error("You must be root to execute this script.")
             self.exit(1)
-        LOG.info("Jetzt geht's looos")
+
+        self.get_api_zones()
+
+    # -------------------------------------------------------------------------
+    def get_api_zones(self):
+
+        LOG.info("Trying to get all zones from PDNS API ...")
+
+        headers = {}
+        if self.pdns_api_key:
+            headers['X-API-Key'] = self.pdns_api_key
+
+        path = os.path.join(
+            self.pdns_api_root_path, 'servers', self.pdns_api_server_id, 'zones')
+        server = self.pdns_api_host
+        if self.pdns_api_port != 80:
+            server = '{}:{}'.format(server, self.pdns_api_port)
+        url = urlunsplit(('http', server, path, None, None))
+        LOG.debug("URL to send API call: {!r}.".format(url))
+        if self.verbose > 1:
+            LOG.debug("Headers:\n%s", pp(headers))
+        session = requests.Session()
+        response = session.request(
+            'GET', url, headers=headers, timeout=10)
+        if self.verbose > 1:
+            LOG.debug("Response status code: {}".format(response.status_code))
+        if not response.ok:
+            try:
+                err = response.json()
+                code = err['httpStatus']
+                msg = err['messages']
+                LOG.error("Got an error from API ({}) with status {}: {}".format(
+                    url, code, msg))
+                self.exit(6)
+            except ValueError:
+                msg = 'Failed to parse the response from {!r}: {}'.format(
+                    url, response.text)
+                LOG.error(msg)
+                self.exit(6)
+
+        json_response = response.json()
+        if self.verbose > 3:
+            LOG.debug("Got a response:\n{}".format(pp(json_response)))
+
+        for entry in json_response:
+
+#           {   'account': '',
+#               'dnssec': False,
+#               'id': '56.66.217.in-addr.arpa.',
+#               'kind': 'Master',
+#               'last_check': 0,
+#               'masters': [],
+#               'name': '56.66.217.in-addr.arpa.',
+#               'notified_serial': 2017080202,
+#               'serial': 2017080202,
+#               'url': 'api/v1/servers/localhost/zones/56.66.217.in-addr.arpa.'},
+
+            zone_name = entry['name']
+            zone = {
+                'account': entry['account'],
+                'kind': entry['kind'],
+                'serial': entry['serial'],
+            }
+
+            if entry['dnssec']:
+                self.named_dnssec = True
+            if self.verbose > 1:
+                LOG.debug("Found zone {!r}.".format(zone_name))
+
+            uni_name = None
+            match = self.re_ipv4_zone.search(zone_name)
+            if match:
+                prefix = self._get_ipv4_prefix(match.group(1))
+                if prefix:
+                    uni_name = 'rev.' + prefix
+
+            match = self.re_ipv6_zone.search(zone_name)
+            if match:
+                prefix = self._get_ipv6_prefix(match.group(1))
+                if prefix:
+                    uni_name = 'rev.' + prefix
+
+            if not uni_name:
+                uni_name = zone_name.encode('utf-8').decode('idna')
+
+            zone['canoniical_name'] = uni_name
+
+            self.zones[zone_name] = zone
+
+        if self.verbose > 2:
+            LOG.debug("Got zones:\n{}".format(pp(self.zones)))
+
+    # -------------------------------------------------------------------------
+    def _get_ipv4_prefix(self, match):
+
+        tuples = []
+        for t in match.split('.'):
+            if t:
+                tuples.insert(0, t)
+        LOG.debug("Got IPv4 tuples: {}".format(pp(tuples)))
+        return '.'.join(tuples)
+
+    # -------------------------------------------------------------------------
+    def _get_ipv6_prefix(self, match):
+
+        tuples = []
+        for t in match.split('.'):
+            if t:
+                tuples.insert(0, t)
+        LOG.debug("Got IPv6 tuples: {}".format(pp(tuples)))
+
+        tokens = []
+        while len(tuples):
+            token = ''.join(tuples[0:4]).ljust(4, '0')
+            if token.startswith('000'):
+                token = token[3:]
+            elif token.startswith('00'):
+                token = token[2:]
+            elif token.startswith('0'):
+                token = token[1:]
+            tokens.append(token)
+            del tuples[0:4]
+
+        return ':'.join(tokens)
 
 # =============================================================================