def get_domain_dnssec(cls, domain_name): """Get domain DNSSEC information.""" domain = Domain.query.filter(Domain.name == domain_name).first() if domain: headers = {} headers['X-API-Key'] = PDNS_API_KEY try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/cryptokeys' % domain.name) jdata = utils.fetch_json(url, headers=headers, method='GET') if 'error' in jdata: return { 'status': 'error', 'msg': 'DNSSEC is not enabled for this domain' } else: return {'status': 'ok', 'dnssec': jdata} except Exception: return { 'status': 'error', 'msg': 'There was something wrong gdd, please contact administrator' } else: return {'status': 'error', 'msg': 'This domain doesnot exist'}
def delete(self, domain): # , username=None): """Delete a record from domain.""" headers = {} headers['X-API-Key'] = PDNS_API_KEY data = { "rrsets": [{ "name": self.name.rstrip('.') + '.', "type": self.type, "changetype": "DELETE", "records": [], }] } try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain) jdata = utils.fetch_json(url, headers=headers, method='PATCH', data=data) LOGGING.debug(jdata) return {'status': 'ok', 'msg': 'Record was removed successfully'} except Exception: LOGGING.error("Cannot remove record %s/%s/%s from domain %s", self.name, self.type, self.data, domain) return { 'status': 'error', 'msg': 'There was something wrong in delete, please contact administrator' }
def get_domains(cls): """Get all domains which has in PowerDNS. jdata example: [ { "id": "example.org.", "url": "/servers/localhost/zones/example.org.", "name": "example.org", "kind": "Native", "dnssec": false, "account": "", "masters": [], "serial": 2015101501, "notified_serial": 0, "last_check": 0 } ] """ headers = {} headers['X-API-Key'] = PDNS_API_KEY jdata = utils.fetch_json(urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers) return jdata
def delete(cls, domain_name): """Delete a single domain name from powerdns.""" headers = {} headers['X-API-Key'] = PDNS_API_KEY try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain_name) utils.fetch_json(url, headers=headers, method='DELETE') LOGGING.info('Delete domain %s successfully', domain_name) return {'status': 'ok', 'msg': 'Delete domain successfully'} except Exception as e: tbck = traceback.format_exc() LOGGING.error('Cannot delete domain %s', domain_name) LOGGING.debug(str(e)) LOGGING.debug(str(tbck)) return {'status': 'error', 'msg': 'Cannot delete domain'}
def update_from_master(cls, domain_name): """Update records from Master DNS server.""" domain = Domain.query.filter(Domain.name == domain_name).first() if domain: headers = {} headers['X-API-Key'] = PDNS_API_KEY try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s/axfr-retrieve' % domain) utils.fetch_json(url, headers=headers, method='PUT') return { 'status': 'ok', 'msg': 'Update from Master successfully' } except Exception: return { 'status': 'error', 'msg': 'There was something wrong ufm, please contact administrator' } else: return {'status': 'error', 'msg': 'This domain doesnot exist'}
def get_statistic(self): """Get server statistics.""" headers = {} headers['X-API-Key'] = PDNS_API_KEY try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/%s/statistics' % self.server_id) jdata = utils.fetch_json(url, headers=headers, method='GET') return jdata except Exception: LOGGING.error("Can not get server statistics.") LOGGING.debug(traceback.format_exc()) return []
def add(cls, domain_name, domain_type, soa_edit_api, domain_ns=None, domain_master_ips=None): """Add a domain to power dns.""" if not domain_ns: domain_ns = [ 'dns001.den01.pop', 'dns002.den01.pop', 'dns001.iad02.pop', 'dns002.iad02.pop', 'dns001.sin01.pop', 'dns002.sin01.pop', 'dns001.ams01.pop', 'dns002.ams01.pop' ] if not domain_master_ips: domain_master_ips = [] headers = {} headers['X-API-Key'] = PDNS_API_KEY domain_name = domain_name + '.' domain_ns = [ns + '.' for ns in domain_ns] post_data = { "name": domain_name, "kind": domain_type, "masters": domain_master_ips, "nameservers": domain_ns, } if soa_edit_api != 'OFF': post_data["soa_edit_api"] = soa_edit_api try: jdata = utils.fetch_json(urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers, method='POST', data=post_data) if 'error' in jdata.keys(): LOGGING.error(jdata['error']) return {'status': 'error', 'msg': jdata['error']} else: LOGGING.info('Added domain %s successfully', domain_name) return {'status': 'ok', 'msg': 'Added domain successfully'} except Exception as err: LOGGING.error('Cannot add domain %s', domain_name) traceback.format_exc() LOGGING.debug(str(err)) return {'status': 'error', 'msg': 'Cannot add this domain.'}
def get_record_data(self, domain, fetchonly=False): """Query domain's DNS records via API.""" # if we already know the rrset id, means we need to pull it from the database when we got it before. if self.rrsetid: rrsetid = int(self.rrsetid) rrset_record = db.session.query(Rrset)\ .filter(Rrset.rrsetid == rrsetid)\ .first() return {'records': rrset_record.rrsets} headers = {} headers['X-API-Key'] = PDNS_API_KEY try: jdata = utils.fetch_json(urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain), headers=headers) except Exception: LOGGING.error( "Cannot fetch domain's record data from remote powerdns api") return False if NEW_SCHEMA and not fetchonly: rrsets = jdata['rrsets'] for rrset in rrsets: r_name = rrset['name'].rstrip('.') if PRETTY_IPV6_PTR: # only if activated if rrset['type'] == 'PTR': # only ptr if 'ip6.arpa' in r_name: # only if v6-ptr r_name = dns.reversename.to_address( dns.name.from_text(r_name)) rrset['name'] = r_name rrset['content'] = rrset['records'][0]['content'] rrset['disabled'] = rrset['records'][0]['disabled'] rrset_ = Rrset(rrsets=rrsets) db.session.add(rrset_) db.session.commit() self.rrsetid = rrset_.rrsetid return {'records': rrsets} return jdata
def update(cls): """Fetch zones (domains) from PowerDNS and update into DB.""" # pylint: disable=R0915,R0914 db_domain = Domain.query.all() list_db_domain = [d.name for d in db_domain] dict_db_domain = dict((x.name, x) for x in db_domain) headers = {} headers['X-API-Key'] = PDNS_API_KEY try: jdata = utils.fetch_json(urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones'), headers=headers) list_jdomain = [d['name'].rstrip('.') for d in jdata] try: # domains should remove from db since it doesn't exist in powerdns anymore should_removed_db_domain = list( set(list_db_domain).difference(list_jdomain)) for d in should_removed_db_domain: # revoke permission before delete domain domain = Domain.query.filter(Domain.name == d).first() domain_user = DomainUser.query.filter( DomainUser.domain_id == domain.id) if domain_user: domain_user.delete() db.session.commit() domain_setting = DomainSetting.query.filter( DomainSetting.domain_id == domain.id) if domain_setting: domain_setting.delete() db.session.commit() # then remove domain Domain.query.filter(Domain.name == d).delete() db.session.commit() except Exception: LOGGING.error('Can not delete domain from DB') LOGGING.debug(traceback.format_exc()) db.session.rollback() # update/add new domain for data in jdata: d = dict_db_domain.get(data['name'].rstrip('.'), None) changed = False if d: # existing domain, only update if something actually has changed tst1 = 1 if data['last_check'] else 0 # pylint: disable=R0916 if (d.master != str(data['masters']) or d.type != data['kind'] or d.serial != data['serial'] or d.notified_serial != data['notified_serial'] or d.last_check != tst1 or d.dnssec != data['dnssec']): d.master = str(data['masters']) d.type = data['kind'] d.serial = data['serial'] d.notified_serial = data['notified_serial'] d.last_check = 1 if data['last_check'] else 0 d.dnssec = 1 if data['dnssec'] else 0 changed = True else: # add new domain d = Domain() d.name = data['name'].rstrip('.') d.master = str(data['masters']) d.type = data['kind'] d.serial = data['serial'] d.notified_serial = data['notified_serial'] d.last_check = data['last_check'] d.dnssec = 1 if data['dnssec'] else 0 db.session.add(d) changed = True if changed: try: db.session.commit() except Exception: db.session.rollback() return { 'status': 'ok', 'msg': 'Domain table has been updated successfully' } except Exception as err: LOGGING.error('Can not update domain table. %s', str(err)) return {'status': 'error', 'msg': 'Can not update domain table'}
def update(self, domain, content, username): """Update single record.""" headers = {} headers['X-API-Key'] = PDNS_API_KEY if NEW_SCHEMA: data = { "rrsets": [{ "name": self.name, "type": self.type, "ttl": self.ttl, "changetype": "REPLACE", "records": [{ "content": content, "disabled": self.status, }] }] } else: data = { "rrsets": [{ "name": self.name, "type": self.type, "changetype": "REPLACE", "records": [{ "content": content, "disabled": self.status, "name": self.name, "ttl": self.ttl, "type": self.type, "priority": 10, }] }] } try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain) # LOGGING.debug("update dyndns data: %s", data) # LOGGING.debug("update dyndns domain: %s", domain) current = self.getcurrent_onrecord(domain) utils.fetch_json(url, headers=headers, method='PATCH', data=data) self.history_write(domain, current, data, 'REPLACE', name=self.name, created_by=username) LOGGING.debug("update dyndns data: %s", data) # LOGGING.debug("update dyndns url: %s", url) return {'status': 'ok', 'msg': 'Record was updated successfully'} except Exception as e: LOGGING.error( "Cannot add record %s/%s/%s to domain %s. DETAIL: %s", self.name, self.type, self.data, domain, str(e)) return { 'status': 'error', 'msg': 'There was something wrong in update, please contact administrator' }
def apply(self, domain, post_records): """Apply record changes to domain.""" # pylint: disable=R0912,R0915,R0914 LOGGING.info('apply() domain is %s', (domain)) records = [] for r in post_records: r_name = domain if r['record_name'] in [ '@', '' ] else r['record_name'] + '.' + domain r_type = r['record_type'] if PRETTY_IPV6_PTR: # only if activated if NEW_SCHEMA: # only if new schema if r_type == 'PTR': # only ptr if ':' in r['record_name']: # dirty ipv6 check r_name = r['record_name'] record = { "name": r_name, "type": r_type, "content": r['record_data'], "disabled": True if r['record_status'] == 'Disabled' else False, "ttl": int(r['record_ttl']) if r['record_ttl'] else 3600, } records.append(record) deleted_records, new_records = self.compare(domain, records) self.records_delete = [] for r in deleted_records: r_name = r['name'].rstrip('.') + '.' if NEW_SCHEMA else r['name'] r_type = r['type'] if PRETTY_IPV6_PTR: # only if activated if NEW_SCHEMA: # only if new schema if r_type == 'PTR': # only ptr if ':' in r['name']: # dirty ipv6 check r_name = dns.reversename.from_address( r['name']).to_text() record = { "name": r_name, "type": r_type, "changetype": "DELETE", "records": [], } self.records_delete.append(record) # postdata_for_delete = {"rrsets": self.records_delete} records = [] for r in new_records: if NEW_SCHEMA: r_name = r['name'].rstrip('.') + '.' r_type = r['type'] if PRETTY_IPV6_PTR: # only if activated if r_type == 'PTR': # only ptr if ':' in r['name']: # dirty ipv6 check r_name = r['name'] record = { "name": r_name, "type": r_type, "changetype": "REPLACE", "ttl": r['ttl'], "records": [{ "content": r['content'], "disabled": r['disabled'], }] } else: # priority field for pdns 3.4.1. # https://doc.powerdns.com/md/authoritative/upgrading/ record = { "name": r['name'], "type": r['type'], "changetype": "REPLACE", "records": [{ "content": r['content'], "disabled": r['disabled'], "name": r['name'], "ttl": r['ttl'], "type": r['type'], "priority": 10, }] } records.append(record) # Adjustment to add multiple records which described in # https://github.com/ngoduykhanh/PowerDNS-Admin/issues/5#issuecomment-181637576 self.fnl_recs = [] records = sorted(records, key=lambda item: (item["name"], item["type"], item["changetype"])) for key, group in itertools.groupby( records, lambda item: (item["name"], item["type"], item["changetype"])): if NEW_SCHEMA: r_name = key[0] r_type = key[1] r_changetype = key[2] if PRETTY_IPV6_PTR: # only if activated if r_type == 'PTR': # only ptr if ':' in r_name: # dirty ipv6 check r_name = dns.reversename.from_address( r_name).to_text() new_record = { "name": r_name, "type": r_type, "changetype": r_changetype, "ttl": None, "records": [], } for item in group: temp_content = item['records'][0]['content'] temp_disabled = item['records'][0]['disabled'] if key[1] in ['MX', 'CNAME', 'SRV', 'NS']: if temp_content.strip()[-1:] != '.': temp_content += '.' if new_record['ttl'] is None: new_record['ttl'] = item['ttl'] new_record['records'].append({ "content": temp_content, "disabled": temp_disabled }) self.fnl_recs.append(new_record) else: self.fnl_recs.append({ "name": key[0], "type": key[1], "changetype": key[2], "records": [{ "content": item['records'][0]['content'], "disabled": item['records'][0]['disabled'], "name": key[0], "ttl": item['records'][0]['ttl'], "type": key[1], "priority": 10, } for item in group] }) self.final_records_limit() postdata_for_changes = {"rrsets": self.net_final} LOGGING.info('apply() postdata_for_changes is %s', (json.dumps(postdata_for_changes))) try: headers = {} headers['X-API-Key'] = PDNS_API_KEY url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain) # utils.fetch_json(url, headers=headers, method='PATCH', data=postdata_for_delete) jdata2 = utils.fetch_json(url, headers=headers, method='PATCH', data=postdata_for_changes) if 'error' in jdata2.keys(): LOGGING.error('Cannot apply record changes.') LOGGING.debug(jdata2['error']) return {'status': 'error', 'msg': jdata2['error']} else: # should this get postdata_for_changes ??? instead of the deprecated new_records, deleted_records # postdata_for_changes is final_records_limit if not re.search('in-addr.arpa', domain): self.auto_ptr(domain) LOGGING.debug("update dyndns data: %s", postdata_for_changes) LOGGING.debug("update dyndns url: %s", url) LOGGING.info('Record was applied successfully.') self.history_log_apply(domain) return { 'status': 'ok', 'msg': 'Record was applied successfully' } except Exception as error: LOGGING.error( "Cannot apply record changes to domain %s. DETAIL: %s", str(error), domain) return { 'status': 'error', 'msg': 'There was something wrong, please contact administrator' }
def add(self, domain, created_by=None): """Add a record to domains.""" # validate record first rec = self.get_record_data(domain) # pylint: disable=W0110 records = rec['records'] check = filter(lambda check: check['name'] == self.name, records) if check: # pylint: disable=E1136 rec = check[0] if rec['type'] in ('A', 'AAAA', 'CNAME'): return { 'status': 'error', 'msg': 'Record already exists with type "A", "AAAA" or "CNAME"' } # continue if the record is ready to be added headers = {} headers['X-API-Key'] = PDNS_API_KEY if NEW_SCHEMA: data = { "rrsets": [{ "name": self.name.rstrip('.') + '.', "type": self.type, "changetype": "REPLACE", "ttl": self.ttl, "records": [{ "content": self.data, "disabled": self.status, }] }] } else: data = { "rrsets": [{ "name": self.name, "type": self.type, "changetype": "REPLACE", "records": [{ "content": self.data, "disabled": self.status, "name": self.name, "ttl": self.ttl, "type": self.type }] }] } try: url = urlparse.urljoin( PDNS_STATS_URL, API_EXTENDED_URL + '/servers/localhost/zones/%s' % domain) LOGGING.debug('add data to pdns server %s', data) current = self.getcurrent_onrecord(domain) LOGGING.debug('rec.add() current "%s" "%s" "%s"', current, domain, self.name) jdata = utils.fetch_json(url, headers=headers, method='PATCH', data=data) LOGGING.debug('fetch_json result %s', jdata) self.history_write(domain, current, data['rrsets'], 'REPLACE', name=self.name, created_by=created_by) return {'status': 'ok', 'msg': 'Record was added successfully'} except Exception as e: LOGGING.error( "Cannot add record %s/%s/%s to domain %s. DETAIL: %s", self.name, self.type, self.data, domain, str(e)) return { 'status': 'error', 'msg': 'There was something wrong in Add, please contact administrator' }