On 03/09/14 12:45, Martin Basti wrote:
On 03/09/14 12:27, Martin Kosek wrote:
On 09/02/2014 05:46 PM, Petr Spacek wrote:
On 25.8.2014 14:52, Martin Basti wrote:
Patches attached.

Ticket: https://fedorahosted.org/freeipa/ticket/4149

There is a bug in bind-dyndb-ldap (or worse in dirsrv), which cause the named
service is stopped after deleting zone.
Bug ticket: https://fedorahosted.org/bind-dyndb-ldap/ticket/138
Functional ACK, it works for me. It can be pushed if Python gurus are okay with
the code.
Is it safe to commit the change given that bind-dyndb-ldap still crash when "."
is removed? Wouldn't it break our CI tests?

Maybe we should wait until fixed bind-dydnb-ldap is released. Hopefully it
would be soon.

Martin

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel
It will broke tests, don't push it until bind-dyndb-ldap is fixed.
Currently I'm testing bind-dyndb-ldap related patch.

Added patches 120 and 121, which are required by DNS to work correctly.
Patches 120 and 121 add all DNS replicas to zone apex as NS, --name-server option doesn't add NS record, only changes the SOA MNAME attribute

Original and new patches attached.

--
Martin Basti

From 9ed12420bf52a2d2dab1f8cc4f1f6b1b5f86a801 Mon Sep 17 00:00:00 2001
From: Martin Basti <[email protected]>
Date: Fri, 22 Aug 2014 17:11:22 +0200
Subject: [PATCH 1/2] Fix DNS plugin to allow to add root zone

Ticket: https://fedorahosted.org/freeipa/ticket/4149
---
 ipalib/plugins/dns.py | 53 ++++++++++++++++++++++++++++++---------------------
 1 file changed, 31 insertions(+), 22 deletions(-)

diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index 24b303d8405aa3b4a6e0474e75d0e46e6949860d..9c8d09856a57f12b0ff1a52c8f0277f7abb29cdd 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -1783,17 +1783,21 @@ class DNSZoneBase(LDAPObject):
         zone = keys[-1]
         assert isinstance(zone, DNSName)
         assert zone.is_absolute()
-        zone = zone.ToASCII()
+        zone_a = zone.ToASCII()
+
+        # special case when zone is the root zone ('.')
+        if zone == DNSName.root:
+            return super(DNSZoneBase, self).get_dn(zone_a, **options)
 
         # try first relative name, a new zone has to be added as absolute
         # otherwise ObjectViolation is raised
-        zone = zone[:-1]
-        dn = super(DNSZoneBase, self).get_dn(zone, **options)
+        zone_a = zone_a[:-1]
+        dn = super(DNSZoneBase, self).get_dn(zone_a, **options)
         try:
             self.backend.get_entry(dn, [''])
         except errors.NotFound:
-            zone = u"%s." % zone
-            dn = super(DNSZoneBase, self).get_dn(zone, **options)
+            zone_a = u"%s." % zone_a
+            dn = super(DNSZoneBase, self).get_dn(zone_a, **options)
 
         return dn
 
@@ -1825,6 +1829,8 @@ class DNSZoneBase(LDAPObject):
         try:
             api.Command['permission_del'](permission_name, force=True)
         except errors.NotFound, e:
