From a48d25749f7aa2a163a7a7d023a4c6cfff5a0ce5 Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Fri, 10 Nov 2017 10:30:02 +0100 Subject: [PATCH] Applying configuration and reloading BIND --- pp_lib/deploy_zones_from_pdns.py | 252 ++++++++++++++++++++++++++++++- 1 file changed, 251 insertions(+), 1 deletion(-) diff --git a/pp_lib/deploy_zones_from_pdns.py b/pp_lib/deploy_zones_from_pdns.py index 611a5c1..7ca8579 100644 --- a/pp_lib/deploy_zones_from_pdns.py +++ b/pp_lib/deploy_zones_from_pdns.py @@ -20,6 +20,7 @@ import socket import tempfile import time import shutil +import pipes from subprocess import Popen, TimeoutExpired, PIPE @@ -43,7 +44,7 @@ from .pdns_record import compare_rrsets from .pidfile import PidFileError, InvalidPidFileError, PidFileInUseError, PidFile -__version__ = '0.3.2' +__version__ = '0.4.1' LOG = logging.getLogger(__name__) @@ -331,6 +332,16 @@ class PpDeployZonesApp(PpPDNSApplication): self.generate_slave_cfg_file() self.compare_files() + try: + self.replace_configfiles() + if not self.check_namedconf(): + self.restore_configfiles() + self.exit(99) + self.apply_config() + except Exception: + self.restore_configfiles() + raise + finally: self.cleanup() self.pidfile = None @@ -568,6 +579,245 @@ class PpDeployZonesApp(PpPDNSApplication): return True + # ------------------------------------------------------------------------- + def replace_configfiles(self): + + if not self.files2replace: + LOG.debug("No replacement of any config files necessary.") + return + + LOG.debug("Start replacing of config files ...") + + for tgt_file in self.files2replace.keys(): + + backup_file = tgt_file + self.backup_suffix + + if os.path.exists(tgt_file): + self.moved_files[tgt_file] = backup_file + LOG.info("Copying {!r} => {!r} ...".format(tgt_file, backup_file)) + if not self.simulate: + shutil.copy2(tgt_file, backup_file) + + if self.verbose > 1: + LOG.debug("All backuped config files:\n{}".format(pp(self.moved_files))) + + for tgt_file in self.files2replace.keys(): + src_file = self.files2replace[tgt_file] + LOG.info("Copying {!r} => {!r} ...".format(src_file, tgt_file)) + if not self.simulate: + shutil.copy2(src_file, tgt_file) + + # ------------------------------------------------------------------------- + def restore_configfiles(self): + + LOG.error("Restoring of original config files because of an exception.") + + for tgt_file in self.moved_files.keys(): + backup_file = self.moved_files[tgt_file] + LOG.info("Moving {!r} => {!r} ...".format(backup_file, tgt_file)) + if not self.simulate: + if os.path.exists(backup_file): + os.rename(backup_file, tgt_file) + else: + LOG.error("Could not find backup file {!r}.".format(backup_file)) + + # ------------------------------------------------------------------------- + def check_namedconf(self): + + LOG.info("Checking syntax correctness of named.conf ...") + cmd = shlex.split(self.cmd_checkconf) + if 'named-checkconf' in self.cmd_checkconf and self.verbose > 2: + cmd.append('-p') + cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd)) + LOG.debug("Executing: {}".format(cmd_str)) + + std_out = None + std_err = None + ret_val = None + + with Popen(cmd, stdout=PIPE, stderr=PIPE) as proc: + try: + std_out, std_err = proc.communicate(timeout=10) + except TimeoutExpired: + proc.kill() + std_out, std_err = proc.communicate() + ret_val = proc.wait() + + LOG.debug("Return value: {!r}".format(ret_val)) + if std_out and std_out.strip(): + s = to_str(std_out.strip()) + LOG.warn("Output on STDOUT: {}".format(s)) + if std_err and std_err.strip(): + s = to_str(std_err.strip()) + LOG.warn("Output on STDERR: {}".format(s)) + + if ret_val: + return False + + return True + + # ------------------------------------------------------------------------- + def apply_config(self): + + if not self.reload_necessary and not self.restart_necessary: + LOG.info("Reload or restart of named is not necessary.") + return + + running = self.named_running() + if not running: + LOG.warn("Named is not running, please start it manually.") + return + + if self.restart_necessary: + self.restart_named() + else: + self.reload_named() + + # ------------------------------------------------------------------------- + def named_running(self): + + LOG.debug("Checking, whether named is running ...") + + cmd = shlex.split(self.cmd_status) + cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd)) + LOG.debug("Executing: {}".format(cmd_str)) + + std_out = None + std_err = None + ret_val = None + + with Popen(cmd, stdout=PIPE, stderr=PIPE) as proc: + try: + std_out, std_err = proc.communicate(timeout=10) + except TimeoutExpired: + proc.kill() + std_out, std_err = proc.communicate() + ret_val = proc.wait() + + LOG.debug("Return value: {!r}".format(ret_val)) + if std_out and std_out.strip(): + s = to_str(std_out.strip()) + LOG.debug("Output on STDOUT:\n{}".format(s)) + if std_err and std_err.strip(): + s = to_str(std_err.strip()) + LOG.warn("Output on STDERR: {}".format(s)) + + if ret_val: + return False + + return True + + # ------------------------------------------------------------------------- + def start_named(self): + + LOG.info("Starting named ...") + + cmd = shlex.split(self.cmd_start) + cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd)) + LOG.debug("Executing: {}".format(cmd_str)) + + if self.simulate: + return + + std_out = None + std_err = None + ret_val = None + + with Popen(cmd, stdout=PIPE, stderr=PIPE) as proc: + try: + std_out, std_err = proc.communicate(timeout=30) + except TimeoutExpired: + proc.kill() + std_out, std_err = proc.communicate() + ret_val = proc.wait() + + LOG.debug("Return value: {!r}".format(ret_val)) + if std_out and std_out.strip(): + s = to_str(std_out.strip()) + LOG.debug("Output on STDOUT:\n{}".format(s)) + if std_err and std_err.strip(): + s = to_str(std_err.strip()) + LOG.error("Output on STDERR: {}".format(s)) + + if ret_val: + return False + + return True + + # ------------------------------------------------------------------------- + def restart_named(self): + + LOG.info("Restarting named ...") + + cmd = shlex.split(self.cmd_restart) + cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd)) + LOG.debug("Executing: {}".format(cmd_str)) + + if self.simulate: + return + + std_out = None + std_err = None + ret_val = None + + with Popen(cmd, stdout=PIPE, stderr=PIPE) as proc: + try: + std_out, std_err = proc.communicate(timeout=30) + except TimeoutExpired: + proc.kill() + std_out, std_err = proc.communicate() + ret_val = proc.wait() + + LOG.debug("Return value: {!r}".format(ret_val)) + if std_out and std_out.strip(): + s = to_str(std_out.strip()) + LOG.debug("Output on STDOUT:\n{}".format(s)) + if std_err and std_err.strip(): + s = to_str(std_err.strip()) + LOG.error("Output on STDERR: {}".format(s)) + + if ret_val: + return False + + return True + + # ------------------------------------------------------------------------- + def reload_named(self): + + LOG.info("Reloading named ...") + + cmd = shlex.split(self.cmd_reload) + cmd_str = ' '.join(map(lambda x: pipes.quote(x), cmd)) + LOG.debug("Executing: {}".format(cmd_str)) + + if self.simulate: + return + + std_out = None + std_err = None + ret_val = None + + with Popen(cmd, stdout=PIPE, stderr=PIPE) as proc: + try: + std_out, std_err = proc.communicate(timeout=30) + except TimeoutExpired: + proc.kill() + std_out, std_err = proc.communicate() + ret_val = proc.wait() + + LOG.debug("Return value: {!r}".format(ret_val)) + if std_out and std_out.strip(): + s = to_str(std_out.strip()) + LOG.debug("Output on STDOUT:\n{}".format(s)) + if std_err and std_err.strip(): + s = to_str(std_err.strip()) + LOG.error("Output on STDERR: {}".format(s)) + + if ret_val: + return False + + return True + # ============================================================================= -- 2.39.5