From 0f54d7c6dae67182894540feb8d4aceca97cb783 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Fri, 13 Oct 2023 15:58:31 +0200 Subject: [PATCH] Moving some methods into mixin module lib/cr_tf/handler/dns.py --- lib/cr_tf/handler/__init__.py | 306 +----------------------------- lib/cr_tf/handler/dns.py | 342 ++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+), 301 deletions(-) create mode 100644 lib/cr_tf/handler/dns.py diff --git a/lib/cr_tf/handler/__init__.py b/lib/cr_tf/handler/__init__.py index 7d27098..5f177cb 100644 --- a/lib/cr_tf/handler/__init__.py +++ b/lib/cr_tf/handler/__init__.py @@ -12,8 +12,6 @@ from __future__ import absolute_import, print_function import os import logging import re -import socket -import ipaddress import shutil import stat import textwrap @@ -32,7 +30,7 @@ from operator import attrgetter import pytz import six -from fb_tools.common import pp, to_bool, to_str, RE_DOT_AT_END +from fb_tools.common import pp, to_bool, to_str from fb_tools.errors import HandlerError, ExpectedHandlerError from fb_tools.handling_obj import HandlingObject, CalledProcessError from fb_tools.handler import BaseHandler @@ -42,6 +40,7 @@ from fb_vmware.config import VSPhereConfigInfo from fb_vmware.connect import VsphereConnection # Own modules +from .dns import CrTfHandlerDnsMixin from .first import CrTfHandlerFirstMixin from .read import CrTfHandlerReadMixin @@ -54,7 +53,7 @@ from ..errors import AbortExecution from ..xlate import XLATOR -__version__ = '3.9.3' +__version__ = '3.9.4' LOG = logging.getLogger(__name__) _ = XLATOR.gettext @@ -62,7 +61,8 @@ ngettext = XLATOR.ngettext # ============================================================================= -class CreateTerraformHandler(BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerReadMixin): +class CreateTerraformHandler( + BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerReadMixin, CrTfHandlerDnsMixin): """ A handler class for creating the terraform environment """ @@ -473,30 +473,6 @@ class CreateTerraformHandler(BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerRead if self.stop_at_step == 'collect-folders': raise AbortExecution('collect-folders') - # -------------------------------------------------------------------------· - def exec_pdns_zones(self): - - if self.config.no_pdns: - return - - if self.stop_at_step == 'pdns-zones': - self.incr_verbosity() - - print() - LOG.info(_("Retrieving informations from PowerDNS ...")) - - self.pdns.get_api_zones() - if self.eval_errors: - msg = ngettext( - "Found one error in exploring PowerDNS zones.", - "Found {n} errors in exploring PowerDNS zones.", - self.eval_errors).format(n=self.eval_errors) - raise ExpectedHandlerError(msg) - - LOG.info(_("Finished step {!r}.").format('pdns-zones')) - if self.stop_at_step == 'pdns-zones': - raise AbortExecution('pdns-zones') - # -------------------------------------------------------------------------· def init_vspheres(self, yaml_file): @@ -812,36 +788,6 @@ class CreateTerraformHandler(BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerRead if self.stop_at_step == 'validate-iface': raise AbortExecution('validate-iface') - # -------------------------------------------------------------------------· - def exec_validate_dns(self): - - if self.stop_at_step == 'validate-dns': - self.incr_verbosity() - - self.validate_dns_mappings() - if self.eval_errors: - msg = ngettext( - "Found one error in validating DNS mappings.", - "Found {n} errors in validating DNS mappings.", - self.eval_errors).format(n=self.eval_errors) - raise ExpectedHandlerError(msg) - - LOG.info(_("Finished step {!r}.").format('validate-dns')) - if self.stop_at_step == 'validate-dns': - raise AbortExecution('validate-dns') - - # -------------------------------------------------------------------------· - def exec_perform_dns(self): - - if self.stop_at_step == 'perform-dns': - self.incr_verbosity() - - self.perform_dns() - - LOG.info(_("Finished step {!r}.").format('perform-dns')) - if self.stop_at_step == 'perform-dns': - raise AbortExecution('perform-dns') - # -------------------------------------------------------------------------· def exec_project_dir(self): @@ -1390,145 +1336,6 @@ class CreateTerraformHandler(BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerRead if network not in self.used_networks[vs_name]: self.used_networks[vs_name].append(network) - # -------------------------------------------------------------------------· - def validate_dns_mappings(self): - - LOG.info(_("Validating DNS mappings ...")) - self._validate_forward_dns_mappings() - self._validate_reverse_dns_mappings() - - lines = [] - if self.dns_mappings2create['forward']: - for pair in self.dns_mappings2create['forward']: - line = ' * {n!r} => {a!r}'.format(n=pair[0], a=str(pair[1])) - lines.append(line) - else: - lines.append(self.colored('>>> ' + _('None') + ' <<<', 'AQUA')) - LOG.info(_("Forward DNS entries to create:") + "\n" + '\n'.join(lines)) - - lines = [] - if self.dns_mappings2create['reverse']: - for pair in self.dns_mappings2create['reverse']: - line = ' * {r} ({a!r}) => {n!r}'.format( - r=pair[0].reverse_pointer, n=pair[1], a=str(pair[0])) - lines.append(line) - else: - lines.append(self.colored('>>> ' + _('None') + ' <<<', 'AQUA')) - LOG.info(_("Reverse DNS entries to create:") + "\n" + '\n'.join(lines)) - - # -------------------------------------------------------------------------· - def _validate_forward_dns_mappings(self): - - if not self.dns_mapping['forward']: - return - - LOG.debug(_("Validating forward DNS mappings ...")) - - for (fqdn, address) in self.dns_mapping['forward']: - - if self.verbose > 1: - LOG.debug(_("Validating {f!r} => {a!r}.").format(f=fqdn, a=str(address))) - - results_v4 = [] - results_v6 = [] - - try: - addr_infos = socket.getaddrinfo(fqdn, 80) - except socket.gaierror: - addr_infos = [] - - for addr_info in addr_infos: - if addr_info[0] not in (socket.AF_INET, socket.AF_INET6): - continue - addr = ipaddress.ip_address(addr_info[4][0]) - if addr.version == 4: - if addr not in results_v4: - results_v4.append(addr) - else: - if addr not in results_v6: - results_v6.append(addr) - if self.verbose > 2: - if results_v4 or results_v6: - lines = [] - for addr in results_v4 + results_v6: - lines.append(' * {}'.format(str(addr))) - out = '\n'.join(lines) - LOG.debug(_("Found existing addresses for {f!r}:").format(f=fqdn) + '\n' + out) - else: - LOG.debug(_("Did not found existing addresses for {!r}.").format(fqdn)) - - if address.version == 4: - if not results_v4: - self.dns_mappings2create['forward'].append((fqdn, address)) - continue - if address in results_v4: - LOG.debug(_("FQDN {f!r} already points to {a!r}.").format( - f=fqdn, a=str(address))) - continue - else: - if not results_v6: - self.dns_mappings2create['forward'].append((fqdn, address)) - continue - if address in results_v6: - LOG.debug(_("FQDN {f!r} already points to {a!r}.").format( - f=fqdn, a=str(address))) - continue - - alist = '\n'.join(map(lambda x: ' * {}'.format(str(x)), results_v4 + results_v6)) - msg = (_( - "FQDN {f!r} has already existing addresses, " - "but none of them are {a!r}:").format(f=fqdn, a=str(address)) + "\n" + alist) - if self.ignore_existing_dns: - LOG.warn(msg) - self.dns_mappings2create['forward'].append((fqdn, address)) - else: - LOG.error(msg) - self.eval_errors += 1 - - # -------------------------------------------------------------------------· - def _validate_reverse_dns_mappings(self): - - if not self.dns_mapping['reverse']: - return - - LOG.debug(_("Validating reverse DNS mappings ...")) - - for (address, fqdn) in self.dns_mapping['reverse']: - - if self.verbose > 1: - LOG.debug(_("Validating {a!r} => {f!r}.").format(f=fqdn, a=str(address))) - - try: - info = socket.gethostbyaddr(str(address)) - except socket.herror: - info = [] - if self.verbose > 2: - LOG.debug(_("Got reverse info:") + "\n" + str(info)) - ptr = None - if info: - ptr = info[0] - - if not ptr: - if self.verbose > 1: - LOG.debug(_("Did not found reverse pointer for {!r}.").format(str(address))) - self.dns_mappings2create['reverse'].append((address, fqdn)) - continue - - ptr = RE_DOT_AT_END.sub('', ptr).lower() - fqdn_canon = RE_DOT_AT_END.sub('', fqdn).lower() - - if self.verbose > 1: - LOG.debug(_("Found reverse pointer {a!r} => {f!r}.").format(f=ptr, a=str(address))) - if fqdn_canon == ptr: - if self.verbose > 1: - LOG.debug(_("Reverse pointer for {!r} was already existing.").format( - str(address))) - continue - - LOG.error(_("Address {a!r} has already an existing reverse pointer to {p!r}.").format( - a=str(address), p=ptr)) - self.eval_errors += 1 - # -------------------------------------------------------------------------· def get_tf_name_network(self, net_name, *args): @@ -1592,109 +1399,6 @@ class CreateTerraformHandler(BaseHandler, CrTfHandlerFirstMixin, CrTfHandlerRead return default raise KeyError(_("Did not found datastore {!r}.").format(ds_name)) - # -------------------------------------------------------------------------- - def perform_dns(self): - - if self.config.no_pdns: - LOG.debug(_("Power DNS actions are not eceuted.")) - return - - print() - LOG.info(_("Performing DNS actions ...")) - print() - - # TODO: Check for simulate and mappings to create - - errors = 0 - - for (fqdn, address) in self.dns_mappings2create['forward']: - if not self._perform_dns_forward(fqdn, address): - errors += 1 - - for (address, fqdn) in self.dns_mappings2create['reverse']: - if not self._perform_dns_reverse(address, fqdn): - errors += 1 - - if errors: - msg = ngettext( - "There was one error in creating DNS mappings.", - "There were {n} errors in creating DNS mappings.", errors).format(n=errors) - raise ExpectedHandlerError(msg) - else: - if self.verbose > 1: - LOG.debug(_("No errors in creating DNS mappings.")) - - print() - - for zone_name in self.updated_zones: - self._increase_zone_serial(zone_name) - - # -------------------------------------------------------------------------- - def _increase_zone_serial(self, zone_name): - - LOG.info(_("Increasing serial of zone {!r}.").format(zone_name)) - - zone = self.pdns.zones[zone_name] - zone.increase_serial() - zone.notify() - - # -------------------------------------------------------------------------- - def _perform_dns_forward(self, fqdn, address): - - record_type = 'A' - addr_obj = ipaddress.ip_address(address) - if addr_obj.version == 6: - record_type = 'AAAA' - - canon_fqdn = self.pdns.canon_name(fqdn) - - zone_name = self.pdns.get_zone_for_item(canon_fqdn, is_fqdn=True) - if zone_name: - if self.verbose > 1: - LOG.debug(_("Got zone {z!r} for FQDN {f!r}.").format( - z=zone_name, f=canon_fqdn)) - else: - LOG.error(_("Did not found zone to insert {t}-record for {f!r}.").format( - t=record_type, f=fqdn)) - return False - - zone = self.pdns.zones[zone_name] - if addr_obj.is_private: - zone.add_address_record( - fqdn, address, set_ptr=False, comment='local', - account=self.config.pdns_comment_account, append_comments=True) - else: - zone.add_address_record(fqdn, address, set_ptr=False) - if zone_name not in self.updated_zones: - self.updated_zones.append(zone_name) - return True - - # -------------------------------------------------------------------------- - def _perform_dns_reverse(self, address, fqdn): - - LOG.debug(_("Trying to create PTR-record {a!r} => {f!r}.").format( - f=fqdn, a=str(address))) - - pointer = self.pdns.canon_name(address.reverse_pointer) - if self.verbose > 1: - LOG.debug(_("PTR of {a!r}: {p!r}.").format(a=str(address), p=pointer)) - - zone_name = self.pdns.get_zone_for_item(pointer, is_fqdn=True) - if zone_name: - if self.verbose > 1: - LOG.debug(_("Got reverse zone {z!r} for address {a!r}.").format( - z=zone_name, a=str(address))) - else: - LOG.warn(_("Did not found zone to insert PTR-record {p!r} ({a}).").format( - p=pointer, a=str(address))) - return True - - zone = self.pdns.zones[zone_name] - zone.add_ptr_record(pointer, fqdn) - if zone_name not in self.updated_zones: - self.updated_zones.append(zone_name) - return True - # -------------------------------------------------------------------------- def ensure_project_dir(self): diff --git a/lib/cr_tf/handler/dns.py b/lib/cr_tf/handler/dns.py new file mode 100644 index 0000000..612aa81 --- /dev/null +++ b/lib/cr_tf/handler/dns.py @@ -0,0 +1,342 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@author: Frank Brehm +@contact: frank.brehm@pixelpark.com +@copyright: © 2023 by Frank Brehm, Berlin +@summary: A mixin module for the handler module for dns related methods. +""" +from __future__ import absolute_import, print_function + +# Standard module +import ipaddress +import logging +import socket + +# Third party modules +from fb_tools.common import RE_DOT_AT_END +from fb_tools.errors import ExpectedHandlerError + +# Own modules + +from ..errors import AbortExecution + +from ..xlate import XLATOR + +__version__ = '0.1.0' +LOG = logging.getLogger(__name__) + +_ = XLATOR.gettext +ngettext = XLATOR.ngettext + + +# ============================================================================= +class CrTfHandlerDnsMixin(): + """A mixin module for the handler module for dns related methods.""" + + # -------------------------------------------------------------------------· + def exec_pdns_zones(self): + + if self.config.no_pdns: + return + + if self.stop_at_step == 'pdns-zones': + self.incr_verbosity() + + print() + LOG.info(_("Retrieving informations from PowerDNS ...")) + + self.pdns.get_api_zones() + if self.eval_errors: + msg = ngettext( + "Found one error in exploring PowerDNS zones.", + "Found {n} errors in exploring PowerDNS zones.", + self.eval_errors).format(n=self.eval_errors) + raise ExpectedHandlerError(msg) + + LOG.info(_("Finished step {!r}.").format('pdns-zones')) + if self.stop_at_step == 'pdns-zones': + raise AbortExecution('pdns-zones') + + # -------------------------------------------------------------------------· + def exec_validate_dns(self): + + if self.stop_at_step == 'validate-dns': + self.incr_verbosity() + + self.validate_dns_mappings() + if self.eval_errors: + msg = ngettext( + "Found one error in validating DNS mappings.", + "Found {n} errors in validating DNS mappings.", + self.eval_errors).format(n=self.eval_errors) + raise ExpectedHandlerError(msg) + + LOG.info(_("Finished step {!r}.").format('validate-dns')) + if self.stop_at_step == 'validate-dns': + raise AbortExecution('validate-dns') + + # -------------------------------------------------------------------------· + def exec_perform_dns(self): + + if self.stop_at_step == 'perform-dns': + self.incr_verbosity() + + self.perform_dns() + + LOG.info(_("Finished step {!r}.").format('perform-dns')) + if self.stop_at_step == 'perform-dns': + raise AbortExecution('perform-dns') + + # -------------------------------------------------------------------------- + def perform_dns(self): + + if self.config.no_pdns: + LOG.debug(_("Power DNS actions are not eceuted.")) + return + + print() + LOG.info(_("Performing DNS actions ...")) + print() + + # TODO: Check for simulate and mappings to create + + errors = 0 + + for (fqdn, address) in self.dns_mappings2create['forward']: + if not self._perform_dns_forward(fqdn, address): + errors += 1 + + for (address, fqdn) in self.dns_mappings2create['reverse']: + if not self._perform_dns_reverse(address, fqdn): + errors += 1 + + if errors: + msg = ngettext( + "There was one error in creating DNS mappings.", + "There were {n} errors in creating DNS mappings.", errors).format(n=errors) + raise ExpectedHandlerError(msg) + else: + if self.verbose > 1: + LOG.debug(_("No errors in creating DNS mappings.")) + + print() + + for zone_name in self.updated_zones: + self._increase_zone_serial(zone_name) + + # -------------------------------------------------------------------------- + def _increase_zone_serial(self, zone_name): + + LOG.info(_("Increasing serial of zone {!r}.").format(zone_name)) + + zone = self.pdns.zones[zone_name] + zone.increase_serial() + zone.notify() + + # -------------------------------------------------------------------------- + def _perform_dns_forward(self, fqdn, address): + + record_type = 'A' + addr_obj = ipaddress.ip_address(address) + if addr_obj.version == 6: + record_type = 'AAAA' + + canon_fqdn = self.pdns.canon_name(fqdn) + + zone_name = self.pdns.get_zone_for_item(canon_fqdn, is_fqdn=True) + if zone_name: + if self.verbose > 1: + LOG.debug(_("Got zone {z!r} for FQDN {f!r}.").format( + z=zone_name, f=canon_fqdn)) + else: + LOG.error(_("Did not found zone to insert {t}-record for {f!r}.").format( + t=record_type, f=fqdn)) + return False + + zone = self.pdns.zones[zone_name] + if addr_obj.is_private: + zone.add_address_record( + fqdn, address, set_ptr=False, comment='local', + account=self.config.pdns_comment_account, append_comments=True) + else: + zone.add_address_record(fqdn, address, set_ptr=False) + if zone_name not in self.updated_zones: + self.updated_zones.append(zone_name) + return True + + # -------------------------------------------------------------------------- + def _perform_dns_reverse(self, address, fqdn): + + LOG.debug(_("Trying to create PTR-record {a!r} => {f!r}.").format( + f=fqdn, a=str(address))) + + pointer = self.pdns.canon_name(address.reverse_pointer) + if self.verbose > 1: + LOG.debug(_("PTR of {a!r}: {p!r}.").format(a=str(address), p=pointer)) + + zone_name = self.pdns.get_zone_for_item(pointer, is_fqdn=True) + if zone_name: + if self.verbose > 1: + LOG.debug(_("Got reverse zone {z!r} for address {a!r}.").format( + z=zone_name, a=str(address))) + else: + LOG.warn(_("Did not found zone to insert PTR-record {p!r} ({a}).").format( + p=pointer, a=str(address))) + return True + + zone = self.pdns.zones[zone_name] + zone.add_ptr_record(pointer, fqdn) + if zone_name not in self.updated_zones: + self.updated_zones.append(zone_name) + return True + + # -------------------------------------------------------------------------· + def validate_dns_mappings(self): + + LOG.info(_("Validating DNS mappings ...")) + self._validate_forward_dns_mappings() + self._validate_reverse_dns_mappings() + + lines = [] + if self.dns_mappings2create['forward']: + for pair in self.dns_mappings2create['forward']: + line = ' * {n!r} => {a!r}'.format(n=pair[0], a=str(pair[1])) + lines.append(line) + else: + lines.append(self.colored('>>> ' + _('None') + ' <<<', 'AQUA')) + LOG.info(_("Forward DNS entries to create:") + "\n" + '\n'.join(lines)) + + lines = [] + if self.dns_mappings2create['reverse']: + for pair in self.dns_mappings2create['reverse']: + line = ' * {r} ({a!r}) => {n!r}'.format( + r=pair[0].reverse_pointer, n=pair[1], a=str(pair[0])) + lines.append(line) + else: + lines.append(self.colored('>>> ' + _('None') + ' <<<', 'AQUA')) + LOG.info(_("Reverse DNS entries to create:") + "\n" + '\n'.join(lines)) + + # -------------------------------------------------------------------------· + def _validate_forward_dns_mappings(self): + + if not self.dns_mapping['forward']: + return + + LOG.debug(_("Validating forward DNS mappings ...")) + + for (fqdn, address) in self.dns_mapping['forward']: + + if self.verbose > 1: + LOG.debug(_("Validating {f!r} => {a!r}.").format(f=fqdn, a=str(address))) + + results_v4 = [] + results_v6 = [] + + try: + addr_infos = socket.getaddrinfo(fqdn, 80) + except socket.gaierror: + addr_infos = [] + + for addr_info in addr_infos: + if addr_info[0] not in (socket.AF_INET, socket.AF_INET6): + continue + addr = ipaddress.ip_address(addr_info[4][0]) + if addr.version == 4: + if addr not in results_v4: + results_v4.append(addr) + else: + if addr not in results_v6: + results_v6.append(addr) + if self.verbose > 2: + if results_v4 or results_v6: + lines = [] + for addr in results_v4 + results_v6: + lines.append(' * {}'.format(str(addr))) + out = '\n'.join(lines) + LOG.debug(_("Found existing addresses for {f!r}:").format(f=fqdn) + '\n' + out) + else: + LOG.debug(_("Did not found existing addresses for {!r}.").format(fqdn)) + + if address.version == 4: + if not results_v4: + self.dns_mappings2create['forward'].append((fqdn, address)) + continue + if address in results_v4: + LOG.debug(_("FQDN {f!r} already points to {a!r}.").format( + f=fqdn, a=str(address))) + continue + else: + if not results_v6: + self.dns_mappings2create['forward'].append((fqdn, address)) + continue + if address in results_v6: + LOG.debug(_("FQDN {f!r} already points to {a!r}.").format( + f=fqdn, a=str(address))) + continue + + alist = '\n'.join(map(lambda x: ' * {}'.format(str(x)), results_v4 + results_v6)) + msg = (_( + "FQDN {f!r} has already existing addresses, " + "but none of them are {a!r}:").format(f=fqdn, a=str(address)) + "\n" + alist) + if self.ignore_existing_dns: + LOG.warn(msg) + self.dns_mappings2create['forward'].append((fqdn, address)) + else: + LOG.error(msg) + self.eval_errors += 1 + + # -------------------------------------------------------------------------· + def _validate_reverse_dns_mappings(self): + + if not self.dns_mapping['reverse']: + return + + LOG.debug(_("Validating reverse DNS mappings ...")) + + for (address, fqdn) in self.dns_mapping['reverse']: + + if self.verbose > 1: + LOG.debug(_("Validating {a!r} => {f!r}.").format(f=fqdn, a=str(address))) + + try: + info = socket.gethostbyaddr(str(address)) + except socket.herror: + info = [] + if self.verbose > 2: + LOG.debug(_("Got reverse info:") + "\n" + str(info)) + ptr = None + if info: + ptr = info[0] + + if not ptr: + if self.verbose > 1: + LOG.debug(_("Did not found reverse pointer for {!r}.").format(str(address))) + self.dns_mappings2create['reverse'].append((address, fqdn)) + continue + + ptr = RE_DOT_AT_END.sub('', ptr).lower() + fqdn_canon = RE_DOT_AT_END.sub('', fqdn).lower() + + if self.verbose > 1: + LOG.debug(_("Found reverse pointer {a!r} => {f!r}.").format(f=ptr, a=str(address))) + if fqdn_canon == ptr: + if self.verbose > 1: + LOG.debug(_("Reverse pointer for {!r} was already existing.").format( + str(address))) + continue + + LOG.error(_("Address {a!r} has already an existing reverse pointer to {p!r}.").format( + a=str(address), p=ptr)) + self.eval_errors += 1 + + +# ============================================================================= + +if __name__ == "__main__": + + pass + +# ============================================================================= + +# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list -- 2.39.5