+            if zone == DNSName.root:  # special case root zone
+                raise
             # compatibility, older IPA versions which allows to create zone
             # without absolute zone name
             permission_name_rel = self.permission_name(
@@ -1988,20 +1994,21 @@ class DNSZoneBase_add_permission(LDAPQuery):
         permission_name = self.obj.permission_name(keys[-1])
 
         # compatibility with older IPA versions which allows relative zonenames
-        permission_name_rel = self.obj.permission_name(
-            keys[-1].relativize(DNSName.root)
-        )
-        try:
-            api.Object['permission'].get_dn_if_exists(permission_name_rel)
-        except errors.NotFound:
-            pass
-        else:
-            # permission exists without absolute domain name
-            raise errors.DuplicateEntry(
-                message=_('permission "%(value)s" already exists') % {
-                        'value': permission_name
-                }
+        if keys[-1] != DNSName.root:  # special case root zone
+            permission_name_rel = self.obj.permission_name(
+                keys[-1].relativize(DNSName.root)
             )
+            try:
+                api.Object['permission'].get_dn_if_exists(permission_name_rel)
+            except errors.NotFound:
+                pass
+            else:
+                # permission exists without absolute domain name
+                raise errors.DuplicateEntry(
+                    message=_('permission "%(value)s" already exists') % {
+                            'value': permission_name
+                    }
+                )
 
         permission = api.Command['permission_add_noaci'](permission_name,
                          ipapermissiontype=u'SYSTEM'
@@ -2417,12 +2424,14 @@ class dnszone_add(DNSZoneBase_add):
                                nameserver_ip_address)
 
         # Add entry to realmdomains
-        # except for our own domain, forwarded zones and reverse zones
+        # except for our own domain, forward zones, reverse zones and root zone
         zone = keys[0]
 
         if (zone != DNSName(api.env.domain).make_absolute()
             and not options.get('idnsforwarders')
-            and not zone.is_reverse()):
+            and not zone.is_reverse()
+            and zone != DNSName.root
+        ):
             try:
                 api.Command['realmdomains_mod'](add_domain=unicode(zone),
                                                 force=True)
@@ -2444,11 +2453,11 @@ class dnszone_del(DNSZoneBase_del):
         super(dnszone_del, self).post_callback(ldap, dn, *keys, **options)
 
         # Delete entry from realmdomains
-        # except for our own domain
+        # except for our own domain, and root zone
         zone = keys[0].make_absolute()
 
         if (zone != DNSName(api.env.domain).make_absolute() and
-                not zone.is_reverse()
+                not zone.is_reverse() and zone != DNSName.root
         ):
             try:
                 api.Command['realmdomains_mod'](del_domain=unicode(zone),
-- 
1.8.3.1

From 1e9150d63da3c5c7f1d82ce722e85449a9915661 Mon Sep 17 00:00:00 2001
From: Martin Basti <[email protected]>
Date: Mon, 25 Aug 2014 12:48:32 +0200
Subject: [PATCH 2/2] DNS test: allow '.' as zone name

https://fedorahosted.org/freeipa/ticket/4149
---
 ipatests/test_xmlrpc/test_dns_plugin.py | 103 ++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
index 4085871ec4ebe2e03e6fee2460f159da2c3bf855..9eca2c3697e40bd42f86231c39c2e80e841d9864 100644
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
@@ -3697,3 +3697,106 @@ class test_dns(Declarative):
         ),
 
     ]
