Finally, there it is. :)

I redesigned the whole thing to fit the baseldap model.

Here's some example on how it's used:

# create zone 'example.com'
# ipa dnszone-add example.com --name=ns.example.com --admin=ad...@example.com

# create a resource in zone 'example.com' named 'machine1'
# (machine1.example.com) with A record 10.10.0.1
# ipa dnsres-add example.com machine1 --a-rec=10.10.0.1

# Add another A record to 'machine1' in 'example.com'
# ipa dnsres-add-record example.com machine1 --a-rec=10.10.0.2

# Remove one of the A records from 'machine1' in 'example.com'
# ipa dnsres-remove-record example.com machine1 --a-rec=10.10.0.1



The plugin is pretty complex and requires my patch number 35 to work. There is a bunch of unit tests, so hopefully it won't be too much pain to review.

You can use both dns and dns2 at the same time.

When dns2 is tested enough, it should replace the original dns plugin.

docstring (ipa help dns2) documentation will follow soon in a separate patch.

Pavel
>From defad70e665a5c126e56032bfc5ed698363eef9f Mon Sep 17 00:00:00 2001
From: Pavel Zuna <pz...@redhat.com>
Date: Mon, 8 Nov 2010 22:34:14 -0500
Subject: [PATCH] Add new version of DNS plugin: complete rework with baseldap + unit tests.

---
 ipalib/plugins/dns2.py               |  504 ++++++++++++++++++++++++++++++++++
 tests/test_xmlrpc/test_dns_plugin.py |  360 ++++++++++++++++++++++++
 2 files changed, 864 insertions(+), 0 deletions(-)
 create mode 100644 ipalib/plugins/dns2.py
 create mode 100644 tests/test_xmlrpc/test_dns_plugin.py

