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 is_allowed_domain(domainname, current_user_id, checkrole=True): """Build a query to populate domains with user and group acls considered.""" uqry = db.session.query(User)\ .filter(User.id == current_user_id)\ .first() if uqry: if uqry.role_id == 1: LOGGING.debug('user is an admin %s', uqry.username) return True domidqry = db.session.query(Domain.id)\ .filter(Domain.name == domainname)\ .first() retval = True if domidqry: if checkrole: duqry = db.session.query(Domain.id) \ .join(DomainUser)\ .filter(DomainUser.user_id == current_user_id)\ .subquery('duqry') dgqry = query_acldomains_fromuser(current_user_id) dgqry = dgqry.subquery('dgqry') netqry = db.session.query(Domain.id) if checkrole: netqry = netqry.filter( db.or_(Domain.id.in_(dgqry), Domain.id.in_(duqry))) else: netqry = netqry.filter(Domain.id.in_(dgqry)) netqry = netqry.filter(Domain.id == domidqry.id)\ .all() retval = bool(netqry) return retval
def ldap_search(cls, searchFilter, baseDN): """Search ldap.""" searchScope = ldap.SCOPE_SUBTREE retrieveAttributes = None try: ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) ldp = ldap.initialize(LDAP_URI) ldp.set_option(ldap.OPT_REFERRALS, 0) ldp.set_option(ldap.OPT_PROTOCOL_VERSION, 3) ldp.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND) ldp.set_option(ldap.OPT_X_TLS_DEMAND, True) ldp.set_option(ldap.OPT_DEBUG_LEVEL, 255) ldp.protocol_version = ldap.VERSION3 ldp.simple_bind_s(LDAP_USERNAME, LDAP_PASSWORD) ldap_result_id = ldp.search(baseDN, searchScope, searchFilter, retrieveAttributes) result_set = [] while 1: result_type, result_data = ldp.result(ldap_result_id, 0) if result_data == []: break else: if result_type == ldap.RES_SEARCH_ENTRY: result_set.append(result_data) return result_set except ldap.LDAPError as err: LOGGING.error(err) raise Exception('Ldap error!')
def add_setting(self, setting, value): """Add a setting.""" try: self.settings.append(DomainSetting(setting=setting, value=value)) db.session.commit() return True except Exception as err: LOGGING.error('Can not create setting %s for domain %s. %s', setting, self.name, str(err)) return False
def set(self, value): """Set data to Database.""" try: self.value = value db.session.commit() return True except Exception: LOGGING.error('Unable to set DomainSetting value') LOGGING.debug(traceback.format_exc()) db.session.rollback() return False
def delete(self): """Delete a user/revoke all user privileges first.""" self.revoke_privilege() try: User.query.filter(User.username == self.username).delete() db.session.commit() return True except Exception: db.session.rollback() LOGGING.error('Cannot delete user %s from DB', self.username) return False
def getcurrent_onrecord(self, domain): """For history, we want the current status of a name.""" # self.name is required to be the name we are looking for retval = dict() LOGGING.debug('getcurrent_onrecord() domain "%s" "%s"', domain, self.name) jdata = self.get_record_data(domain, fetchonly=True) rrsets = None if 'rrsets' in jdata: rrsets = jdata['rrsets'] findme = self.name if 'records' in jdata: rrsets = jdata['records'] re_endindot = re.compile(r'\.$') findme = re_endindot.sub('', self.name) LOGGING.debug("getcurrent_onrecord findme : %s self.name %s", findme, self.name) cnter = None if rrsets: LOGGING.debug("getcurrent_onrecord iterating to find of name : %s", findme) for cnter, item in enumerate(rrsets): if item['name'] == findme: retval = item LOGGING.debug( "getcurrent_onrecord returning : %s searching for %s in %s records", pformat(retval), self.name, cnter) return retval
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 revoke_privilege(self): """Revoke all privielges from a user.""" user = User.query.filter(User.username == self.username).first() if user: user_id = user.id try: DomainUser.query.filter(DomainUser.user_id == user_id).delete() db.session.commit() return True except Exception: db.session.rollback() LOGGING.error('Cannot revoke user %s privielges.', self.username) return False return False
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 set_mainteance(cls, mode): """mode = True/False.""" mode = str(mode) maintenance = Setting.query.filter( Setting.name == 'maintenance').first() try: if maintenance: if maintenance.value != mode: maintenance.value = mode db.session.commit() return True else: s = Setting(name='maintenance', value=mode) db.session.add(s) db.session.commit() return True except Exception: LOGGING.error('Cannot set maintenance to %s', mode) LOGGING.debug(traceback.format_exc()) db.session.rollback() return False
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 set_admin(self, is_admin): """Set role for a user. is_admin == True => Administrator is_admin == False => User """ user_role_name = 'Administrator' if is_admin else 'User' role = Role.query.filter(Role.name == user_role_name).first() try: if role: user = User.query.filter( User.username == self.username).first() user.role_id = role.id db.session.commit() return True else: return False except Exception: db.session.roleback() LOGGING.error('Cannot change user role in DB') LOGGING.debug(traceback.format_exc()) return False
def grant_privielges(self, new_user_list): """Reconfigure domain_user table.""" domain_id = self.get_id_by_name(self.name) domain_user_ids = self.get_user() if new_user_list: new_user_ids = [ u.id for u in User.query.filter( User.username.in_(new_user_list)).all() ] else: new_user_ids = [] removed_ids = list(set(domain_user_ids).difference(new_user_ids)) added_ids = list(set(new_user_ids).difference(domain_user_ids)) try: for uid in removed_ids: DomainUser.query.filter(DomainUser.user_id == uid).filter( DomainUser.domain_id == domain_id).delete() db.session.commit() except Exception: db.session.rollback() LOGGING.error('Cannot revoke user privielges on domain %s', self.name) try: for uid in added_ids: du = DomainUser(domain_id, uid) db.session.add(du) db.session.commit() except Exception: db.session.rollback() LOGGING.error('Cannot grant user privielges to domain %s', self.name)
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 remove_all(cls): """Remove all history from DB.""" try: db.session.query(History).delete() db.session.commit() LOGGING.info("Removed all history") return True except Exception: db.session.rollback() LOGGING.error("Cannot remove history") LOGGING.debug(traceback.format_exc()) return False
def is_validate(self, method): """Validate user credential.""" if method == 'LOCAL': user_info = User.query.filter( User.username == self.username).first() if user_info: if user_info.password and self.check_passwd_local( user_info.password): LOGGING.info('User "%s" logged in successfully', self.username) return True LOGGING.error('User "%s" input a wrong password', self.username) return False LOGGING.warning('User "%s" does not exist', self.username) return False if method == 'LDAP': return self.check_passwd_ldap() LOGGING.error('Unsupported authentication method') return False
def set(cls, setting, value): """Set Setting.""" setting = str(setting) new_value = str(value) current_setting = Setting.query.filter(Setting.name == setting).first() try: if current_setting: current_setting.value = new_value db.session.commit() return True else: LOGGING.error('Setting %s does not exist', setting) return False except Exception: LOGGING.error('Cannot edit setting %s', setting) LOGGING.debug(traceback.format_exec()) db.session.rollback() return False
def toggle(cls, setting): """Toggle Setting.""" setting = str(setting) current_setting = Setting.query.filter(Setting.name == setting).first() try: if current_setting: if current_setting.value == "True": current_setting.value = "False" else: current_setting.value = "True" db.session.commit() return True else: LOGGING.error('Setting %s does not exist', setting) return False except Exception: LOGGING.error('Cannot toggle setting %s', setting) LOGGING.debug(traceback.format_exec()) db.session.rollback() return False
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 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 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 check_passwd_ldap(self, validate_thispass=None): """Check to see if this user will authenticate with LDAP.""" if not LDAP_TYPE: LOGGING.error('LDAP authentication is disabled') return False searchFilter = "(&(objectcategory=person)(samaccountname=%s))" % self.username if LDAP_TYPE == 'ldap': searchFilter = "(&(%s=%s)%s)" % (LDAP_USERNAMEFIELD, self.username, LDAP_FILTER) LOGGING.info('Ldap searchFilter "%s"', searchFilter) result = self.ldap_search(searchFilter, LDAP_SEARCH_BASE) if not result: LOGGING.warning('User "%s" does not exist', self.username) return False ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER) ldp = ldap.initialize(LDAP_URI) ldp.set_option(ldap.OPT_REFERRALS, 0) ldp.set_option(ldap.OPT_PROTOCOL_VERSION, 3) ldp.set_option(ldap.OPT_X_TLS, ldap.OPT_X_TLS_DEMAND) ldp.set_option(ldap.OPT_X_TLS_DEMAND, True) ldp.set_option(ldap.OPT_DEBUG_LEVEL, 255) ldp.protocol_version = ldap.VERSION3 if not validate_thispass: validate_thispass = self.password try: ldap_username = result[0][0][0] ldp.simple_bind_s(ldap_username, validate_thispass) LOGGING.info('User "%s" logged in successfully', self.username) except Exception: LOGGING.error('User "%s" input a wrong password', self.username) return False # create user if not exist in the db if not User.query.filter(User.username == self.username).first(): try: # try to get user's firstname & lastname from LDAP # this might be changed in the future self.firstname = result[0][0][1]['givenName'][0] self.lastname = result[0][0][1]['sn'][0] self.email = result[0][0][1]['mail'][0] except Exception: self.firstname = self.username self.lastname = '' # first register user will be in Administrator role self.role_id = Role.query.filter_by(name='User').first().id if User.query.count() == 0: self.role_id = Role.query.filter_by( name='Administrator').first().id self.create_user() LOGGING.info('Created user "%s" in the DB', self.username) return True
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' }