+
+
+zone_root = u'.'
+zone_root_dnsname = DNSName(zone_root)
+zone_root_ip = u'172.16.29.222'
+zone_root_dn = DN(('idnsname',zone_root), api.env.container_dns, api.env.basedn)
+zone_root_ns = u'ns'
+zone_root_ns_dnsname = DNSName(zone_root_ns)
+zone_root_ns_dn = DN(('idnsname', zone_root_ns), zone_root_dn)
+zone_root_rname = u'root.example.com.'
+zone_root_rname_dnsname = DNSName(zone_root_rname)
+zone_root_permission = u'Manage DNS zone %s' % zone_root
+zone_root_permission_dn = DN(('cn',zone_root_permission),
+                            api.env.container_permission,api.env.basedn)
+
+class test_root_zone(Declarative):
+
+    @classmethod
+    def setUpClass(cls):
+        super(test_root_zone, cls).setUpClass()
+
+        if not api.Backend.rpcclient.isconnected():
+            api.Backend.rpcclient.connect(fallback=False)
+        try:
+            api.Command['dnszone_add'](zone1,
+               idnssoamname = zone1_ns,
+               idnssoarname = zone1_rname,
+               force = True,
+               )
+            api.Command['dnszone_del'](zone1)
+        except errors.NotFound:
+            raise nose.SkipTest('DNS is not configured')
+        except errors.DuplicateEntry:
+            pass
+
+    cleanup_commands = [
+        ('dnszone_del', [zone_root, ],
+            {'continue': True}),
+        ('permission_del', [zone_root_permission, ], {'force': True}
+        ),
+    ]
+
+    tests = [
+
+        dict(
+            desc='Create zone %r' % zone_root,
+            command=(
+                'dnszone_add', [zone_root], {
+                    'idnssoamname': zone_root_ns,
+                    'idnssoarname': zone_root_rname,
+                    'ip_address' : zone_root_ip,
+                }
+            ),
+            expected={
+                'value': zone_root_dnsname,
+                'summary': None,
+                'result': {
+                    'dn': zone_root_dn,
+                    'idnsname': [zone_root_dnsname],
+                    'idnszoneactive': [u'TRUE'],
+                    'idnssoamname': [zone_root_ns_dnsname],
+                    'nsrecord': [zone_root_ns],
+                    'idnssoarname': [zone_root_rname_dnsname],
+                    'idnssoaserial': [fuzzy_digits],
+                    'idnssoarefresh': [fuzzy_digits],
+                    'idnssoaretry': [fuzzy_digits],
+                    'idnssoaexpire': [fuzzy_digits],
+                    'idnssoaminimum': [fuzzy_digits],
+                    'idnsallowdynupdate': [u'FALSE'],
+                    'idnsupdatepolicy': [u'grant %(realm)s krb5-self * A; '
+                                         u'grant %(realm)s krb5-self * AAAA; '
+                                         u'grant %(realm)s krb5-self * SSHFP;'
+                                         % dict(realm=api.env.realm)],
+                    'idnsallowtransfer': [u'none;'],
+                    'idnsallowquery': [u'any;'],
+                    'objectclass': objectclasses.dnszone,
+                },
+            },
+        ),
+
+        dict(
+            desc='Add per-zone permission for zone %r' % zone_root,
+            command=(
+                'dnszone_add_permission', [zone_root], {}
+            ),
+            expected=dict(
+                result=True,
+                value=zone_root_permission,
+                summary=u'Added system permission "%s"' % zone_root_permission,
+            ),
+        ),
+
+        dict(
+            desc='Delete zone %r' % zone_root,
+            command=('dnszone_del', [zone_root], {}),
+            expected={
+                'value': [zone_root_dnsname],
+                'summary': u'Deleted DNS zone "%s"' % zone_root,
+                'result': {'failed': []},
+            },
+        ),
+
+    ]
-- 
1.8.3.1

From 92548ddb5e97c216f762cf2f51885ae73bb86b0d Mon Sep 17 00:00:00 2001
From: Martin Basti <[email protected]>
Date: Fri, 5 Sep 2014 16:09:59 +0200
Subject: [PATCH 1/2] Deprecation of --name-server and --ip-address option in
 DNS

Option --name-server is changing only SOA MNAME, this option has no more
effect to NS records

Option --ip-addres is just ignored

A warning message is sent after use these options

Part of ticket: https://fedorahosted.org/freeipa/ticket/4149
---
 API.txt               |   2 +-
 VERSION               |   4 +-
 ipalib/messages.py    |  22 ++++++++++
 ipalib/plugins/dns.py | 111 +++++++++++++++++---------------------------------
 4 files changed, 63 insertions(+), 76 deletions(-)

