Due to DNS plugin architecture --setattr and --addattr options are not
available. DNS plugin does things in its own way and is based on
LDAPQuery base class which cannot use --setattr and --addattr options.

Hopefully the WebUI will be able to deal with it.

---

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 3f9474e1b8cf7bb2acff83c15e640badf013d648 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Tue, 12 Jul 2011 09:11:00 +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
---
 API.txt                              |   44 ++++++++++++
 VERSION                              |    2 +-
 ipalib/plugins/dns.py                |  127 +++++++++++++++++++++++-----------
 tests/test_xmlrpc/test_dns_plugin.py |   48 ++++++++++++-
 4 files changed, 176 insertions(+), 45 deletions(-)

diff --git a/API.txt b/API.txt
index 15970a043058b484c30ca9d21b2522a684c4bdcf..e25c8507e2a668feb60000f77dd052b19f4d2d5e 100644
--- a/API.txt
+++ b/API.txt
@@ -681,6 +681,50 @@ output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly
 output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
 output: Output('count', <type 'int'>, 'Number of entries returned')
 output: Output('truncated', <type 'bool'>, 'True if not all results were returned')
+command: dnsrecord_mod
+args: 2,37,3
+arg: Str('dnszoneidnsname', cli_name='dnszone', label=Gettext('Zone name', domain='ipa', localedir=None), query=True, required=True)
+arg: Str('idnsname', attribute=True, cli_name='name', label=Gettext('Record name', domain='ipa', localedir=None), multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui', flags=['no_output'])
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui', flags=['no_output'])
+option: Str('version?', exclude='webui', flags=['no_option', 'no_output'])
+option: List('arecord?', _validate_ipaddr, attribute=True, cli_name='a_rec', label='A record', multivalue=True)
+option: List('aaaarecord?', _validate_ipaddr, attribute=True, cli_name='aaaa_rec', label='AAAA record', multivalue=True)
+option: List('a6record?', attribute=True, cli_name='a6_rec', label='A6 record', multivalue=True)
+option: List('afsdbrecord?', attribute=True, cli_name='afsdb_rec', label='AFSDB record', multivalue=True)
+option: List('aplrecord?', _validate_ipnet, attribute=True, cli_name='apl_rec', label='APL record', multivalue=True)
+option: List('certrecord?', attribute=True, cli_name='cert_rec', label='CERT record', multivalue=True)
+option: List('cnamerecord?', attribute=True, cli_name='cname_rec', label='CNAME record', multivalue=True)
+option: List('dhcidrecord?', attribute=True, cli_name='dhcid_rec', label='DHCID record', multivalue=True)
+option: List('dlvrecord?', attribute=True, cli_name='dlv_rec', label='DLV record', multivalue=True)
+option: List('dnamerecord?', attribute=True, cli_name='dname_rec', label='DNAME record', multivalue=True)
+option: List('dnskeyrecord?', attribute=True, cli_name='dnskey_rec', label='DNSKEY record', multivalue=True)
+option: List('dsrecord?', attribute=True, cli_name='ds_rec', label='DS record', multivalue=True)
+option: List('hiprecord?', attribute=True, cli_name='hip_rec', label='HIP record', multivalue=True)
+option: List('ipseckeyrecord?', attribute=True, cli_name='ipseckey_rec', label='IPSECKEY record', multivalue=True)
+option: List('keyrecord?', attribute=True, cli_name='key_rec', label='KEY record', multivalue=True)
+option: List('kxrecord?', attribute=True, cli_name='kx_rec', label='KX record', multivalue=True)
+option: List('locrecord?', attribute=True, cli_name='loc_rec', label='LOC record', multivalue=True)
+option: List('mxrecord?', _validate_mx, attribute=True, cli_name='mx_rec', label='MX record', multivalue=True)
+option: List('naptrrecord?', _validate_naptr, attribute=True, cli_name='naptr_rec', label='NAPTR record', multivalue=True)
+option: List('nsrecord?', attribute=True, cli_name='ns_rec', label='NS record', multivalue=True)
+option: List('nsecrecord?', attribute=True, cli_name='nsec_rec', label='NSEC record', multivalue=True)
+option: List('nsec3record?', attribute=True, cli_name='nsec3_rec', label='NSEC3 record', multivalue=True)
+option: List('nsec3paramrecord?', attribute=True, cli_name='nsec3param_rec', label='NSEC3PARAM record', multivalue=True)
+option: List('ptrrecord?', attribute=True, cli_name='ptr_rec', label='PTR record', multivalue=True)
+option: List('rrsigrecord?', attribute=True, cli_name='rrsig_rec', label='RRSIG record', multivalue=True)
+option: List('rprecord?', attribute=True, cli_name='rp_rec', label='RP record', multivalue=True)
+option: List('sigrecord?', attribute=True, cli_name='sig_rec', label='SIG record', multivalue=True)
+option: List('spfrecord?', attribute=True, cli_name='spf_rec', label='SPF record', multivalue=True)
+option: List('srvrecord?', _validate_srv, attribute=True, cli_name='srv_rec', label='SRV record', multivalue=True)
+option: List('sshfprecord?', attribute=True, cli_name='sshfp_rec', label='SSHFP record', multivalue=True)
+option: List('tarecord?', attribute=True, cli_name='ta_rec', label='TA record', multivalue=True)
+option: List('tkeyrecord?', attribute=True, cli_name='tkey_rec', label='TKEY record', multivalue=True)
+option: List('tsigrecord?', attribute=True, cli_name='tsig_rec', label='TSIG record', multivalue=True)
+option: List('txtrecord?', attribute=True, cli_name='txt_rec', label='TXT record', multivalue=True)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), 'User-friendly description of action performed')
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, "The primary_key value of the entry, e.g. 'jdoe' for a user")
 command: dnsrecord_show
 args: 2,4,3
 arg: Str('dnszoneidnsname', cli_name='dnszone', label=Gettext('Zone name', domain='ipa', localedir=None), query=True, required=True)
diff --git a/VERSION b/VERSION
index e4f79dcb3833c502096f43be11c8f48ca9e78985..b4b7066895b7dd9f83ffdd2c7e034f197fe60825 100644
--- a/VERSION
+++ b/VERSION
@@ -79,4 +79,4 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=7
+IPA_API_VERSION_MINOR=8
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 2bba446d04169bf50ce9d3485c465034ef37ac40..c868b73b7080641f47dbad32a8575d42e5fe5f58 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.
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.6

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to