import copy
import datetime
import socket
+import tempfile
+import time
+import shutil
from subprocess import Popen, TimeoutExpired, PIPE
from .pidfile import PidFileError, InvalidPidFileError, PidFileInUseError, PidFile
-__version__ = '0.2.4'
+__version__ = '0.3.1'
LOG = logging.getLogger(__name__)
default_named_conf_dir = '/etc'
default_named_zones_cfg_file = 'named.zones.conf'
+ default_named_basedir = '/var/named'
+ default_named_slavedir = 'slaves'
zone_masters_local = [
'217.66.53.87',
# 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.named_basedir = self.default_named_basedir
+ self._named_slavedir = self.default_named_slavedir
self.zone_masters = copy.copy(self.zone_masters_public)
self.masters_configured = False
"""The file for configuration of all own zones."""
return os.path.join(self.named_conf_dir, self._named_zones_cfg_file)
+ # -------------------------------------------
+ @property
+ def named_slavedir_rel(self):
+ """The directory for zone files of slave zones."""
+ return self._named_slavedir
+
+ # -------------------------------------------
+ @property
+ def named_slavedir_abs(self):
+ """The directory for zone files of slave zones."""
+ return os.path.join(self.named_basedir, self._named_slavedir)
+
# -------------------------------------------------------------------------
def init_arg_parser(self):
section, section_name, 'config_dir', 'named_conf_dir', True)
self._check_path_config(
section, section_name, 'zones_cfg_file', '_named_zones_cfg_file', False)
+ self._check_path_config(section, section_name, 'base_dir', 'named_basedir', True)
+ self._check_path_config(section, section_name, 'slave_dir', '_named_slavedir', False)
if 'masters' in section:
self._get_masters_from_cfg(section['masters'], section_name)
self.zones = self.get_api_zones()
self.zones.sort(key=lambda x: cmp_to_key(compare_fqdn)(x.name_unicode))
+ self.init_temp_objects()
+ self.generate_slave_cfg_file()
+
finally:
self.cleanup()
self.pidfile = None
shutil.rmtree(self.tempdir, False, emit_rm_err)
self.tempdir = None
+ # -------------------------------------------------------------------------
+ def init_temp_objects(self):
+ """Init temporary objects and properties."""
+
+ self.tempdir = tempfile.mkdtemp(
+ prefix=(self.appname + '.'), suffix='.tmp.d'
+ )
+ LOG.debug("Temporary directory: {!r}.".format(self.tempdir))
+
+ self.temp_zones_cfg_file = os.path.join(
+ self.tempdir, self.default_named_zones_cfg_file)
+
+ if self.verbose > 1:
+ LOG.debug("Temporary zones conf: {!r}".format(self.temp_zones_cfg_file))
+
+ # -------------------------------------------------------------------------
+ def generate_slave_cfg_file(self):
+
+ LOG.info("Generating {} ...".format(self.default_named_zones_cfg_file))
+
+ cur_date = datetime.datetime.now().isoformat(' ')
+ re_rev = re.compile(r'^rev\.', re.IGNORECASE)
+ re_trail_dot = re.compile(r'\.+$')
+
+ lines = []
+ lines.append('###############################################################')
+ lines.append('')
+ lines.append(' Bind9 configuration file for slave sones')
+ lines.append(' {}'.format(self.named_zones_cfg_file))
+ lines.append('')
+ lines.append(' Generated at: {}'.format(cur_date))
+ lines.append('')
+ lines.append('###############################################################')
+ header = textwrap.indent('\n'.join(lines), '//', lambda line: True) + '\n'
+
+ content = header
+
+ for zone in self.zones:
+
+ canonical_name = zone.name_unicode
+ match = self.re_ipv4_zone.search(zone.name)
+ if match:
+ prefix = self._get_ipv4_prefix(match.group(1))
+ if prefix:
+ if prefix == '127.0.0':
+ LOG.debug("Pure local zone {!r} will not be considered.".format(prefix))
+ continue
+ canonical_name = 'rev.' + prefix
+ else:
+ match = self.re_ipv6_zone.search(zone.name)
+ if match:
+ prefix = self._get_ipv6_prefix(match.group(1))
+ if prefix:
+ canonical_name = 'rev.' + prefix
+
+ show_name = canonical_name
+ show_name = re_rev.sub('Reverse ', show_name)
+ show_name = re_trail_dot.sub('', show_name)
+ zname = re_trail_dot.sub('', zone.name)
+
+ zfile = os.path.join(
+ self.named_slavedir_rel, re_trail_dot.sub('', canonical_name) + '.zone')
+
+ lines = []
+ lines.append('')
+ lines.append('// {}'.format(show_name))
+ lines.append('zone "{}" in {{'.format(zname))
+ lines.append('\tmasters {')
+ for master in self.zone_masters:
+ lines.append('\t\t{};'.format(master))
+ lines.append('\t};')
+ lines.append('\ttype slave;')
+ lines.append('\tfile "{}";'.format(zfile))
+ lines.append('};')
+
+ content += '\n'.join(lines) + '\n'
+
+ content += '\n// vim: ts=8 filetype=named noet noai\n'
+
+ with open(self.temp_zones_cfg_file, 'w', **self.open_args) as fh:
+ fh.write(content)
+
+ if self.verbose > 2:
+ LOG.debug("Generated {!r}:\n{}".format(self.temp_zones_cfg_file, content.strip()))
+
+ # -------------------------------------------------------------------------
+ def _get_ipv4_prefix(self, match):
+
+ tuples = []
+ for t in match.split('.'):
+ if t:
+ tuples.insert(0, t)
+ if self.verbose > 2:
+ 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)
+
+ 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]
+
+ if self.verbose > 2:
+ LOG.debug("Got IPv6 tokens: {}".format(pp(tokens)))
+
+ return ':'.join(tokens)
+
# =============================================================================