diff --git a/API.txt b/API.txt
index 4fa27559269e05088d2e8858d0e0f3b723315fc7..02848a35f12e4fb1fbd7b2f4234326e43ecce07c 100644
--- a/API.txt
+++ b/API.txt
@@ -1152,7 +1152,7 @@ option: StrEnum('idnsforwardpolicy', attribute=True, cli_name='forward_policy',
 option: Bool('idnssecinlinesigning', attribute=True, cli_name='dnssec', default=False, multivalue=False, required=False)
 option: Int('idnssoaexpire', attribute=True, autofill=True, cli_name='expire', default=1209600, maxvalue=2147483647, minvalue=0, multivalue=False, required=True)
 option: Int('idnssoaminimum', attribute=True, autofill=True, cli_name='minimum', default=3600, maxvalue=2147483647, minvalue=0, multivalue=False, required=True)
-option: DNSNameParam('idnssoamname', attribute=True, cli_name='name_server', multivalue=False, required=True)
+option: DNSNameParam('idnssoamname', attribute=True, cli_name='name_server', multivalue=False, required=False)
 option: Int('idnssoarefresh', attribute=True, autofill=True, cli_name='refresh', default=3600, maxvalue=2147483647, minvalue=0, multivalue=False, required=True)
 option: Int('idnssoaretry', attribute=True, autofill=True, cli_name='retry', default=900, maxvalue=2147483647, minvalue=0, multivalue=False, required=True)
 option: DNSNameParam('idnssoarname', attribute=True, cli_name='admin_email', multivalue=False, only_absolute=True, required=True)
diff --git a/VERSION b/VERSION
index 379ead7565f23218cea087ef7ee487a71102fa7f..17672ff0c3d7f8f4771901c9cb828f02f0b8a8f2 100644
--- a/VERSION
+++ b/VERSION
@@ -89,5 +89,5 @@ IPA_DATA_VERSION=20100614120000
 #                                                      #
 ########################################################
 IPA_API_VERSION_MAJOR=2
-IPA_API_VERSION_MINOR=102
-# Last change: pviktori - allow adding services to roles
+IPA_API_VERSION_MINOR=103
+# Last change: mbasti - make --name-server option optional
diff --git a/ipalib/messages.py b/ipalib/messages.py
index f637e5b17de816f3a88645c65f4a01179d97552c..1c226ea766e44d2872a339b14a591a4dfa06caee 100644
--- a/ipalib/messages.py
+++ b/ipalib/messages.py
@@ -157,6 +157,28 @@ class DNSSECWarning(PublicMessage):
     type = "warning"
     format = _("DNSSEC support is experimental.\n%(additional_info)s")
 
+
+class OptionDeprecatedWarning(PublicMessage):
+    """
+    **13004** Used when user uses a deprecated option
+    """
+
+    errno = 13004
+    type = "warning"
+    format = _(u"'%(option)s' option is deprecated. %(additional_info)s")
+
+
+class OptionNameServerWarning(PublicMessage):
+    """
+    **13005** Used when user uses a dnszone-add/mod --name-server option
+    """
+
+    errno = 13005
+    type = "warning"
+    format = _(u"'--name-server' is used only for setting up the SOA MNAME record.\n"
+               u"To edit NS record(s) in zone apex, use command 'dnsrecord-mod [zone] @ --ns-rec=nameserver'.")
+
+
 def iter_messages(variables, base):
     """Return a tuple with all subclasses
     """
diff --git a/ipalib/plugins/dns.py b/ipalib/plugins/dns.py
index ab79a8ef3e636be10aa10c2a22e07e3d31758cbc..d35d2be761029bc3403a69a1734d082e9cbe0f25 100644
--- a/ipalib/plugins/dns.py
+++ b/ipalib/plugins/dns.py
@@ -42,7 +42,7 @@ from ipalib import messages
 from ipalib.util import (validate_zonemgr, normalize_zonemgr,
                          get_dns_forward_zone_update_policy,
                          get_dns_reverse_zone_update_policy,
-                         get_reverse_zone_default, REVERSE_DNS_ZONES)
+                         get_reverse_zone_default, REVERSE_DNS_ZONES, normalize_zone)
 from ipapython.ipautil import valid_ip, CheckedIPAddress, is_host_resolvable
 from ipapython.dnsutil import DNSName
 
@@ -2088,6 +2088,7 @@ class dnszone(DNSZoneBase):
             cli_name='name_server',
             label=_('Authoritative nameserver'),
             doc=_('Authoritative nameserver domain name'),
+            required=False,
         ),
         DNSNameParam('idnssoarname',
             _rname_validator,
@@ -2317,6 +2318,10 @@ class dnszone(DNSZoneBase):
                     "server.")
                 ))
 
