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 <mko...@redhat.com>
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
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to