def __exit__(self, exc_type, exc_val, exc_tb): PDNSChangeTracker._active_change_trackers -= 1 self._manage_signals('disconnect') if exc_type: # An exception occurred inside our context, exit db transaction and dismiss pdns changes self.transaction.__exit__(exc_type, exc_val, exc_tb) return # TODO introduce two phase commit protocol changes = self._compute_changes() axfr_required = set() for change in changes: try: change.pdns_do() change.api_do() if change.axfr_required: axfr_required.add(change.domain_name) except Exception as e: self.transaction.__exit__(type(e), e, e.__traceback__) exc = ValueError( f'For changes {list(map(str, changes))}, {type(e)} occurred during {change}: {str(e)}' ) raise exc from e self.transaction.__exit__(None, None, None) for name in axfr_required: _pdns_put(NSMASTER, '/zones/%s/axfr-retrieve' % pdns_id(name)) Domain.objects.filter(name__in=axfr_required).update( published=timezone.now())
def __exit__(self, exc_type, exc_val, exc_tb): self._manage_signals('disconnect') if exc_type: # An exception occurred inside our context, exit db transaction and dismiss pdns changes self.transaction.__exit__(exc_type, exc_val, exc_tb) return # TODO introduce two phase commit protocol changes = self._compute_changes() axfr_required = set() for change in changes: try: change.pdns_do() change.api_do() if change.axfr_required: axfr_required.add(change.domain_name) except Exception as e: # TODO gather as much info as possible # see if pdns and api are possibly in an inconsistent state self.transaction.__exit__(type(e), e, e.__traceback__) raise e self.transaction.__exit__(None, None, None) for name in axfr_required: _pdns_put(NSMASTER, '/zones/%s/axfr-retrieve' % pdns_id(name)) Domain.objects.filter(name__in=axfr_required).update( published=timezone.now())
def _sync_domain(domain): created = False # Create domain on pdns if it does not exist try: PDNSChangeTracker.CreateDomain(domain_name=domain.name).pdns_do() except PDNSException as e: # Domain already exists if e.response.status_code == 409: pass else: raise e else: created = True # modifications actually merged with additions in CreateUpdateDeleteRRSets modifications = {(rrset.type, rrset.subname) for rrset in domain.rrset_set.all()} deletions = {(rrset['type'], rrset['subname']) for rrset in pdns.get_rrset_datas(domain)} - modifications deletions.discard(('SOA', '')) # do not remove SOA record # Update zone on nslord, propagate to nsmaster PDNSChangeTracker.CreateUpdateDeleteRRSets(domain.name, set(), modifications, deletions).pdns_do() pdns._pdns_put( pdns.NSMASTER, '/zones/{}/axfr-retrieve'.format(pdns.pdns_id(domain.name))) return created
def update_catalog(self, delete=False): return _pdns_patch( NSMASTER, '/zones/' + pdns_id(settings.CATALOG_ZONE), { 'rrsets': [ construct_catalog_rrset(zone=self.domain_name, delete=delete) ] })
def update_catalog(self, delete=False): content = _pdns_patch( NSMASTER, '/zones/' + pdns_id(settings.CATALOG_ZONE), { 'rrsets': [ construct_catalog_rrset(zone=self.domain_name, delete=delete) ] }) metrics.get('desecapi_pdns_catalog_updated').inc() return content
def handle(self, *args, **options): catalog_zone_id = pdns_id(settings.CATALOG_ZONE) # Fetch zones from NSLORD response = _pdns_get(NSLORD, '/zones').json() zones = {zone['name'] for zone in response} # Retrieve catalog zone serial (later reused for recreating the catalog zone, for allow for smooth rollover) try: response = _pdns_get(NSMASTER, f'/zones/{catalog_zone_id}') serial = response.json()['serial'] except PDNSException as e: if e.response.status_code == 404: serial = None else: raise e # Purge catalog zone if exists try: _pdns_delete(NSMASTER, f'/zones/{catalog_zone_id}') except PDNSException as e: if e.response.status_code != 404: raise e # Create new catalog zone rrsets = [ construct_catalog_rrset(subname='', qtype='NS', rdata='invalid.'), # as per the specification construct_catalog_rrset(subname='version', qtype='TXT', rdata='"2"'), # as per the specification *(construct_catalog_rrset(zone=zone) for zone in zones) ] data = { 'name': settings.CATALOG_ZONE + '.', 'kind': 'MASTER', 'dnssec': False, # as per the specification 'nameservers': [], 'rrsets': rrsets, } if serial is not None: data['serial'] = serial + 1 # actually, pdns does increase this as well, but let's not rely on this _pdns_post(NSMASTER, '/zones?rrsets=false', data) print(f'Aligned catalog zone ({len(zones)} member zones).')
def domain_pdns_id(self): return pdns_id(self._domain_name)