+    def _warning_name_server_option(self, result, context, **options):
+        if getattr(context, 'show_warning_nameserver_option', False):
+            messages.add_message(options['version'],
+                                 result, messages.OptionNameServerWarning())
 
 @register()
 class dnszone_add(DNSZoneBase_add):
@@ -2327,39 +2332,23 @@ class dnszone_add(DNSZoneBase_add):
              label=_('Force'),
              doc=_('Force DNS zone creation even if nameserver is not resolvable.'),
         ),
+
+        # Deprecated
+        # ip-address option is not used anymore, we have to keep it due to compability with clients older than 4.1
         Str('ip_address?', _validate_ipaddr,
-            doc=_('Add forward record for nameserver located in the created zone'),
-            label=_('Nameserver IP address'),
+            doc=_('Add forward record for nameserver located in the created zone (Deprecated)'),
+            label=_('Nameserver IP address (Deprecated)'),
+            flags = ['no_option', ]
         ),
     )
 
-    def interactive_prompt_callback(self, kw):
-        """
-        Interactive mode should prompt for nameserver IP address only if all
-        of the following conditions are true:
-        * New zone is a forward zone
-        * NS is defined inside the new zone (NS can be given either in the
-          form of absolute or relative name)
-        """
-        if kw.get('ip_address', None):
-            return
-
-        try:
-            zone = DNSName(kw['idnsname']).make_absolute()
-        except Exception, e:
-            raise errors.ValidationError(name='idnsname', error=unicode(e))
-
-        try:
-            ns = DNSName(kw['idnssoamname'])
-        except Exception, e:
-            raise errors.ValidationError(name='idnssoamname', error=unicode(e))
-
-        relative_ns = not ns.is_absolute()
-        ns_in_zone = self.obj.get_name_in_zone(zone, ns)
-
-        if not zone.is_reverse() and (relative_ns or ns_in_zone):
-            ip_address = self.Backend.textui.prompt(_(u'Nameserver IP address'))
-            kw['ip_address'] = ip_address
+    def _warning_deprecated_option(self, result, **options):
+        if 'ip_address' in options:
+            messages.add_message(
+                options['version'],
+                result,
+                messages.OptionDeprecatedWarning(option='ip-address', additional_info=u"Value will be ignored.")
+            )
 
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
@@ -2367,61 +2356,36 @@ class dnszone_add(DNSZoneBase_add):
         dn = super(dnszone_add, self).pre_callback(ldap, dn, entry_attrs,
             attrs_list, *keys, **options)
 
-        # Check nameserver has a forward record
-        nameserver = entry_attrs['idnssoamname']
+        nameservers = [normalize_zone(x) for x in api.Object.dnsrecord.get_dns_masters()]
+        server = normalize_zone(api.env.host)
 
-        # NS record must contain domain name
-        if valid_ip(nameserver):
-            raise errors.ValidationError(name='name-server',
-                    error=_("Nameserver address is not a domain name"))
-
-        nameserver_ip_address = options.get('ip_address')
-        zone = keys[-1]
-        if nameserver.is_absolute():
-            record_in_zone = self.obj.get_name_in_zone(keys[-1], nameserver)
+        if not entry_attrs.get('idnssoamname'):
+            # user didn't specify SOA mname
+            if server in nameservers:
+                # current ipa server is authoritative nameserver in SOA record
+                entry_attrs['idnssoamname'] = [server]
+            else:
+                # a DNS capable server is authoritative nameserver in SOA record
+                entry_attrs['idnssoamname'] = [nameservers[0]]
         else:
-            record_in_zone = nameserver
+            # show warning about --name-server option
+            context.show_warning_nameserver_option = True
 
-        if zone.is_reverse():
-            if not nameserver.is_absolute():
-                raise errors.ValidationError(name='name-server',
-                        error=_("Nameserver for reverse zone cannot be "
-                                "a relative DNS name"))
-            elif nameserver_ip_address:
-                raise errors.ValidationError(name='ip_address',
-                        error=_("Nameserver DNS record is created for "
-                                "for forward zones only"))
-        elif (nameserver_ip_address and nameserver.is_absolute() and
-              record_in_zone is None):
-            raise errors.ValidationError(name='ip_address',
-                    error=_("Nameserver DNS record is created only for "
-                            "nameservers in current zone"))
+        # all ipa DNS servers should be in NS zone record (as absolute domain name)
+        entry_attrs['nsrecord'] = nameservers
 
