def _data_to_zi(self, name, zi_data, change_by, normalize_ttls=False, admin_privilege=False, helpdesk_privilege=False): """ Construct a new ZI, RRS and comments, from zone_data. """ def set_missing_zi_data(): """ Set missing fields in supplied zi_data to prevent problems """ # Set ZI Zone ttl if not already set if 'zone_ttl' not in zi_data: zi_data['zone_ttl'] = zone_ttl # Set other SOA values in zi_data from defaults # if they are not there. soa_ttl can be None for field in ['soa_mname', 'soa_rname', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum']: if not zi_data.get(field): zi_data[field] = zone_cfg.get_row_exc(db_session, field, sg=zone_sm.sg) # We always update serial number on zone udpdate/publish # but it is nicer and probably less troublesome to replace # an existing serial number that may be out there if not zi_data.get('soa_serial'): if zone_sm.soa_serial: zi_data['soa_serial'] = zone_sm.soa_serial else: # Obviously a new zone zi_data['soa_serial'] = new_zone_soa_serial(db_session) def check_zi_data(): """ Check incoming zi_data attributes for correctness """ for field in ['soa_mname', 'soa_rname']: validate_zi_hostname(name, field, zi_data[field]) for field in ['soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'soa_ttl', 'zone_ttl']: if field == 'soa_ttl' and not zi_data.get(field): # SOA TTL can be None continue validate_zi_ttl(name, field, zi_data[field]) for field in ['soa_serial']: if field == 'soa_serial' and zi_data.get(field, None) == None: # SOA serial can be None continue # Check incoming data type of soa_serial if not isinstance(zi_data['soa_serial'], int): raise SOASerialTypeError(name) if not ( 0 < zi_data['soa_serial'] <= (2**32-1)): # RFC 2136 Section 4.2 AO serial cannot be zero raise SOASerialRangeError(name) # Function start db_session = self.db_session # Get zone_sm to get zone ID etc zone_sm = self._get_zone_sm(name) zone_id = zone_sm.id_ # initialise data and zone consistency checking data_tools = DataTools(db_session, zone_sm) # Sort out a candidate value for zone_ttl so that RRs can be created zone_ttl = zi_data.get('zone_ttl', zone_cfg.get_row_exc(db_session, 'zone_ttl', sg=zone_sm.sg)) zone_ttl_supplied = 'zone_ttl' in zi_data # Create comments, and set up comment IDs, and stuff for handlng # RR Groups zi_data structures data_tools.rr_data_create_comments(zi_data, zone_ttl) # Deal with ZI data problems, and supply defaults if missing set_missing_zi_data() check_zi_data() # This constructor call sets attributes in zi as well! zi = ZoneInstance(change_by=change_by, **zi_data) db_session.add(zi) apex_comment = data_tools.get_apex_comment() if apex_comment: zi.add_apex_comment(apex_comment) # Get zi.id_ zi.zone_id from database db_session.flush() # Add RRs to zi # Note use of lambda so that list of rrs is always refreshed in # function data_tools.add_rrs(lambda :zi.rrs, zi.add_rr, admin_privilege, helpdesk_privilege) # tie zi into data_structures zone_sm.all_zis.append(zi) zi.zone = zone_sm db_session.flush() # Normalise TTLs here if normalize_ttls and zone_ttl_supplied: zi.normalize_ttls() # Update SOA and NS records - can't hurt to do it here # This also cleans out any incoming apex NS records if # client should not be setting them. zi.update_apex(db_session) # Update Zone TTLs for clean initialisation zi.update_zone_ttls() db_session.flush() # Check zone consistency. Do this here as Apex RRs need to be complete. data_tools.check_zi_consistency(zi.rrs) return zi, data_tools.get_auto_ptr_data()
def main_process(self): """Main process editzone """ def clean_up(): if (tmp_file): os.unlink(tmp_file) tmp_file = '' # Get update session object error_str = '' try: update_session = DynDNSUpdate(settings['dns_server'], settings['dyndns_key_file'], settings['dyndns_key_name'], ) except (socket.error, DynDNSCantReadKeyError, IOError) as exc: error_str = str(exc) # Process above error... if (error_str): log_error("%s" % error_str) sys.exit(os.EX_NOHOST) # Do AXFR to obtain current zone data msg = None try: (zone, dnskey_flag, nesc3param_flag) \ = update_session.read_zone(self.zone_name) except NoSuchZoneOnServerError as exc: msg = str(exc) if msg: log_error(msg) sys.exit(os.EX_NOINPUT) # Only edit zone if not wrapping SOA serial number if (not settings['wrap_serial'] and not settings['update_serial'] and not settings['nsec3_seed'] and not settings['clear_nsec3'] and not settings['clear_dnskey']): # Write zone out to a temporary file (fd, tmp_file) = tempfile.mkstemp(prefix=settings['process_name'] + '-', suffix='.zone') os.close(fd) zone.to_file(tmp_file) # Edit zone data old_stat = os.stat(tmp_file) editor = self._get_editor() try: output = check_call([editor, tmp_file]) except CalledProcessError as exc: log_error("editor exited with '%s'." % exc.returncode) sys.exit(os.EX_SOFTWARE) new_stat = os.stat(tmp_file) if (not settings['force_update'] and old_stat[stat.ST_MTIME] == new_stat[stat.ST_MTIME] and old_stat[stat.ST_SIZE] == new_stat[stat.ST_SIZE] and old_stat[stat.ST_INO] == new_stat[stat.ST_INO]): log_info("File '%s' unchanged after editing - exiting." % tmp_file) clean_up() sys.exit(os.EX_OK) # Read in file and form zi structure zone = dns.zone.from_file(tmp_file, self.zone_name) # At the moment these values are just for the sake of it. zi = ZoneInstance(soa_refresh='5m', soa_retry='5m', soa_expire='7d', soa_minimum='600') for rdata in zone.iterate_rdatas(): zi.add_rr(dnspython_to_rr(rdata)) # Update Zone in DNS rcode, msg, soa_serial, *stuff = update_session.update_zone( self.zone_name, zi, force_soa_serial_update=not(settings['no_serial']), wrap_serial_next_time=settings['wrap_serial'], nsec3_seed=settings['nsec3_seed'], clear_nsec3=settings['clear_nsec3'], clear_dnskey=settings['clear_dnskey'] ) if rcode == RCODE_NOCHANGE: log_info(msg) sys.exit(os.EX_OK) # Delete temporary file clean_up() if rcode == RCODE_ERROR: log_warning(msg) sys.exit(os.EX_TEMPFAIL) elif rcode == RCODE_RESET: log_error(msg) sys.exit(os.EX_IOERR) elif rcode == RCODE_FATAL: log_error(msg) sys.exit(os.EX_IOERR) # Everything good - Lets GO! if (settings['verbose']): log_info(msg) else: log_debug(msg) sys.exit(os.EX_OK)
def _data_to_zi(self, name, zi_data, change_by, normalize_ttls=False, admin_privilege=False, helpdesk_privilege=False): """ Construct a new ZI, RRS and comments, from zone_data. """ def set_missing_zi_data(): """ Set missing fields in supplied zi_data to prevent problems """ # Set ZI Zone ttl if not already set if 'zone_ttl' not in zi_data: zi_data['zone_ttl'] = zone_ttl # Set other SOA values in zi_data from defaults # if they are not there. soa_ttl can be None for field in [ 'soa_mname', 'soa_rname', 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum' ]: if not zi_data.get(field): zi_data[field] = zone_cfg.get_row_exc(db_session, field, sg=zone_sm.sg) # We always update serial number on zone udpdate/publish # but it is nicer and probably less troublesome to replace # an existing serial number that may be out there if not zi_data.get('soa_serial'): if zone_sm.soa_serial: zi_data['soa_serial'] = zone_sm.soa_serial else: # Obviously a new zone zi_data['soa_serial'] = new_zone_soa_serial(db_session) def check_zi_data(): """ Check incoming zi_data attributes for correctness """ for field in ['soa_mname', 'soa_rname']: validate_zi_hostname(name, field, zi_data[field]) for field in [ 'soa_refresh', 'soa_retry', 'soa_expire', 'soa_minimum', 'soa_ttl', 'zone_ttl' ]: if field == 'soa_ttl' and not zi_data.get(field): # SOA TTL can be None continue validate_zi_ttl(name, field, zi_data[field]) for field in ['soa_serial']: if field == 'soa_serial' and zi_data.get(field, None) == None: # SOA serial can be None continue # Check incoming data type of soa_serial if not isinstance(zi_data['soa_serial'], int): raise SOASerialTypeError(name) if not (0 < zi_data['soa_serial'] <= (2**32 - 1)): # RFC 2136 Section 4.2 AO serial cannot be zero raise SOASerialRangeError(name) # Function start db_session = self.db_session # Get zone_sm to get zone ID etc zone_sm = self._get_zone_sm(name) zone_id = zone_sm.id_ # initialise data and zone consistency checking data_tools = DataTools(db_session, zone_sm) # Sort out a candidate value for zone_ttl so that RRs can be created zone_ttl = zi_data.get( 'zone_ttl', zone_cfg.get_row_exc(db_session, 'zone_ttl', sg=zone_sm.sg)) zone_ttl_supplied = 'zone_ttl' in zi_data # Create comments, and set up comment IDs, and stuff for handlng # RR Groups zi_data structures data_tools.rr_data_create_comments(zi_data, zone_ttl) # Deal with ZI data problems, and supply defaults if missing set_missing_zi_data() check_zi_data() # This constructor call sets attributes in zi as well! zi = ZoneInstance(change_by=change_by, **zi_data) db_session.add(zi) apex_comment = data_tools.get_apex_comment() if apex_comment: zi.add_apex_comment(apex_comment) # Get zi.id_ zi.zone_id from database db_session.flush() # Add RRs to zi # Note use of lambda so that list of rrs is always refreshed in # function data_tools.add_rrs(lambda: zi.rrs, zi.add_rr, admin_privilege, helpdesk_privilege) # tie zi into data_structures zone_sm.all_zis.append(zi) zi.zone = zone_sm db_session.flush() # Normalise TTLs here if normalize_ttls and zone_ttl_supplied: zi.normalize_ttls() # Update SOA and NS records - can't hurt to do it here # This also cleans out any incoming apex NS records if # client should not be setting them. zi.update_apex(db_session) # Update Zone TTLs for clean initialisation zi.update_zone_ttls() db_session.flush() # Check zone consistency. Do this here as Apex RRs need to be complete. data_tools.check_zi_consistency(zi.rrs) return zi, data_tools.get_auto_ptr_data()