diff --git a/ipalib/plugins/dns2.py b/ipalib/plugins/dns2.py
new file mode 100644
index 0000000..5b36d37
--- /dev/null
+++ b/ipalib/plugins/dns2.py
@@ -0,0 +1,504 @@
+# Authors:
+#   Pavel Zuna <pz...@redhat.com>
+#
+# Copyright (C) 2010  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+import time
+
+from ipalib import api, errors, output
+from ipalib import Command
+from ipalib import Flag, Int, List, Str, StrEnum
+from ipalib.plugins.baseldap import *
+from ipalib import _, ngettext
+
+# supported resource record types
+_record_types = (
+    u'A', u'AAAA', u'A6', u'AFSDB', u'CERT', u'CNAME', u'DNAME',
+    u'DS', u'HINFO', u'KEY', u'KX', u'LOC', u'MD', u'MINFO', u'MX',
+    u'NAPTR', u'NS', u'NSEC', u'NXT', u'PTR', u'RRSIG', u'SSHFP',
+    u'SRV', u'TXT',
+)
+
+# attributes derived from record types
+_record_attributes = [str('%srecord' % t.lower()) for t in _record_types]
+
+# supported DNS classes, IN = internet, rest is almost never used
+_record_classes = (u'IN', u'CS', u'CH', u'HS')
+
+# normalizer for admin email
+def _rname_normalizer(value):
+    value = value.replace('@', '.')
+    if not value.endswith('.'):
+        value += '.'
+    return value
+
+def _create_zone_serial(**kwargs):
+    """Generate serial number for zones."""
+    return int('%s01' % time.strftime('%Y%d%m'))
+
+
+class dnszone(LDAPObject):
+    """
+    DNS Zone, container for resource records.
+    """
+    container_dn = api.env.container_dns
+    object_name = 'DNS zone'
+    object_name_plural = 'DNS zones'
+    object_class = ['top', 'idnsrecord', 'idnszone']
+    default_attributes = [
+        'idnsname', 'idnszoneactive', 'idnssoamname', 'idnssoarname',
+        'idnssoaserial', 'idnssoarefresh', 'idnssoaretry', 'idnssoaexpire',
+        'idnssoaminimum'
+    ] + _record_attributes
+    label = _('DNS zone')
+
+    takes_params = (
+        Str('idnsname',
+            cli_name='name',
+            label=_('Zone name'),
+            doc=_('Zone name (FQDN)'),
+            normalizer=lambda value: value.lower(),
+            primary_key=True,
+        ),
+        Str('idnssoamname',
+            cli_name='name_server',
+            label=_('Authoritative name server'),
+            doc=_('Authoritative name server'),
+        ),
+        Str('idnssoarname',
+            cli_name='admin_email',
+            label=_('Administrator e-mail address'),
+            doc=_('Administrator e-mail address'),
+            default_from=lambda idnsname: 'root.%s' % idnsname,
+            normalizer=_rname_normalizer,
+        ),
+        Int('idnssoaserial?',
+            cli_name='serial',
+            label=_('SOA serial'),
+            doc=_('SOA record serial number'),
+            create_default=_create_zone_serial,
+            autofill=True,
+        ),
+        Int('idnssoarefresh?',
+            cli_name='refresh',
+            label=_('SOA refresh'),
+            doc=_('SOA record refresh time'),
+            default=3600,
+            autofill=True,
+        ),
+        Int('idnssoaretry?',
+            cli_name='retry',
+            label=_('SOA retry'),
+            doc=_('SOA record retry time'),
+            default=900,
+            autofill=True,
+        ),
+        Int('idnssoaexpire?',
+            cli_name='expire',
+            label=_('SOA expire'),
+            doc=_('SOA record expire time'),
+            default=1209600,
+            autofill=True,
+        ),
+        Int('idnssoaminimum?',
+            cli_name='minimum',
+            label=_('SOA minimum'),
+            doc=_('SOA record minimum value'),
+            default=3600,
+            autofill=True,
+        ),
+        Int('idnssoamaximum?',
+            cli_name='maximum',
+            label=_('SOA maximum'),
+            doc=_('SOA record maximum value'),
+        ),
+        Int('dnsttl?',
+            cli_name='ttl',
+            label=_('SOA time to live'),
+            doc=_('SOA record time to live'),
+        ),
+        StrEnum('dnsclass?',
+            cli_name='class',
+            label=_('SOA class'),
+            doc=_('SOA record class'),
+            values=_record_classes,
+        ),
+        Str('idnsupdatepolicy?',
+            cli_name='update_policy',
+            label=_('BIND update policy'),
+            doc=_('BIND update policy'),
+        ),
+    )
+
+    def check_container_exists(self):
+        try:
+            self.backend.get_entry(self.container_dn, [])
+        except errors.NotFound:
+            raise errors.NotFound(reason=_('DNS is not configured'))
+
+api.register(dnszone)
+
+
+class dnszone_add(LDAPCreate):
+    """
+    Create new DNS zone (SOA record).
+    """
+    takes_options = (
+        Flag('idnsallowdynupdate',
+            cli_name='allow_dynupdate',
+            label=_('Dynamic update'),
+            doc=_('allow dynamic update?'),
+            attribute=True,
+        ),
+    )
+
+    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        self.obj.check_container_exists()
+        entry_attrs['idnszoneactive'] = 'TRUE'
+        entry_attrs['idnsallowdynupdate'] = str(
+            entry_attrs.get('idnsallowdynupdate', False)
+        ).upper()
+        return dn
+
+api.register(dnszone_add)
+
+
+class dnszone_del(LDAPDelete):
+    """
+    Delete DNS zone (SOA record).
+    """
+
+api.register(dnszone_del)
+
+
+class dnszone_mod(LDAPUpdate):
+    """
+    Modify DNS zone (SOA record).
+    """
+    takes_options = (
+        Flag('idnsallowdynupdate',
+            cli_name='allow_dynupdate',
+            label=_('Dynamic update'),
+            doc=_('allow dynamic update?'),
+            attribute=True,
+        ),
+    )
+
+    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        entry_attrs['idnsallowdynupdate'] = str(
+            entry_attrs.get('idnsallowdynupdate', False)
+        ).upper()
+        return dn
+
+api.register(dnszone_mod)
+
+
+class dnszone_find(LDAPSearch):
+    """
+    Search for DNS zones (SOA records).
+    """
+
+api.register(dnszone_find)
+
+
+class dnszone_show(LDAPRetrieve):
+    """
+    Display information about a DNS zone (SOA record).
+    """
+
+api.register(dnszone_show)
+
+
+class dnszone_disable(LDAPQuery):
+    """
+    Disable DNS Zone.
+    """
+    has_output = output.standard_value
+    msg_summary = _('Disabled DNS zone "%(value)s"')
+
+    def execute(self, *keys, **options):
+        ldap = self.obj.backend
+
+        dn = self.obj.get_dn(*keys, **options)
+
+        try:
+            ldap.update_entry(dn, {'idnszoneactive': 'FALSE'})
+        except errors.EmptyModlist:
+            pass
+
+        return dict(result=True, value=keys[-1])
+
+api.register(dnszone_disable)
+
+
+class dnszone_enable(LDAPQuery):
+    """
+    Enable DNS Zone.
+    """
+    has_output = output.standard_value
+    msg_summary = _('Enabled DNS zone "%(value)s"')
+
+    def execute(self, *keys, **options):
+        ldap = self.obj.backend
+
+        dn = self.obj.get_dn(*keys, **options)
+
+        try:
+            ldap.update_entry(dn, {'idnszoneactive': 'TRUE'})
+        except errors.EmptyModlist:
+            pass
+
+        return dict(result=True, value=keys[-1])
+
+api.register(dnszone_enable)
+
+
+class dnsres(LDAPObject):
+    """
+    DNS resource.
+    """
+    parent_object = 'dnszone'
+    container_dn = api.env.container_dns
+    object_name = 'DNS resource'
+    object_name_plural = 'DNS resources'
+    object_class = ['top', 'idnsrecord']
+    default_attributes = _record_attributes + ['idnsname']
+
+    label = _('DNS resource')
+
+    takes_params = (
+        Str('idnsname',
+            cli_name='resource',
+            label=_('Resource name'),
+            doc=_('Resource name'),
+            primary_key=True,
+        ),
+        Int('dnsttl?',
+            cli_name='ttl',
+            label=_('Time to live'),
+            doc=_('Time to live'),
+        ),
+        StrEnum('dnsclass?',
+            cli_name='class',
+            label=_('Class'),
+            doc=_('DNS class of records for this resource'),
+            values=_record_classes,
+        ),
+    )
+
+    def is_pkey_zone_record(*keys):
+        idnsname = keys[-1]
+        if idnsname == '@' or idnsname == ('%s.' % keys[-2]):
+            return True
+        return False
+
+    def get_dn(self, *keys, **options):
+        if self.is_pkey_zone_record(*keys):
+            return self.api.Object[self.parent_object].get_dn(*keys[:-1], **options)
+        return super(dnsres, self).get_dn(*keys, **options)
+
+api.register(dnsres)
+
+
+class dnsres_cmd_with_record_options(Command):
+    """
+    Base class for DNS resource commands with record options.
+    """
+    record_param_doc = 'comma-separated list of %s records'
+
+    def get_record_options(self):
+        for t in _record_types:
+            t = t.encode('utf-8')
+            doc = self.record_param_doc % t
+            yield List(
+                '%srecord?' % t.lower(), cli_name='%s_rec' % t.lower(), doc=doc,
+                label='%s record' % t
+            )
+
+    def record_options_2_entry(self, **options):
+        return dict((t, options.get(t, [])) for t in _record_attributes)
+
+
+class dnsres_add(LDAPCreate, dnsres_cmd_with_record_options):
+    """
+    Create new DNS resource.
+    """
+    def get_options(self):
+        for option in super(dnsres_add, self).get_options():
+            yield option
+        for option in self.get_record_options():
+            yield option
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        entry_attrs.update(self.record_options_2_entry(**options))
+        return dn
+
+api.register(dnsres_add)
+
+
+class dnsres_del(LDAPDelete):
+    """
+    Delete DNS resource.
+    """
+
+api.register(dnsres_del)
+
+
+class dnsres_mod(LDAPUpdate, dnsres_cmd_with_record_options):
+    """
+    Modify DNS resource.
+    """
+    def has_output_params(self):
+        for option in self.get_record_options():
+            yield option
+
+    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        if self.obj.is_pkey_zone_record(*keys):
+            entry_attrs[self.obj.primary_key.name] = [u'@']
+        return dn
+
+api.register(dnsres_mod)
+
+
+class dnsres_show(LDAPRetrieve, dnsres_cmd_with_record_options):
+    """
+    Display DNS resource.
+    """
+    def has_output_params(self):
+        for option in self.get_record_options():
+            yield option
+
+    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        if self.obj.is_pkey_zone_record(*keys):
+            entry_attrs[self.obj.primary_key.name] = [u'@']
+        return dn
+
+api.register(dnsres_show)
+
+
+class dnsres_find(LDAPSearch, dnsres_cmd_with_record_options):
+    """
+    Search for DNS resources.
+    """
+    def get_options(self):
+        for option in super(dnsres_find, self).get_options():
+            yield option
+        for option in self.get_record_options():
+            yield option
+
+    def pre_callback(self, ldap, filter, attrs_list, base_dn, *args, **options):
+        record_attrs = self.record_options_2_entry(**options)
+        record_filter = ldap.make_filter(record_attrs, rules=ldap.MATCH_ALL)
+        filter = ldap.combine_filters(
+            (filter, record_filter), rules=ldap.MATCH_ALL
+        )
+        return filter
+
+    def post_callback(self, ldap, entries, truncated, *args, **options):
+        parent_obj = self.api.Object[self.obj.parent_object]
+        zone_entry = parent_obj.methods.show(args[-2], all=True)['result']
+        zone_entry[self.obj.primary_key.name] = [u'@']
+        zone_dn = zone_entry['dn']
+        del zone_entry['dn']
+        entries.insert(0, (zone_dn, zone_entry))
+
+
+api.register(dnsres_find)
+
+
+class dnsres_mod_record(LDAPQuery, dnsres_cmd_with_record_options):
+    """
+    Base class for adding/removing records from DNS resources.
+    """
+    has_output = output.standard_entry
+
+    def get_options(self):
+        for option in super(dnsres_mod_record, self).get_options():
+            yield option
+        for option in self.get_record_options():
+            yield option
+
+    def execute(self, *keys, **options):
+        ldap = self.obj.backend
+
+        dn = self.obj.get_dn(*keys, **options)
+
+        entry_attrs = self.record_options_2_entry(**options)
+
+        try:
+            (dn, old_entry_attrs) = ldap.get_entry(dn, entry_attrs.keys())
+        except errors.NotFound:
+            self.obj.handle_not_found(*keys)
+
+        self.update_old_entry_callback(entry_attrs, old_entry_attrs)
+
+        try:
+            ldap.update_entry(dn, old_entry_attrs)
+        except errors.EmptyModlist:
+            pass
+
+        if options.get('all', False):
+            attrs_list = ['*']
+        else:
+            attrs_list = list(
+                set(self.obj.default_attributes + entry_attrs.keys())
+            )
+
+        try:
+            (dn, entry_attrs) = ldap.get_entry(dn, attrs_list)
+        except errors.NotFound:
+            self.obj.handle_not_found(*keys)
+
+        if self.obj.is_pkey_zone_record(*keys):
+            entry_attrs[self.obj.primary_key.name] = [u'@']
+
+        return dict(result=entry_attrs, value=keys[-1])
+
+    def update_old_entry_callback(self, entry_attrs, old_entry_attrs):
+        pass
+
+
+class dnsres_add_record(dnsres_mod_record):
+    """
+    Add records to DNS resource.
+    """
+    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, [])
+            old_entry_attrs[a] += v
+
+api.register(dnsres_add_record)
+
+
+class dnsres_remove_record(dnsres_mod_record):
+    """
+    Remove records from DNS resource.
+    """
+    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]
+            for val in v:
+                try:
+                    old_entry_attrs[a].remove(val)
+                except (KeyError, ValueError):
+                    pass
+
+api.register(dnsres_remove_record)
+
diff --git a/tests/test_xmlrpc/test_dns_plugin.py b/tests/test_xmlrpc/test_dns_plugin.py
new file mode 100644
index 0000000..cc14d94
--- /dev/null
+++ b/tests/test_xmlrpc/test_dns_plugin.py
@@ -0,0 +1,360 @@
+# Authors:
+#   Pavel Zuna <pz...@redhat.com>
+#
+# Copyright (C) 2010  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; version 2 only
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+"""
+Test the `ipalib/plugins/dns.py` module.
+"""
+
+from ipalib import api, errors
+from tests.test_xmlrpc import objectclasses
+from xmlrpc_test import Declarative, fuzzy_digits, fuzzy_uuid
+
+dnszone1 = u'dnszone.test'
+dnsres1 = u'testdnsres'
+
+class test_dns(Declarative):
+
+    cleanup_commands = [
+        ('dnszone_del', [dnszone1], {}),
+        ('dnsres_del', [dnszone1, dnsres1], {}),
+    ]
+
+    tests = [
+
+        dict(
+            desc='Try to retrieve non-existent zone %r' % dnszone1,
+            command=('dnszone_show', [dnszone1], {}),
+            expected=errors.NotFound(reason='DNS zone not found'),
+        ),
+
+
+        dict(
+            desc='Try to update non-existent zone %r' % dnszone1,
+            command=('dnszone_mod', [dnszone1], {'idnssoamname': u'foobar'}),
+            expected=errors.NotFound(reason='DNS zone not found'),
+        ),
+
+
+        dict(
+            desc='Try to delete non-existent zone %r' % dnszone1,
+            command=('dnszone_del', [dnszone1], {}),
+            expected=errors.NotFound(reason='DNS zone not found'),
+        ),
+
+
+        dict(
+            desc='Try to search for resource record in non-existent zone %r' % dnszone1,
+            command=('dnsres_find', [dnszone1, dnsres1], {}),
+            expected=errors.NotFound(reason='DNS zone not found'),
+        ),
+
+
+        dict(
+            desc='Create zone %r' % dnszone1,
+            command=(
+                'dnszone_add', [dnszone1], {
+                    'idnssoamname': u'ns1.%s' % dnszone1,
+                    'idnssoarname': u'root.%s' % dnszone1,
+                }
+            ),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': {
+                    'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [fuzzy_digits],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                    'idnsallowdynupdate': [u'FALSE'],
+                    'objectclass': [u'top', u'idnsrecord', u'idnszone'],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Try to create duplicate zone %r' % dnszone1,
+            command=(
+                'dnszone_add', [dnszone1], {
+                    'idnssoamname': u'ns1.%s' % dnszone1,
+                    'idnssoarname': u'root.%s' % dnszone1,
+                }
+            ),
+            expected=errors.DuplicateEntry(),
+        ),
+
+
+        dict(
+            desc='Retrieve zone %r' % dnszone1,
+            command=('dnszone_show', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': {
+                    'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [fuzzy_digits],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Update zone %r' % dnszone1,
+            command=('dnszone_mod', [dnszone1], {'idnssoarefresh': 5478}),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': {
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [u'5478'],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                    'idnsallowdynupdate': [u'FALSE'],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Search for zones with name server %r' % (u'ns1.%s' % dnszone1),
+            command=('dnszone_find', [], {'idnssoamname': u'ns1.%s' % dnszone1}),
+            expected={
+                'summary': None,
+                'count': 1,
+                'truncated': False,
+                'result': [{
+                    'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [u'5478'],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                }],
+            },
+        ),
+
+
+        dict(
+            desc='Disable zone %r' % dnszone1,
+            command=('dnszone_disable', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': u'Disabled DNS zone "%s"' % dnszone1,
+                'result': True,
+            },
+        ),
+
+
+        dict(
+            desc='Check if zone %r is really disabled' % dnszone1,
+            command=('dnszone_show', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': {
+                    'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'FALSE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [fuzzy_digits],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Enable zone %r' % dnszone1,
+            command=('dnszone_enable', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': u'Enabled DNS zone "%s"' % dnszone1,
+                'result': True,
+            },
+        ),
+
+
+        dict(
+            desc='Check if zone %r is really enabled' % dnszone1,
+            command=('dnszone_show', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': {
+                    'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                    'idnsname': [dnszone1],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [u'ns1.%s' % dnszone1],
+                    'idnssoarname': [u'root.%s.' % dnszone1],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [fuzzy_digits],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Try to retrieve non-existent resource %r in zone %r' % (dnsres1, dnszone1),
+            command=('dnsres_show', [dnszone1, dnsres1], {}),
+            expected=errors.NotFound(reason='DNS resource not found'),
+        ),
+
+
+        dict(
+            desc='Try to update non-existent resource %r in zone %r' % (dnsres1, dnszone1),
+            command=('dnsres_mod', [dnszone1, dnsres1], {'dnsttl': 20}),
+            expected=errors.NotFound(reason='DNS resource not found'),
+        ),
+
+
+        dict(
+            desc='Try to delete non-existent resource %r in zone %r' % (dnsres1, dnszone1),
+            command=('dnsres_del', [dnszone1, dnsres1], {}),
+            expected=errors.NotFound(reason='DNS resource not found'),
+        ),
+
+
+        dict(
+            desc='Create resource %r in zone %r' % (dnszone1, dnsres1),
+            command=('dnsres_add', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}),
+            expected={
+                'value': dnsres1,
+                'summary': None,
+                'result': {
+                    'dn': u'idnsname=%s,idnsname=%s,cn=dns,%s' % (dnsres1, dnszone1, api.env.basedn),
+                    'idnsname': [dnsres1],
+                    'objectclass': [u'top', u'idnsrecord'],
+                    'arecord': [u'127.0.0.1'],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Search for all resources in zone %r' % dnszone1,
+            command=('dnsres_find', [dnszone1], {}),
+            expected={
+                'summary': None,
+                'count': 2,
+                'truncated': False,
+                'result': [
+                    {
+                        'dn': u'idnsname=%s,cn=dns,%s' % (dnszone1, api.env.basedn),
+                        'idnsname': [u'@'],
+                        'idnszoneactive': [u'TRUE'],
+                        'idnssoamname': [u'ns1.%s' % dnszone1],
+                        'idnssoarname': [u'root.%s.' % dnszone1],
+                        'idnssoaserial': [fuzzy_digits],
+                        'idnssoarefresh': [fuzzy_digits],
+                        'idnssoaretry': [fuzzy_digits],
+                        'idnssoaexpire': [fuzzy_digits],
+                        'idnssoaminimum': [fuzzy_digits],
+                        'idnsallowdynupdate': [u'FALSE'],
+                        'objectclass': [u'top', u'idnsrecord', u'idnszone'],
+                    },
+                    {
+                        'dn': u'idnsname=%s,idnsname=%s,cn=dns,%s' % (dnsres1, dnszone1, api.env.basedn),
+                        'idnsname': [dnsres1],
+                        'arecord': [u'127.0.0.1'],
+                    },
+                ],
+            },
+        ),
+
+
+        dict(
+            desc='Add A record to %r in zone %r' % (dnszone1, dnsres1),
+            command=('dnsres_add_record', [dnszone1, dnsres1], {'arecord': u'10.10.0.1'}),
+            expected={
+                'value': dnsres1,
+                'summary': None,
+                'result': {
+                    'idnsname': [dnsres1],
+                    'arecord': [u'127.0.0.1', u'10.10.0.1'],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Remove A record from %r in zone %r' % (dnszone1, dnsres1),
+            command=('dnsres_remove_record', [dnszone1, dnsres1], {'arecord': u'127.0.0.1'}),
+            expected={
+                'value': dnsres1,
+                'summary': None,
+                'result': {
+                    'idnsname': [dnsres1],
+                    'arecord': [u'10.10.0.1'],
+                },
+            },
+        ),
+
+
+        dict(
+            desc='Delete resource %r in zone %r' % (dnsres1, dnszone1),
+            command=('dnsres_del', [dnszone1, dnsres1], {}),
+            expected={
+                'value': dnsres1,
+                'summary': None,
+                'result': True,
+            },
+        ),
+
+
+        dict(
+            desc='Delete zone %r' % dnszone1,
+            command=('dnszone_del', [dnszone1], {}),
+            expected={
+                'value': dnszone1,
+                'summary': None,
+                'result': True,
+            },
+        ),
+
+    ]
+
-- 
1.7.1.1

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

Reply via email to