-        if not nameserver_ip_address and not options['force']:
-             check_ns_rec_resolvable(keys[0], nameserver)
-
-        entry_attrs['nsrecord'] = nameserver
-        entry_attrs['idnssoamname'] = nameserver
         return dn
 
     def execute(self, *keys, **options):
         result = super(dnszone_add, self).execute(*keys, **options)
+        self._warning_deprecated_option(result, **options)
         self.obj._warning_forwarding(result, **options)
         self.obj._warning_dnssec_experimental(result, *keys, **options)
+        self.obj._warning_name_server_option(result, context, **options)
         return result
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
         assert isinstance(dn, DN)
-        nameserver_ip_address = options.get('ip_address')
-        if nameserver_ip_address:
-            nameserver = entry_attrs['idnssoamname'][0]
-            if nameserver.is_absolute():
-                dns_record = self.obj.get_name_in_zone(keys[-1], nameserver)
-            else:
-                dns_record = nameserver
-            add_forward_record(keys[-1],
-                               dns_record,
-                               nameserver_ip_address)
 
         # Add entry to realmdomains
         # except for our own domain, forward zones, reverse zones and root zone
@@ -2484,8 +2448,8 @@ class dnszone_mod(DNSZoneBase_mod):
         if not _check_DN_objectclass(ldap, dn, self.obj.object_class):
             self.obj.handle_not_found(*keys)
         nameserver = entry_attrs.get('idnssoamname')
-        if nameserver and not nameserver.is_empty() and not options['force']:
-            check_ns_rec_resolvable(keys[0], nameserver)
+        if nameserver:
+            context.show_warning_nameserver_option = True
 
         return dn
 
@@ -2493,6 +2457,7 @@ class dnszone_mod(DNSZoneBase_mod):
         result = super(dnszone_mod, self).execute(*keys, **options)
         self.obj._warning_forwarding(result, **options)
         self.obj._warning_dnssec_experimental(result, *keys, **options)
+        self.obj._warning_name_server_option(result, context, **options)
         return result
 
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
-- 
1.8.3.1

From 5f1dd93f8ab67174d89bef90663ef06f0c6e5a57 Mon Sep 17 00:00:00 2001
From: Martin Basti <[email protected]>
Date: Fri, 12 Sep 2014 13:20:16 +0200
Subject: [PATCH 2/2] Add correct NS records during installation

All ipa-dns capable server is added to root zones as nameserver

During uninstall all NS records pointing to particular replica are
removed.

Part of ticket: https://fedorahosted.org/freeipa/ticket/4149
---
 install/tools/ipa-replica-manage  |   1 +
 ipaserver/install/bindinstance.py | 106 +++++++++++++++++++++-----------------
 2 files changed, 61 insertions(+), 46 deletions(-)

diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index aa710953a7d41348b330498727dbc57818e5ce0b..e44131ebe637f10990ecc5a81f3fb896c92d5313 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -745,6 +745,7 @@ def del_master(realm, hostname, options):
             bind = bindinstance.BindInstance()
             bind.remove_master_dns_records(hostname, realm, realm.lower())
             bind.remove_ipa_ca_dns_records(hostname, realm.lower())
+            bind.remove_server_ns_records(hostname)
     except Exception, e:
         print "Failed to cleanup %s DNS entries: %s" % (hostname, e)
         print "You may need to manually remove them from the tree"
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index dfb086f109371ae97151622e2c44db27256896db..086e0276e8ef824c2367cc35861c616f3df7f00c 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -279,11 +279,11 @@ def read_reverse_zone(default, ip_address):
 
     return normalize_zone(zone)
 
-def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_address=None,
+def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None,
        update_policy=None, force=False):
