def _sync_delete_domain(self, domain, new_domain_flag=False): """ Remove domain zone files and reload bind config """ LOG.debug('Delete Domain: %s' % domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join( output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) os.remove(output_path) rndc_op = 'delzone' rndc_call = self._rndc_base() + [rndc_op, domain['name']] utils.execute(*rndc_call) #This goes and gets the name of the .nzf file that is a mirror of the #zones.config file we wish to maintain. The file name can change as it #is a hash of rndc view name, we're only interested in the first file #name this returns because there is only one .nzf file nzf_name = glob.glob('%s/*.nzf' % cfg.CONF[self.name].nzf_path) output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _sync_delete_domain(self, domain, new_domain_flag=False): """ Remove domain zone files and reload bind config """ LOG.debug('Delete Domain: %s' % domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join(output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) os.remove(output_path) rndc_op = 'delzone' rndc_call = self._rndc_base() + [rndc_op, domain['name']] utils.execute(*rndc_call) #This goes and gets the name of the .nzf file that is a mirror of the #zones.config file we wish to maintain. The file name can change as it #is a hash of rndc view name, we're only interested in the first file #name this returns because there is only one .nzf file nzf_name = glob.glob('/var/cache/bind/*.nzf') output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _execute_rndc(self, rndc_call): try: LOG.debug('Executing RNDC call: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) except utils.processutils.ProcessExecutionError as e: LOG.debug('RNDC call failure: %s' % e) raise exceptions.Backend(e)
def _sync_domain(self, domain, new_domain_flag=False): """ Sync a single domain's zone file """ LOG.debug('Synchronising Domain: %s' % domain['id']) servers = self.central_service.get_servers(self.admin_context) records = self.central_service.get_records(self.admin_context, domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join(output_folder, '%s.zone' % domain['id']) utils.render_template_to_file('bind9-zone.jinja2', output_path, servers=servers, domain=domain, records=records) self._sync_domains() rndc_op = 'reconfig' if new_domain_flag else 'reload' rndc_call = self._rndc_base() + [rndc_op] if not new_domain_flag: rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call)
def delete_domain(self, domain_name): LOG.debug('Delete Domain: %s' % domain_name) rndc_op = 'delzone' # RNDC doesn't like the trailing dot on the domain name rndc_call = self._rndc_base() + [rndc_op, domain_name.rstrip('.')] utils.execute(*rndc_call)
def _execute_rndc(self, rndc_op): try: rndc_call = self._rndc_base() rndc_call.extend(rndc_op) LOG.debug('Executing RNDC call: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) except processutils.ProcessExecutionError as e: raise exceptions.Backend(e)
def delete_zone(self, zone_name): LOG.debug('Delete Zone: %s' % zone_name) rndc_op = 'delzone' # RNDC doesn't like the trailing dot on the zone name rndc_call = self._rndc_base() + [rndc_op, zone_name.rstrip('.')] utils.execute(*rndc_call)
def _sync_domains(self): """ Update the zone file and reconfig rndc to update bind. Unike regular bind, this only needs to be done upon adding or deleting domains as mysqlbind takes care of updating bind upon regular record changes """ LOG.debug('Synchronising domains') domains = self.central_service.find_domains(self.admin_context) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') # Create the output folder tree if necessary if not os.path.exists(output_folder): os.makedirs(output_folder) output_path = os.path.join(output_folder, 'zones.config') abs_state_path = os.path.abspath(cfg.CONF.state_path) LOG.debug("Getting ready to write zones.config at %s" % output_path) # NOTE(CapTofu): Might have to adapt this later on? url = self.get_url_data() utils.render_template_to_file( 'mysql-bind9-config.jinja2', output_path, domains=domains, state_path=abs_state_path, dns_server_type=cfg.CONF[self.name].dns_server_type, dns_db_schema=url['database'], dns_db_table=cfg.CONF[self.name].database_dns_table, dns_db_host=url['host'], dns_db_user=url['username'], dns_db_password=url['password']) # only do this if domain create, domain delete rndc_call = [ 'rndc', '-s', cfg.CONF[self.name].rndc_host, '-p', str(cfg.CONF[self.name].rndc_port), ] if cfg.CONF[self.name].rndc_config_file: rndc_call.extend(['-c', self.config.rndc_config_file]) if cfg.CONF[self.name].rndc_key_file: rndc_call.extend(['-k', self.config.rndc_key_file]) rndc_call.extend(['reconfig']) utils.execute(*rndc_call)
def _sync_domains(self): """ Update the zone file and reconfig rndc to update bind. Unike regular bind, this only needs to be done upon adding or deleting domains as mysqlbind takes care of updating bind upon regular record changes """ LOG.debug('Synchronising domains') domains = self.central_service.get_domains(self.admin_context) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') # Create the output folder tree if necessary if not os.path.exists(output_folder): os.makedirs(output_folder) output_path = os.path.join(output_folder, 'zones.config') abs_state_path = os.path.abspath(cfg.CONF.state_path) LOG.debug("Getting ready to write zones.config at %s" % output_path) # NOTE(CapTofu): Might have to adapt this later on? url = self.get_url_data() utils.render_template_to_file('mysql-bind9-config.jinja2', output_path, domains=domains, state_path=abs_state_path, dns_server_type=cfg.CONF[self.name]. dns_server_type, dns_db_schema=url['database'], dns_db_table=cfg.CONF[self.name]. database_dns_table, dns_db_host=url['host'], dns_db_user=url['username'], dns_db_password=url['password']) # only do this if domain create, domain delete rndc_call = [ 'rndc', '-s', cfg.CONF[self.name].rndc_host, '-p', str(cfg.CONF[self.name].rndc_port), ] if cfg.CONF[self.name].rndc_config_file: rndc_call.extend(['-c', self.config.rndc_config_file]) if cfg.CONF[self.name].rndc_key_file: rndc_call.extend(['-k', self.config.rndc_key_file]) rndc_call.extend(['reconfig']) utils.execute(*rndc_call)
def start(self): super(Bind9Backend, self).start() domains = self.central_service.find_domains(self.admin_context) for domain in domains: rndc_op = 'reload' rndc_call = self._rndc_base() + [rndc_op] rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call)
def _execute_rndc(self, rndc_op): """Execute rndc :param rndc_op: rndc arguments :type rndc_op: list :returns: None :raises: exceptions.Backend """ try: rndc_call = self._rndc_call_base + rndc_op LOG.debug('Executing RNDC call: %r', rndc_call) utils.execute(*rndc_call) except utils.processutils.ProcessExecutionError as e: raise exceptions.Backend(e)
def _execute_rndc(self, rndc_op): """Execute rndc :param rndc_op: rndc arguments :type rndc_op: list :returns: None :raises: exceptions.Backend """ try: rndc_call = self._rndc_call_base + rndc_op LOG.debug('Executing RNDC call: %r', rndc_call) utils.execute(*rndc_call) except utils.processutils.ProcessExecutionError as e: LOG.info('RNDC call failure: %s', e) raise exceptions.Backend(e)
def _execute_knotc(self, *knotc_args, **kw): """Run the Knot client and check the output :param expected_output: expected output (default: 'OK') :type expected_output: str :param expected_error: expected alternative output, will be \ logged as info(). Default: not set. :type expected_error: str """ # Knotc returns "0" even on failure, we have to check for 'OK' # https://gitlab.labs.nic.cz/labs/knot/issues/456 LOG.debug("Executing knotc with %r", knotc_args) expected = kw.get('expected_output', 'OK') expected_alt = kw.get('expected_error', None) try: out, err = execute(self._knotc_cmd_name, *knotc_args) out = out.rstrip() LOG.debug("Command output: %r" % out) if out != expected: if expected_alt is not None and out == expected_alt: LOG.info(_LI("Ignoring error: %r"), out) else: raise ProcessExecutionError(stdout=out, stderr=err) except ProcessExecutionError as e: LOG.error(_LE("Command output: %(out)r Stderr: %(err)r"), { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend(e)
def _perform_axfr_from_minidns(self, zone_name): """Instruct axfr-get to request an AXFR from MiniDNS. :raises: exceptions.Backend on error """ zone_fn = self._datafiles_path_tpl % zone_name zone_tmp_fn = self._datafiles_tmp_path_tpl % zone_name # Perform AXFR, create or update a zone datafile # No need to lock globally here. # Axfr-get creates the datafile atomically by doing rename mdns_hostname, mdns_port = random.choice(self._masters) with lockutils.lock("%s.lock" % zone_name): LOG.debug("writing to %s", zone_fn) cmd = (self._tcpclient_cmd_name, mdns_hostname, "%d" % mdns_port, self._axfr_get_cmd_name, zone_name, zone_fn, zone_tmp_fn) LOG.debug("Executing AXFR as %r", ' '.join(cmd)) try: out, err = execute(*cmd) except ProcessExecutionError as e: LOG.error("Error executing AXFR as %r", ' '.join(cmd)) LOG.error("Command output: %(out)r Stderr: %(err)r", { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend(str(e)) finally: try: os.remove(zone_tmp_fn) except OSError: pass
def find_zone_serial(self, zone_name): """Get serial from a zone by running knotc :returns: serial (int or None) :raises: exceptions.Backend """ zone_name = zone_name.rstrip('.') LOG.debug("Finding %s", zone_name) # Output example: # [530336536.com.] type: slave | serial: 0 | next-event: idle | # auto-dnssec: disabled] try: out, err = execute(self._knotc_cmd_name, 'zone-status', zone_name) except ProcessExecutionError as e: if 'no such zone' in e.stdout: # Zone not found return None LOG.error(_LE("Command output: %(out)r Stderr: %(err)r"), { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend(e) try: serial = out.split('|')[1].split()[1] return int(serial) except Exception as e: LOG.error(_LE("Unable to parse knotc output: %r"), out) raise exceptions.Backend("Unexpected knotc zone-status output")
def _execute_rndc(self, rndc_op): """Execute rndc :param rndc_op: rndc arguments :type rndc_op: list :returns: None :raises: exceptions.Backend """ try: rndc_call = self._rndc_call_base + rndc_op LOG.debug('Executing RNDC call: %r with timeout %s', rndc_call, self._rndc_timeout) utils.execute(*rndc_call, timeout=self._rndc_timeout) except (utils.processutils.ProcessExecutionError, subprocess.TimeoutExpired) as e: raise exceptions.Backend(e)
def _sync_delete_domain(self, domain, new_domain_flag=False): """ Remove domain zone files and reload bind config """ # TODO(kiall): Rewrite this entire thing ASAP LOG.debug('Delete Domain: %s' % domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join(output_folder, '%s.zone' % domain['id']) os.remove(output_path) self._sync_domains() rndc_call = self._rndc_base() + ['reload'] utils.execute(*rndc_call)
def _sync_domain(self, domain, new_domain_flag=False): """ Sync a single domain's zone file and reload bind config """ LOG.debug('Synchronising Domain: %s' % domain['id']) servers = self.central_service.find_servers(self.admin_context) records = self.central_service.find_records(self.admin_context, domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join( output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) utils.render_template_to_file('bind9-zone.jinja2', output_path, servers=servers, domain=domain, records=records) rndc_call = self._rndc_base() if new_domain_flag: rndc_op = [ 'addzone', '%s { type master; file "%s"; };' % (domain['name'], output_path), ] rndc_call.extend(rndc_op) else: rndc_op = 'reload' rndc_call.extend([rndc_op]) rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) nzf_name = glob.glob('/var/cache/bind/*.nzf') output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _execute(self, op, kwargs): try: call = self._base() + op + self._params(**kwargs) LOG.debug(('Executing Denominator call: %s' % ' '.join(call))) stdout, _ = utils.execute(*call) return stdout except utils.processutils.ProcessExecutionError as e: LOG.debug('Denominator call failure: %s' % e) raise exceptions.Base(e)
def _sync_domain(self, domain, new_domain_flag=False): """ Sync a single domain's zone file and reload bind config """ LOG.debug('Synchronising Domain: %s' % domain['id']) servers = self.central_service.find_servers(self.admin_context) records = self.central_service.find_records(self.admin_context, domain['id']) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join(output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) utils.render_template_to_file('bind9-zone.jinja2', output_path, servers=servers, domain=domain, records=records) rndc_call = self._rndc_base() if new_domain_flag: rndc_op = [ 'addzone', '%s { type master; file "%s"; };' % (domain['name'], output_path), ] rndc_call.extend(rndc_op) else: rndc_op = 'reload' rndc_call.extend([rndc_op]) rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) nzf_name = glob.glob('/var/cache/bind/*.nzf') output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _rebuild_data_cdb(self): """Rebuild data.cdb file from zone datafiles Requires global lock On zone creation, axfr-get creates datafiles atomically by doing rename. On zone deletion, os.remove deletes the file atomically Globbing and reading the datafiles can be done without locking on them. The data and data.cdb files are written into a unique temp directory """ tmpdir = tempfile.mkdtemp(dir=self._datafiles_dir) data_fn = os.path.join(tmpdir, 'data') tmp_cdb_fn = os.path.join(tmpdir, 'data.cdb') try: self._concatenate_zone_datafiles(data_fn, self._datafiles_path_glob) # Generate the data.cdb file LOG.info("Updating data.cdb") LOG.debug("Convert %s to %s", data_fn, tmp_cdb_fn) try: out, err = execute( cfg.CONF[CFG_GROUP].tinydns_data_cmd_name, cwd=tmpdir ) except ProcessExecutionError as e: LOG.error("Failed to generate data.cdb") LOG.error("Command output: %(out)r Stderr: %(err)r", { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend("Failed to generate data.cdb") LOG.debug("Move %s to %s", tmp_cdb_fn, self._tinydns_cdb_filename) try: os.rename(tmp_cdb_fn, self._tinydns_cdb_filename) except OSError: os.remove(tmp_cdb_fn) LOG.error("Unable to move data.cdb to %s", self._tinydns_cdb_filename) raise exceptions.Backend("Unable to move data.cdb") finally: try: os.remove(data_fn) except OSError: pass try: os.removedirs(tmpdir) except OSError: pass
def start(self): super(Bind9Backend, self).start() domains = self.central_service.find_domains(self.admin_context) for domain in domains: rndc_op = 'reload' rndc_call = self._rndc_base() + [rndc_op] rndc_call.extend([domain['name']]) try: LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) except utils.processutils.ProcessExecutionError as proc_exec_err: stderr = proc_exec_err.stderr if stderr.count("rndc: 'reload' failed: not found") is not 0: LOG.warn("Domain %s (%s) missing from backend, recreating", domain['name'], domain['id']) self._sync_domain(domain, new_domain_flag=True) else: raise proc_exec_err
def _check_conf(self): """Run gdnsd to check its configuration """ try: out, err = utils.execute( cfg.CONF[CFG_GROUP].gdnsd_cmd_name, '-D', '-x', 'checkconf', '-c', self._confdir_path, run_as_root=False, ) except ProcessExecutionError as e: LOG.error(_LE("Command output: %(out)r Stderr: %(err)r"), { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend("Configuration check failed")
def test_execute(self, mock_execute): mock_execute.return_value = ('designate.conf\npools.yaml\n', '') out, err = utils.execute('/bin/ls', '/etc/designate/', run_as_root=False) mock_execute.assert_called_once_with( '/bin/ls', '/etc/designate/', root_helper='sudo designate-rootwrap /etc/designate/rootwrap.conf', run_as_root=False) self.assertEqual('designate.conf\npools.yaml\n', out) self.assertFalse(err)
def _check_conf(self): """Run gdnsd to check its configuration """ try: out, err = utils.execute( cfg.CONF[CFG_GROUP].gdnsd_cmd_name, '-D', '-x', 'checkconf', '-c', self._confdir_path, run_as_root=False, ) except ProcessExecutionError as e: LOG.error("Command output: %(out)r Stderr: %(err)r", { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend("Configuration check failed")
def test_execute_with_rootwrap(self, mock_execute): CONF.set_override('root_helper', 'sudo designate-test') mock_execute.return_value = ('designate.conf\npools.yaml\n', '') out, err = utils.execute('/bin/ls', '/etc/designate/', run_as_root=True) mock_execute.assert_called_once_with('/bin/ls', '/etc/designate/', root_helper='sudo designate-test', run_as_root=True) self.assertEqual('designate.conf\npools.yaml\n', out) self.assertFalse(err)
def _perform_axfr_from_minidns(self, zone_name): """Instruct axfr-get to request an AXFR from MiniDNS. :raises: exceptions.Backend on error """ zone_fn = self._datafiles_path_tpl % zone_name zone_tmp_fn = self._datafiles_tmp_path_tpl % zone_name # Perform AXFR, create or update a zone datafile # No need to lock globally here. # Axfr-get creates the datafile atomically by doing rename mdns_hostname, mdns_port = random.choice(self._masters) with lockutils.lock("%s.lock" % zone_name): LOG.debug("writing to %s", zone_fn) cmd = ( self._tcpclient_cmd_name, mdns_hostname, "%d" % mdns_port, self._axfr_get_cmd_name, zone_name, zone_fn, zone_tmp_fn ) LOG.debug("Executing AXFR as %r", ' '.join(cmd)) try: out, err = execute(*cmd) except ProcessExecutionError as e: LOG.error("Error executing AXFR as %r", ' '.join(cmd)) LOG.error("Command output: %(out)r Stderr: %(err)r", { 'out': e.stdout, 'err': e.stderr }) raise exceptions.Backend(str(e)) finally: try: os.remove(zone_tmp_fn) except OSError: pass
def _reload_dnsmasq(self): """ Send HUP to dnsmasq """ # TODO(Andrey): Lets be a little more targetted that every dnsmasq # instance utils.execute('killall', '-HUP', 'dnsmasq')
def _sync_domain(self, domain, new_domain_flag=False): """ Sync a single domain's zone file and reload bind config """ LOG.debug('Synchronising Domain: %s' % domain['id']) servers = self.central_service.find_servers(self.admin_context) recordsets = self.central_service.find_recordsets( self.admin_context, {'domain_id': domain['id']}) records = [] for recordset in recordsets: criterion = { 'domain_id': domain['id'], 'recordset_id': recordset['id'] } raw_records = self.central_service.find_records( self.admin_context, criterion) for record in raw_records: records.append({ 'name': recordset['name'], 'type': recordset['type'], 'ttl': recordset['ttl'], 'priority': record['priority'], 'data': record['data'], }) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join(output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) utils.render_template_to_file('bind9-zone.jinja2', output_path, servers=servers, domain=domain, records=records) rndc_call = self._rndc_base() if new_domain_flag: rndc_op = [ 'addzone', '%s { type master; file "%s"; };' % (domain['name'], output_path), ] rndc_call.extend(rndc_op) else: rndc_op = 'reload' rndc_call.extend([rndc_op]) rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) nzf_name = glob.glob('%s/*.nzf' % cfg.CONF[self.name].nzf_path) output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _sync_domain(self, domain, new_domain_flag=False): """ Sync a single domain's zone file and reload bind config """ LOG.debug('Synchronising Domain: %s' % domain['id']) servers = self.central_service.find_servers(self.admin_context) recordsets = self.central_service.find_recordsets( self.admin_context, {'domain_id': domain['id']}) records = [] for recordset in recordsets: criterion = { 'domain_id': domain['id'], 'recordset_id': recordset['id'] } raw_records = self.central_service.find_records( self.admin_context, criterion) for record in raw_records: records.append({ 'name': recordset['name'], 'type': recordset['type'], 'ttl': recordset['ttl'], 'priority': record['priority'], 'data': record['data'], }) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_path = os.path.join( output_folder, '%s.zone' % "_".join([domain['name'], domain['id']])) utils.render_template_to_file('bind9-zone.jinja2', output_path, servers=servers, domain=domain, records=records) rndc_call = self._rndc_base() if new_domain_flag: rndc_op = [ 'addzone', '%s { type master; file "%s"; };' % (domain['name'], output_path), ] rndc_call.extend(rndc_op) else: rndc_op = 'reload' rndc_call.extend([rndc_op]) rndc_call.extend([domain['name']]) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) nzf_name = glob.glob('%s/*.nzf' % cfg.CONF[self.name].nzf_path) output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)
def _sync_domain(self, domain, new_domain_flag=False): """Sync a single domain's zone file and reload bind config""" # NOTE: Only one thread should be working with the Zonefile at a given # time. The sleep(1) below introduces a not insignificant risk # of more than 1 thread working with a zonefile at a given time. with lockutils.lock('bind9-%s' % domain['id']): LOG.debug('Synchronising Domain: %s' % domain['id']) recordsets = self.central_service.find_recordsets( self.admin_context, {'domain_id': domain['id']}) records = [] for recordset in recordsets: criterion = { 'domain_id': domain['id'], 'recordset_id': recordset['id'] } raw_records = self.central_service.find_records( self.admin_context, criterion) for record in raw_records: records.append({ 'name': recordset['name'], 'type': recordset['type'], 'ttl': recordset['ttl'], 'data': record['data'], }) output_folder = os.path.join(os.path.abspath(cfg.CONF.state_path), 'bind9') output_name = "_".join([domain['name'], domain['id']]) output_path = os.path.join(output_folder, '%s.zone' % output_name) utils.render_template_to_file('bind9-zone.jinja2', output_path, domain=domain, records=records) rndc_call = self._rndc_base() if new_domain_flag: rndc_op = [ 'addzone', '%s { type master; file "%s"; };' % (domain['name'], output_path), ] rndc_call.extend(rndc_op) else: rndc_op = 'reload' rndc_call.extend([rndc_op]) rndc_call.extend([domain['name']]) if not new_domain_flag: # NOTE: Bind9 will only ever attempt to re-read a zonefile if # the file's timestamp has changed since the previous # reload. A one second sleep ensures we cross over a # second boundary before allowing the next change. time.sleep(1) LOG.debug('Calling RNDC with: %s' % " ".join(rndc_call)) utils.execute(*rndc_call) nzf_name = glob.glob('%s/*.nzf' % cfg.CONF[self.name].nzf_path) output_file = os.path.join(output_folder, 'zones.config') shutil.copyfile(nzf_name[0], output_file)