summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2016-04-27 14:26:59 +0200
committerJan Cholasta <jcholast@redhat.com>2016-05-25 16:06:26 +0200
commiteb8be95043210cf3be0e07d3def1bd3439f9e540 (patch)
tree059c21e560efafa21245d36ab2799be7b6af241a
parentb6af621432a4120f1e895315259537546dd99f4d (diff)
downloadfreeipa-eb8be95043210cf3be0e07d3def1bd3439f9e540.tar.gz
freeipa-eb8be95043210cf3be0e07d3def1bd3439f9e540.tar.xz
freeipa-eb8be95043210cf3be0e07d3def1bd3439f9e540.zip
dns: do not rely on server data structures in code called on client
Replace code which references the DNSRecord and dnsrecord classes with equivalent code which uses only generic data structures. This will make it possible to move client code to ipaclient without dnsrecord bits, DNSRecord and all its subclasses. The conversion from record value to structured record can't be done on the client without DNSRecord and subclasses. Introduce a new internal command dnsrecord_split_parts to do the job on the server when necessary. https://fedorahosted.org/freeipa/ticket/4739 Reviewed-By: David Kupka <dkupka@redhat.com>
-rw-r--r--API.txt6
-rw-r--r--VERSION4
-rw-r--r--ipalib/plugins/dns.py126
3 files changed, 86 insertions, 50 deletions
diff --git a/API.txt b/API.txt
index 3c584cba0..2d02ecd8c 100644
--- a/API.txt
+++ b/API.txt
@@ -1366,6 +1366,12 @@ option: Str('version?')
output: Entry('result')
output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
output: PrimaryKey('value')
+command: dnsrecord_split_parts
+args: 2,1,1
+arg: Str('name')
+arg: Str('value')
+option: Str('version?')
+output: Output('result')
command: dnszone_add
args: 1,28,3
arg: DNSNameParam('idnsname', cli_name='name')
diff --git a/VERSION b/VERSION
index 36d3fe267..17b335280 100644
--- a/VERSION
+++ b/VERSION
@@ -90,5 +90,5 @@ IPA_DATA_VERSION=20100614120000
# #
########################################################
IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=166
-# Last change: tbabej - idviews: Add user certificate attribute to user ID overrides
+IPA_API_VERSION_MINOR=167
+# Last change: dns: do not rely on server data structures in code called on client
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 2ea803025..a613a7399 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -295,6 +295,9 @@ server:
register = Registry()
+# dnsrecord param name formats
+record_name_format = '%srecord'
+
# supported resource record types
_record_types = (
u'A', u'AAAA', u'A6', u'AFSDB', u'APL', u'CERT', u'CNAME', u'DHCID', u'DLV',
@@ -312,7 +315,8 @@ _rev_top_record_types = ('PTR', )
_zone_top_record_types = ('NS', 'MX', 'LOC', )
# attributes derived from record types
-_record_attributes = [str('%srecord' % t.lower()) for t in _record_types]
+_record_attributes = [str(record_name_format % t.lower())
+ for t in _record_types]
# Deprecated
# supported DNS classes, IN = internet, rest is almost never used
@@ -331,6 +335,14 @@ _output_permissions = (
)
+def get_record_rrtype(name):
+ match = re.match('([^_]+)record$', name)
+ if match is None:
+ return None
+
+ return match.group(1).upper()
+
+
def _rname_validator(ugettext, zonemgr):
try:
DNSName(zonemgr) # test only if it is valid domain name
@@ -665,9 +677,9 @@ def _check_DN_objectclass(ldap, dn, objectclasses):
return _check_entry_objectclass(entry, objectclasses)
-def __get_part_param(param, cmd, part, output_kw, default=None):
- name = param.part_name_format % (param.rrtype.lower(), part.name)
- label = param.part_label_format % (param.rrtype, unicode(part.label))
+def __get_part_param(cmd, part, output_kw, default=None):
+ name = part.name
+ label = unicode(part.label)
optional = not part.required
output_kw[name] = cmd.prompt_param(part,
@@ -675,33 +687,37 @@ def __get_part_param(param, cmd, part, output_kw, default=None):
label=label)
-def prompt_parts(param, cmd, mod_dnsvalue=None):
+def prompt_parts(rrtype, cmd, mod_dnsvalue=None):
mod_parts = None
if mod_dnsvalue is not None:
- mod_parts = param._get_part_values(mod_dnsvalue)
+ name = record_name_format % rrtype.lower()
+ mod_parts = cmd.api.Command.dnsrecord_split_parts(
+ name, mod_dnsvalue)['result']
user_options = {}
- if param.parts is None:
+ parts = [p for p in cmd.params if 'dnsrecord_part' in p.flags]
+ if not parts:
return user_options
- for part_id, part in enumerate(param.parts):
+ for part_id, part in enumerate(parts):
if mod_parts:
default = mod_parts[part_id]
else:
default = None
- __get_part_param(param, cmd, part, user_options, default)
+ __get_part_param(cmd, part, user_options, default)
return user_options
-def prompt_missing_parts(param, cmd, kw, prompt_optional=False):
+def prompt_missing_parts(rrtype, cmd, kw, prompt_optional=False):
user_options = {}
- if param.parts is None:
+ parts = [p for p in cmd.params if 'dnsrecord_part' in p.flags]
+ if not parts:
return user_options
- for part in param.parts:
- name = param.part_name_format % (param.rrtype.lower(), part.name)
+ for part in parts:
+ name = part.name
if name in kw:
continue
@@ -711,7 +727,7 @@ def prompt_missing_parts(param, cmd, kw, prompt_optional=False):
continue
default = part.get_default(**kw)
- __get_part_param(param, cmd, part, user_options, default)
+ __get_part_param(cmd, part, user_options, default)
return user_options
@@ -723,8 +739,10 @@ def has_cli_options(cmd, options, no_option_msg, allow_empty_attrs=False):
has_options = False
for attr in options.keys():
- if (attr in cmd.obj.params and
- not cmd.obj.params[attr].primary_key):
+ obj_params = [
+ p.name for p in cmd.params()
+ if get_record_rrtype(p.name) or 'dnsrecord_part' in p.flags]
+ if attr in obj_params:
if options[attr] or allow_empty_attrs:
has_options = True
break
@@ -741,7 +759,7 @@ def get_rrparam_from_part(cmd, part_name):
:param part_name Part parameter name
"""
try:
- param = cmd.obj.params[part_name]
+ param = cmd.params[part_name]
if not any(flag in param.flags for flag in
('dnsrecord_part', 'dnsrecord_extra')):
@@ -749,7 +767,7 @@ def get_rrparam_from_part(cmd, part_name):
# All DNS record part or extra parameters contain a name of its
# parent RR parameter in its hint attribute
- rrparam = cmd.obj.params[param.hint]
+ rrparam = cmd.params[param.hint]
except (KeyError, AttributeError):
return None
@@ -772,7 +790,7 @@ def iterate_rrparams_by_parts(cmd, kw, skip_extra=False):
if rrparam is None:
continue
- if skip_extra and 'dnsrecord_extra' in cmd.obj.params[opt].flags:
+ if skip_extra and 'dnsrecord_extra' in cmd.params[opt].flags:
continue
if rrparam.name not in processed:
@@ -812,7 +830,7 @@ class DNSRecord(Str):
raise ValueError("Unknown RR type: %s. Must be one of %s" % \
(str(self.rrtype), ", ".join(_record_types)))
if not name:
- name = "%srecord*" % self.rrtype.lower()
+ name = "%s*" % (record_name_format % self.rrtype.lower())
kw.setdefault('cli_name', '%s_rec' % self.rrtype.lower())
kw.setdefault('label', self.label_format % self.rrtype)
kw.setdefault('doc', self.doc_format % self.rrtype)
@@ -1625,8 +1643,7 @@ def __dns_record_options_iter():
yield extra
_dns_record_options = tuple(__dns_record_options_iter())
-_dns_supported_record_types = tuple(record.rrtype for record in _dns_records \
- if record.supported)
+
def check_ns_rec_resolvable(zone, name, log):
assert isinstance(zone, DNSName)
@@ -1787,8 +1804,8 @@ def _create_idn_filter(cmd, ldap, term=None, **options):
return filter
-map_names_to_records = {"%srecord" % record.rrtype.lower(): record for record
- in _dns_records if record.supported}
+map_names_to_records = {record_name_format % record.rrtype.lower(): record
+ for record in _dns_records if record.supported}
def _records_idn_postprocess(record, **options):
for attr in record.keys():
@@ -3118,7 +3135,7 @@ class dnsrecord(LDAPObject):
# dissallowed wildcard (RFC 4592 section 4)
no_wildcard_rtypes = ['DNAME', 'DS', 'NS']
if (keys[-1].is_wild() and
- any(entry_attrs.get('%srecord' % r.lower())
+ any(entry_attrs.get(record_name_format % r.lower())
for r in no_wildcard_rtypes)
):
raise errors.ValidationError(
@@ -3228,9 +3245,8 @@ class dnsrecord(LDAPObject):
return super(dnsrecord, self).get_dn(*keys, **options)
def attr_to_cli(self, attr):
- try:
- cliname = attr[:-len('record')].upper()
- except IndexError:
+ cliname = get_record_rrtype(attr)
+ if not cliname:
cliname = attr
return cliname
@@ -3348,7 +3364,7 @@ class dnsrecord(LDAPObject):
if nsrecords and not self.is_pkey_zone_record(*keys):
for r_type in _record_types:
if (r_type not in allowed_records
- and rrattrs.get('%srecord' % r_type.lower())
+ and rrattrs.get(record_name_format % r_type.lower())
):
raise errors.ValidationError(
name='nsrecord',
@@ -3379,7 +3395,6 @@ class dnsrecord(LDAPObject):
{rdtype: None} if RRset of given type is empty
{rdtype: RRset} if RRset of given type is non-empty
'''
- record_attr_suf = 'record'
ldap_rrsets = {}
if not entry_attrs:
@@ -3387,10 +3402,11 @@ class dnsrecord(LDAPObject):
return None
for attr, value in entry_attrs.items():
- if not attr.endswith(record_attr_suf):
+ rrtype = get_record_rrtype(attr)
+ if not rrtype:
continue
- rdtype = dns.rdatatype.from_text(attr[0:-len(record_attr_suf)])
+ rdtype = dns.rdatatype.from_text(rrtype)
if not value:
ldap_rrsets[rdtype] = None # RRset is empty
continue
@@ -3583,6 +3599,20 @@ class dnsrecord(LDAPObject):
@register()
+class dnsrecord_split_parts(Command):
+ NO_CLI = True
+
+ takes_args = (
+ Str('name'),
+ Str('value'),
+ )
+
+ def execute(self, name, value, *args, **options):
+ result = self.api.Object.dnsrecord.params[name]._get_part_values(value)
+ return dict(result=result)
+
+
+@register()
class dnsrecord_add(LDAPCreate):
__doc__ = _('Add new DNS resource record.')
@@ -3614,7 +3644,8 @@ class dnsrecord_add(LDAPCreate):
new_kw = {}
for rrparam in iterate_rrparams_by_parts(self, kw,
skip_extra=True):
- user_options = prompt_missing_parts(rrparam, self, kw,
+ rrtype = get_record_rrtype(rrparam.name)
+ user_options = prompt_missing_parts(rrtype, self, kw,
prompt_optional=False)
new_kw.update(user_options)
kw.update(new_kw)
@@ -3652,21 +3683,21 @@ class dnsrecord_add(LDAPCreate):
return
try:
- name = '%srecord' % rrtype.lower()
+ name = record_name_format % rrtype.lower()
param = self.params[name]
- if not isinstance(param, DNSRecord):
- raise ValueError()
-
- if not param.supported:
+ if 'no_option' in param.flags:
raise ValueError()
except (KeyError, ValueError):
- all_types = u', '.join(_dns_supported_record_types)
+ all_types = u', '.join(get_record_rrtype(p.name)
+ for p in self.params()
+ if (get_record_rrtype(p.name) and
+ 'no_option' not in p.flags))
self.Backend.textui.print_plain(_(u'Invalid or unsupported type. Allowed values are: %s') % all_types)
continue
ok = True
- user_options = prompt_parts(param, self)
+ user_options = prompt_parts(rrtype, self)
kw.update(user_options)
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
@@ -3935,7 +3966,6 @@ class dnsrecord_mod(LDAPUpdate):
# get DNS record first so that the NotFound exception is raised
# before the helper would start
dns_record = self.api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
- rec_types = [rec_type for rec_type in dns_record if rec_type in _record_attributes]
self.Backend.textui.print_plain(_("No option to modify specific record provided."))
@@ -3948,23 +3978,24 @@ class dnsrecord_mod(LDAPUpdate):
param = self.params[attr]
except KeyError:
continue
- if not isinstance(param, DNSRecord):
+ rrtype = get_record_rrtype(param.name)
+ if not rrtype:
continue
- record_params.append(param)
+ record_params.append((param, rrtype))
rec_type_content = u', '.join(dns_record[param.name])
self.Backend.textui.print_plain(u'%s: %s' % (param.label, rec_type_content))
self.Backend.textui.print_plain(u'')
# ask what records to remove
- for param in record_params:
+ for param, rrtype in record_params:
rec_values = list(dns_record[param.name])
for rec_value in dns_record[param.name]:
rec_values.remove(rec_value)
mod_value = self.Backend.textui.prompt_yesno(
_("Modify %(name)s '%(value)s'?") % dict(name=param.label, value=rec_value), default=False)
if mod_value is True:
- user_options = prompt_parts(param, self,
+ user_options = prompt_parts(rrtype, self,
mod_dnsvalue=rec_value)
kw[param.name] = [rec_value]
kw.update(user_options)
@@ -3973,7 +4004,7 @@ class dnsrecord_mod(LDAPUpdate):
self.Backend.textui.print_plain(ngettext(
u'%(count)d %(type)s record skipped. Only one value per DNS record type can be modified at one time.',
u'%(count)d %(type)s records skipped. Only one value per DNS record type can be modified at one time.',
- 0) % dict(count=len(rec_values), type=param.rrtype))
+ 0) % dict(count=len(rec_values), type=rrtype))
break
@@ -4124,7 +4155,6 @@ class dnsrecord_del(LDAPUpdate):
# get DNS record first so that the NotFound exception is raised
# before the helper would start
dns_record = self.api.Command['dnsrecord_show'](kw['dnszoneidnsname'], kw['idnsname'])['result']
- rec_types = [rec_type for rec_type in dns_record if rec_type in _record_attributes]
self.Backend.textui.print_plain(_("No option to delete specific record provided."))
user_del_all = self.Backend.textui.prompt_yesno(_("Delete all?"), default=False)
@@ -4142,7 +4172,7 @@ class dnsrecord_del(LDAPUpdate):
param = self.params[attr]
except KeyError:
continue
- if not isinstance(param, DNSRecord):
+ if not get_record_rrtype(param.name):
continue
present_params.append(param)