-    if zone_is_reverse(name):
-        # always normalize reverse zones
-        name = normalize_zone(name)
+
+    # always normalize zones
+    name = normalize_zone(name)
 
     if update_policy is None:
         if zone_is_reverse(name):
@@ -294,27 +294,13 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
     if zonemgr is None:
         zonemgr = 'hostmaster.%s' % name
 
-    if ns_hostname is None:
-        # automatically retrieve list of DNS masters
-        dns_masters = api.Object.dnsrecord.get_dns_masters()
-        if not dns_masters:
-            raise installutils.ScriptError(
-                "No IPA server with DNS support found!")
-        ns_main = dns_masters.pop(0)
-        ns_replicas = dns_masters
-    else:
-        ns_main = ns_hostname
-        ns_replicas = []
-    ns_main = normalize_zone(ns_main)
-
-    if ns_ip_address is not None:
-        ns_ip_address = unicode(ns_ip_address)
+    if ns_hostname:
+        ns_hostname = normalize_zone(ns_hostname)
 
     try:
         api.Command.dnszone_add(unicode(name),
-                                idnssoamname=unicode(ns_main),
+                                idnssoamname=unicode(ns_hostname),
                                 idnssoarname=unicode(zonemgr),
-                                ip_address=ns_ip_address,
                                 idnsallowdynupdate=True,
                                 idnsupdatepolicy=unicode(update_policy),
                                 idnsallowquery=u'any',
@@ -323,11 +309,6 @@ def add_zone(name, zonemgr=None, dns_backup=None, ns_hostname=None, ns_ip_addres
     except (errors.DuplicateEntry, errors.EmptyModlist):
         pass
 
-    nameservers = ns_replicas + [ns_main]
-    for hostname in nameservers:
-        hostname = normalize_zone(hostname)
-        add_ns_rr(name, hostname, dns_backup=None, force=True)
-
 def add_rr(zone, name, type, rdata, dns_backup=None, **kwargs):
     addkw = { '%srecord' % str(type.lower()) : unicode(rdata) }
     addkw.update(kwargs)
@@ -368,6 +349,9 @@ def del_fwd_rr(zone, host, ip_address):
     elif addr.version == 6:
         del_rr(zone, host, "AAAA", ip_address)
 
+def del_ns_rr(zone, name, rdata):
+    del_rr(zone, name, 'NS', rdata)
+
 def get_rr(zone, name, type):
     rectype = '%srecord' % unicode(type.lower())
     ret = api.Command.dnsrecord_find(unicode(zone), unicode(name))
@@ -533,16 +517,16 @@ class BindInstance(service.Service):
         if self.first_instance:
             self.step("adding DNS container", self.__setup_dns_container)
 
-        if dns_zone_exists(self.domain):
-            self.step("adding NS record to the zone", self.__add_self_ns)
-        else:
+        if not dns_zone_exists(self.domain):
             self.step("setting up our zone", self.__setup_zone)
+
         if self.reverse_zone is not None:
             self.step("setting up reverse zone", self.__setup_reverse_zone)
 
         self.step("setting up our own record", self.__add_self)
         if self.first_instance:
             self.step("setting up records for other masters", self.__add_others)
+        self.step("adding NS record to the zones", self.__add_self_ns)  # all zones must be created before this step
         self.step("setting up CA record", self.__add_ipa_ca_record)
 
         self.step("setting up kerberos principal", self.__setup_principal)
@@ -615,19 +599,20 @@ class BindInstance(service.Service):
         self._ldap_mod("dns.ldif", self.sub_dict)
 
     def __setup_zone(self):
-        nameserver_ip_address = self.ip_address
-        if not self.host_in_default_domain():
-            # Nameserver is in self.host_domain, no forward record added to self.domain
-            nameserver_ip_address = None
         # Always use force=True as named is not set up yet
         add_zone(self.domain, self.zonemgr, dns_backup=self.dns_backup,
-                ns_hostname=api.env.host, ns_ip_address=nameserver_ip_address,
-                force=True)
+                ns_hostname=api.env.host, force=True)
 
         add_rr(self.domain, "_kerberos", "TXT", self.realm)
 
     def __add_self_ns(self):
-        add_ns_rr(self.domain, api.env.host, self.dns_backup, force=True)
+        # add NS record to all zones
+        ns_hostname = normalize_zone(api.env.host)
+        result = api.Command.dnszone_find()
+        for zone in result['result']:
+            zone = unicode(zone['idnsname'][0])  # we need unicode due to backup
+            root_logger.debug("adding self NS to zone %s apex", zone)
+            add_ns_rr(zone, ns_hostname, self.dns_backup, force=True)
 
     def __setup_reverse_zone(self):
         # Always use force=True as named is not set up yet
@@ -666,14 +651,8 @@ class BindInstance(service.Service):
                     zone, self.domain))
             root_logger.debug("Add DNS zone for host first.")
 
