Since this is a new-feature type patch it should be pushed only to master. ------- 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
>From 9c9e193c1d76a4c51c496ec3f76d18a4a9dd2b4b Mon Sep 17 00:00:00 2001 From: Martin Kosek <[email protected]> Date: Wed, 30 Mar 2011 17:07:17 +0200 Subject: [PATCH] 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 | 95 ++++++++++++++++++++++++---------- tests/test_xmlrpc/test_dns_plugin.py | 48 ++++++++++++++++- 2 files changed, 113 insertions(+), 30 deletions(-) diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py index f58e1ae1fae170270e8d065ada42da2f898992f5..cc70413bb387852307ac262379eb075b0a9b546c 100644 --- a/ipalib/plugins/dns.py +++ b/ipalib/plugins/dns.py @@ -189,9 +189,12 @@ _record_validators = { u'NAPTR': _validate_naptr, } -def has_cli_options(entry, no_option_msg): +def has_cli_options(entry, no_option_msg, allow_empty_attr=False): entry = dict((t, entry.get(t, [])) for t in _record_attributes) - numattr = reduce(lambda x,y: x+y, + if allow_empty_attr: + 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) @@ -514,6 +517,30 @@ class dnsrecord(LDAPObject): cliname = attr return cliname + 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] + if zone.find('ip6') != -1: + zone = zone.replace('.ip6.arpa.','') + zone_len = 32 + else: + zone = zone.replace('.in-addr.arpa.','') + zone_len = 4 + + if len(addr.split('.'))+len(zone.split('.')) != zone_len: + raise errors.ValidationError(name='cn', error=unicode('IP address must have exactly '+str(zone_len)+' components')) + + return dn + api.register(dnsrecord) @@ -648,35 +675,11 @@ 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] - if zone.find('ip6') != -1: - zone = zone.replace('.ip6.arpa.','') - zone_len = 32 - else: - zone = zone.replace('.in-addr.arpa.','') - zone_len = 4 - - if len(addr.split('.'))+len(zone.split('.')) != zone_len: - raise errors.ValidationError(name='cn', error=unicode('IP address must have exactly '+str(zone_len)+' components')) - - return dn - 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 @@ -692,6 +695,42 @@ 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 + + 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) + + return dn + + def post_callback(self, keys, 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. diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py index b994a2383ddf83f31268e956e58bd6732f7ebcb1..4a149db2efbb61194a34d038ac5ddbeb140b53f2 100644 --- a/tests/test_xmlrpc/test_dns_plugin.py +++ b/tests/test_xmlrpc/test_dns_plugin.py @@ -364,7 +364,7 @@ class test_dns(Declarative): dict( - desc='Add A record to %r in zone %r' % (dnszone1, dnsres1), + desc='Add A record to %r in zone %r' % (dnsres1, dnszone1), command=('dnsrecord_add', [dnszone1, dnsres1], {'arecord': u'10.10.0.1'}), expected={ 'value': dnsres1, @@ -380,7 +380,7 @@ class test_dns(Declarative): dict( - desc='Remove A record from %r in zone %r' % (dnszone1, dnsres1), + desc='Remove A record from %r in zone %r' % (dnsres1, dnszone1), command=('dnsrecord_del', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}), expected={ 'value': dnsres1, @@ -394,6 +394,50 @@ class test_dns(Declarative): dict( + desc='Add AAAA record to %r in zone %r using dnsrecord_mod' % (dnsres1, dnszone1), + command=('dnsrecord_mod', [dnszone1, dnsres1], {'aaaarecord': u'::1'}), + expected={ + 'value': dnsres1, + 'summary': None, + 'result': { + 'idnsname': [dnsres1], + 'arecord': [u'10.10.0.1'], + 'aaaarecord': [u'::1'], + }, + }, + ), + + + dict( + desc='Modify AAAA record in %r in zone %r' % (dnsres1, dnszone1), + command=('dnsrecord_mod', [dnszone1, dnsres1], {'aaaarecord': u'ff02::1'}), + expected={ + 'value': dnsres1, + 'summary': None, + 'result': { + 'idnsname': [dnsres1], + 'arecord': [u'10.10.0.1'], + 'aaaarecord': [u'ff02::1'], + }, + }, + ), + + + dict( + desc='Remove AAAA record from %r in zone %r using dnsrecord_mod' % (dnsres1, dnszone1), + command=('dnsrecord_mod', [dnszone1, dnsres1], {'aaaarecord': u''}), + expected={ + 'value': dnsres1, + 'summary': None, + 'result': { + 'idnsname': [dnsres1], + 'arecord': [u'10.10.0.1'], + }, + }, + ), + + + dict( desc='Delete record %r in zone %r' % (dnsres1, dnszone1), command=('dnsrecord_del', [dnszone1, dnsres1], {'del_all': True }), expected={ -- 1.7.4
_______________________________________________ Freeipa-devel mailing list [email protected] https://www.redhat.com/mailman/listinfo/freeipa-devel
