From e6c68e999358a323500aac9680c4aa8ca76a18cc Mon Sep 17 00:00:00 2001 From: Martin Kosek Date: Tue, 12 Jul 2011 09:11:00 +0200 Subject: Add DNS record modification command The DNS record plugin does not support modification of a record. One can only add A type addresses to a DNS record or remove the current ones. To actually change a DNS record value it has to be removed and then added with a desired value. This patch adds a new DNS plugin command "dnsrecord-mod" which enables user to: - modify a DNS record value (note than DNS record can hold multiple values and those will be overwritten) - remove a DNS record when an empty value is passed New tests for this new command have been added to the CLI test suite. https://fedorahosted.org/freeipa/ticket/1137 --- ipalib/plugins/dns.py | 127 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 42 deletions(-) (limited to 'ipalib/plugins') diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index 2bba446d0..c868b73b7 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -234,10 +234,13 @@ def zone_is_reverse(zone_name): return False -def has_cli_options(entry, no_option_msg): +def has_cli_options(entry, no_option_msg, allow_empty_attrs=False): entry = dict((t, entry.get(t, [])) for t in _record_attributes) - numattr = reduce(lambda x,y: x+y, - map(lambda x: len(x), [ v for v in entry.values() if v is not None ])) + if allow_empty_attrs: + numattr = len(entry) + else: + numattr = reduce(lambda x,y: x+y, + map(lambda x: len(x), [ v for v in entry.values() if v is not None ])) if numattr == 0: raise errors.OptionError(no_option_msg) return entry @@ -544,6 +547,43 @@ class dnsrecord(LDAPObject): ), ) + def _nsrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options): + if options.get('force', False): + return dn + + for ns in options['nsrecord']: + is_ns_rec_resolvable(ns) + return dn + + def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options): + components = dn.split(',',2) + addr = components[0].split('=')[1] + zone = components[1].split('=')[1] + zone_len = 0 + for valid_zone in _valid_reverse_zones: + if zone.find(valid_zone) != -1: + zone = zone.replace(valid_zone,'') + zone_name = valid_zone + zone_len = _valid_reverse_zones[valid_zone] + + if not zone_len: + allowed_zones = ', '.join(_valid_reverse_zones) + raise errors.ValidationError(name='cn', + error=unicode(_('Reverse zone for PTR record should be a sub-zone of one the following fully qualified domains: %s') % allowed_zones)) + + ip_addr_comp_count = len(addr.split('.')) + len(zone.split('.')) + if ip_addr_comp_count != zone_len: + raise errors.ValidationError(name='cn', + error=unicode(_('Reverse zone %s requires exactly %d IP address components, %d given') + % (zone_name, zone_len, ip_addr_comp_count))) + + for ptr in options['ptrrecord']: + if not ptr.endswith('.'): + raise errors.ValidationError(name='ptr-rec', + error=unicode(_('PTR record \'%s\' is not fully qualified (check traling \'.\')') % ptr)) + + return dn + def is_pkey_zone_record(self, *keys): idnsname = keys[-1] if idnsname == str(_dns_zone_record) or idnsname == ('%s.' % keys[-2]): @@ -732,43 +772,6 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options): has_cli_options(options, self.no_option_msg) return super(dnsrecord_add, self).args_options_2_entry(*keys, **options) - def _nsrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options): - if options.get('force', False): - return dn - - for ns in options['nsrecord']: - is_ns_rec_resolvable(ns) - return dn - - def _ptrrecord_pre_callback(self, ldap, dn, entry_attrs, *keys, **options): - components = dn.split(',',2) - addr = components[0].split('=')[1] - zone = components[1].split('=')[1] - zone_len = 0 - for valid_zone in _valid_reverse_zones: - if zone.find(valid_zone) != -1: - zone = zone.replace(valid_zone,'') - zone_name = valid_zone - zone_len = _valid_reverse_zones[valid_zone] - - if not zone_len: - allowed_zones = ', '.join(_valid_reverse_zones) - raise errors.ValidationError(name='cn', - error=unicode(_('Reverse zone for PTR record should be a sub-zone of one the following fully qualified domains: %s') % allowed_zones)) - - ip_addr_comp_count = len(addr.split('.')) + len(zone.split('.')) - if ip_addr_comp_count != zone_len: - raise errors.ValidationError(name='cn', - error=unicode(_('Reverse zone %s requires exactly %d IP address components, %d given') - % (zone_name, zone_len, ip_addr_comp_count))) - - for ptr in options['ptrrecord']: - if not ptr.endswith('.'): - raise errors.ValidationError(name='ptr-rec', - error=unicode(_('PTR record \'%s\' is not fully qualified (check traling \'.\')') % ptr)) - - return dn - def interactive_prompt_callback(self, kw): for param in kw.keys(): if param in _record_attributes: @@ -790,8 +793,8 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options): def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): for rtype in options: rtype_cb = '_%s_pre_callback' % rtype - if hasattr(self, rtype_cb): - dn = getattr(self, rtype_cb)(ldap, dn, entry_attrs, *keys, **options) + if hasattr(self.obj, rtype_cb): + dn = getattr(self.obj, rtype_cb)(ldap, dn, entry_attrs, *keys, **options) return dn @@ -807,6 +810,46 @@ class dnsrecord_add(LDAPCreate, dnsrecord_cmd_w_record_options): api.register(dnsrecord_add) +class dnsrecord_mod(dnsrecord_mod_record): + """ + Modify a DNS resource record. + """ + no_option_msg = 'No options to modify a specific record provided.' + + def update_old_entry_callback(self, entry_attrs, old_entry_attrs): + for (a, v) in entry_attrs.iteritems(): + if not isinstance(v, (list, tuple)): + v = [v] + old_entry_attrs.setdefault(a, []) + if v or v is None: # overwrite the old entry + old_entry_attrs[a] = v + print "DNSRECORD_MOD::update_old_entry_callback: old:", old_entry_attrs + print "DNSRECORD_MOD::update_old_entry_callback: new:", entry_attrs + + def record_options_2_entry(self, **options): + entries = dict((t, options.get(t, [])) for t in _record_attributes) + return has_cli_options(entries, self.no_option_msg, True) + + def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): + for rtype in options: + rtype_cb = '_%s_pre_callback' % rtype + if hasattr(self.obj, rtype_cb): + dn = getattr(self.obj, rtype_cb)(ldap, dn, entry_attrs, *keys, **options) + print "DNSRECORD_MOD::pre_callback: rtype_cb:", rtype_cb + + return dn + + def post_callback(self, keys, entry_attrs): + print "DNSRECORD_MOD::post_callback:", entry_attrs + if not self.obj.is_pkey_zone_record(*keys): + for a in _record_attributes: + if a in entry_attrs and entry_attrs[a]: + return + return self.obj.methods.delentry(*keys) + +api.register(dnsrecord_mod) + + class dnsrecord_delentry(LDAPDelete): """ Delete DNS record entry. -- cgit