-            if normalize_zone(zone) == normalize_zone(self.host_domain):
-                ns_ip_address = self.ip_address
-            else:
-                ns_ip_address = None
-
             add_zone(zone, self.zonemgr, dns_backup=self.dns_backup,
-                     ns_hostname=self.fqdn, ns_ip_address=ns_ip_address,
-                     force=True)
+                     ns_hostname=self.fqdn, force=True)
 
         # Add forward and reverse records to self
         for addr in addrs:
@@ -921,7 +900,6 @@ class BindInstance(service.Service):
             ("_kpasswd._tcp", "SRV", "0 100 464 %s" % self.host_in_rr),
             ("_kpasswd._udp", "SRV", "0 100 464 %s" % self.host_in_rr),
             ("_ntp._udp", "SRV", "0 100 123 %s" % self.host_in_rr),
-            ("@", "NS", normalize_zone(fqdn)),
         )
 
         for (record, type, rdata) in resource_records:
@@ -935,8 +913,6 @@ class BindInstance(service.Service):
             if rzone is not None:
                 record = get_reverse_record_name(rzone, rdata)
                 del_rr(rzone, record, "PTR", normalize_zone(fqdn))
-                # remove also master NS record from the reverse zone
-                del_rr(rzone, "@", "NS", normalize_zone(fqdn))
 
     def remove_ipa_ca_dns_records(self, fqdn, domain_name):
         host, zone = fqdn.split(".", 1)
@@ -948,6 +924,44 @@ class BindInstance(service.Service):
         for addr in addrs:
             del_fwd_rr(domain_name, IPA_CA_RECORD, addr)
 
+    def remove_server_ns_records(self, fqdn):
+        """
+        Remove all NS records pointing to this server
+        """
+        ns_rdata = normalize_zone(fqdn)
+        if not api.Backend.ldap2.isconnected():
+            root_logger.debug("no ldap2 connection, removing NS records pointing to %s failed", ns_rdata)
+            print "no ldap2 connection"  #TODO remove
+            return  # we can't modify records
+        ldap = api.Backend.ldap2
+
+        # find all NS records pointing to this server
+
+        search_kw = {}
+        search_kw['nsrecord'] = ns_rdata
+        attr_filter = ldap.make_filter(search_kw, rules=ldap.MATCH_ALL)
+        attributes = ['idnsname', 'objectclass']
+        dn = DN(api.env.container_dns, api.env.basedn)
+
+        entries, truncated = ldap.find_entries(attr_filter, attributes, base_dn=dn)
+
+        # remove records
+        if entries:
+            root_logger.debug("Removing all NS records pointing to %s:", ns_rdata)
+
+        for entry in entries:
+            print "entry", repr(entry)
+            if 'idnszone' in entry['objectclass']:
+                # zone record
+                zone = entry.single_value['idnsname']
+                root_logger.debug("zone record %s", zone)
+                del_ns_rr(zone, u'@', ns_rdata)
+            else:
+                zone = entry.dn[1].value  # get zone from DN
+                record = entry.single_value['idnsname']
+                root_logger.debug("record %s in zone %s", record, zone)
+                del_ns_rr(zone, record, ns_rdata)
+
     def check_global_configuration(self):
         """
         Check global DNS configuration in LDAP server and inform user when it
-- 
1.8.3.1

_______________________________________________
Freeipa-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to