Example #1
0
 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'}
Example #2
0
 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'
         }
Example #3
0
    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
Example #4
0
 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'}
Example #5
0
 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'}
Example #6
0
    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 []
Example #7
0
    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.'}
Example #8
0
    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
Example #9
0
    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'}
Example #10
0
    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'
            }
Example #11
0
    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'
            }
Example #12
0
    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'
            }