https://fedorahosted.org/freeipa/ticket/3575

Also should fix https://bugzilla.redhat.com/show_bug.cgi?id=1128380 as installation is no longer interrupted when multiple IPs are resolved.
But it does not add the option to change the IP address during second run.

--
David Kupka
From 5785d3ed205141aee0989751cab573fae84c53b3 Mon Sep 17 00:00:00 2001
From: David Kupka <dku...@redhat.com>
Date: Mon, 25 Aug 2014 16:30:49 +0200
Subject: [PATCH] Detect and configure all usable IP addresses.

Find, verify and configure all IP addresses that can be used to reach the server
FreeIPA is being installed on. Ignore some IP address only if user specifies
subset of detected addresses using --ip-address option.
This change simplyfies FreeIPA installation on multihomed and dual-stacked servers.

https://fedorahosted.org/freeipa/ticket/3575
---
 install/tools/ipa-server-install  | 61 ++++++++++++++++++++-------
 ipaserver/install/bindinstance.py | 46 +++++++++++----------
 ipaserver/install/installutils.py | 86 +++++++++++++++++++--------------------
 3 files changed, 113 insertions(+), 80 deletions(-)

diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install
index 6e77b434a018faec36a2808626c99a54bd493908..5c05a0c6e49a3471b6547a9a627324ba78080211 100755
--- a/install/tools/ipa-server-install
+++ b/install/tools/ipa-server-install
@@ -38,6 +38,7 @@ import nss.error
 import base64
 import pwd
 import textwrap
+import re
 from optparse import OptionGroup, OptionValueError
 
 try:
@@ -176,7 +177,8 @@ def parse_options():
                                 "on their first login")
     basic_group.add_option("--hostname", dest="host_name", help="fully qualified name of server")
     basic_group.add_option("--ip-address", dest="ip_address",
-                      type="ip", ip_local=True,
+    # TODO: remove this workaround (type="ip") when #4506 is done
+                      type="str",
                       help="Master Server IP Address")
     basic_group.add_option("-N", "--no-ntp", dest="conf_ntp", action="store_false",
                       help="do not configure ntp", default=True)
@@ -348,6 +350,25 @@ def parse_options():
         parser.error("idmax (%u) cannot be smaller than idstart (%u)" %
                     (options.idmax, options.idstart))
 
+    # TODO: remove this workaround (parsing reverse zones) when #4506 is done
+    if options.reverse_zone:
+        reverse_zone = []
+        for rz in re.split(r'[\s,]+', options.reverse_zone):
+            if rz and rz not in reverse_zone:
+                reverse_zone.append(rz)
+        options.reverse_zone = reverse_zone
+
+    # TODO: remove this workaround (parsing IPs) when #4506 is done
+    if options.ip_address:
+        ip_address = []
+        for ip in re.split(r'[\s,]+', options.ip_address):
+            if ip and ip not in ip_address:
+                try:
+                    ip_address.append(CheckedIPAddress(ip, match_local=True))
+                except ValueError, e:
+                    parser.error("Invalid IP address: %s" % e)
+        options.ip_address = ip_address
+
     #Automatically disable pkinit w/ dogtag until that is supported
     options.setup_pkinit = False
 
@@ -832,11 +853,11 @@ def main():
     realm_name = ""
     host_name = ""
     domain_name = ""
-    ip_address = ""
+    ip_address = []
     master_password = ""
     dm_password = ""
     admin_password = ""
-    reverse_zone = None
+    reverse_zone = []
 
     if not options.setup_dns and not options.unattended:
         if ipautil.user_input("Do you want to configure integrated DNS (BIND)?", False):
@@ -895,11 +916,14 @@ def main():
 
     domain_name = domain_name.lower()
 
-    ip = get_server_ip_address(host_name, fstore, options.unattended, options)
-    ip_address = str(ip)
+    ip_address = get_server_ip_address(host_name, fstore, options.unattended, options)
 
-    if options.reverse_zone and not bindinstance.verify_reverse_zone(options.reverse_zone, ip):
-        sys.exit(1)
+    for ip in map(str, ip_address):
+        for rev_zone in reverse_zone:
+            if bindinstance.verify_reverse_zone(rev_zone, ip):
+                break
+            else:
+                sys.exit(1)
 
     if not options.realm_name:
         realm_name = read_realm_name(domain_name, options.unattended)
@@ -972,16 +996,23 @@ def main():
             dns_forwarders = read_dns_forwarders()
 
         if options.reverse_zone:
-            reverse_zone = bindinstance.normalize_zone(options.reverse_zone)
+            for rz in options.reverse_zone:
+                reverse_zone.append(bindinstance.normalize_zone(rz))
         elif not options.no_reverse:
             if options.unattended:
-                reverse_zone = util.get_reverse_zone_default(ip)
+                for ip in map(str, ip_address):
+                    rz = util.get_reverse_zone_default(ip)
+                    if not rz in reverse_zone:
+                        reverse_zone.append(rz)
             elif bindinstance.create_reverse():
-                reverse_zone = util.get_reverse_zone_default(ip)
-                reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip)
+                for ip in map(str, ip_address):
+                    rz = util.get_reverse_zone_default(ip)
+                    rz = bindinstance.read_reverse_zone(rz, ip)
+                    if not rz in reverse_zone:
+                        reverse_zone.append(rz)
 
-        if reverse_zone is not None:
-            print "Using reverse zone %s" % reverse_zone
+        if reverse_zone:
+            print "Using reverse zone %s" % ", ".join(map(str, reverse_zone))
     else:
         dns_forwarders = ()
     root_logger.debug("will use dns_forwarders: %s\n" % str(dns_forwarders))
@@ -989,7 +1020,7 @@ def main():
     print
     print "The IPA Master Server will be configured with:"
     print "Hostname:      %s" % host_name
-    print "IP address:    %s" % ip_address
+    print "IP address:    %s" % ", ".join(map(str, ip_address))
     print "Domain name:   %s" % domain_name
     print "Realm name:    %s" % realm_name
     print
@@ -999,7 +1030,7 @@ def main():
         print "Forwarders:    %s" % ("No forwarders" if not dns_forwarders \
                 else ", ".join([str(ip) for ip in dns_forwarders]))
         print "Reverse zone:  %s" % ("No reverse zone" if options.no_reverse \
-                or reverse_zone is None else reverse_zone)
+                or reverse_zone is None else ", ".join(map(str, reverse_zone)))
         print
 
     # If domain name and realm does not match, IPA server will not be able
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
index 9a27c781764f3dc311d20cfcf9150fde31307b03..c9fab7da0dd6f9bbf6c1684467b4629da4a94cf3 100644
--- a/ipaserver/install/bindinstance.py
+++ b/ipaserver/install/bindinstance.py
@@ -467,11 +467,11 @@ class BindInstance(service.Service):
         self.named_user = None
         self.domain = None
         self.host = None
-        self.ip_address = None
+        self.ip_address = []
         self.realm = None
         self.forwarders = None
         self.sub_dict = None
-        self.reverse_zone = None
+        self.reverse_zone = []
         self.dm_password = dm_password
 
         if fstore:
@@ -539,8 +539,9 @@ class BindInstance(service.Service):
         # get a connection to the DS
         self.ldap_connect()
 
-        if installutils.record_in_hosts(self.ip_address, self.fqdn) is None:
-            installutils.add_record_to_hosts(self.ip_address, self.fqdn)
+        for ip_address in map(str, self.ip_address):
+            if installutils.record_in_hosts(ip_address, self.fqdn) is None:
+                installutils.add_record_to_hosts(ip_address, self.fqdn)
 
         # Make sure generate-rndc-key.sh runs before named restart
         self.step("generating rndc key file", self.__generate_rndc_key)
@@ -552,7 +553,7 @@ class BindInstance(service.Service):
             self.step("adding NS record to the zone", self.__add_self_ns)
         else:
             self.step("setting up our zone", self.__setup_zone)
-        if self.reverse_zone is not None:
+        if self.reverse_zone:
             self.step("setting up reverse zone", self.__setup_reverse_zone)
 
         self.step("setting up our own record", self.__add_self)
@@ -603,18 +604,17 @@ class BindInstance(service.Service):
         else:
             optional_ntp = ""
 
-        addr = netaddr.IPAddress(self.ip_address)
-        if addr.version in (4, 6):
-            ipa_ca = "%s\t\t\tIN %s\t\t\t%s\n" % (
-                IPA_CA_RECORD,
-                "A" if addr.version == 4 else "AAAA",
-                self.ip_address)
-        else:
-            ipa_ca = ""
+        ipa_ca = ""
+        for addr in map(netaddr.IPAddress, self.ip_address):
+            if addr.version in (4, 6):
+                ipa_ca += "%s\t\t\tIN %s\t\t\t%s\n" % (
+                    IPA_CA_RECORD,
+                    "A" if addr.version == 4 else "AAAA",
+                    str(addr))
 
         self.sub_dict = dict(
             FQDN=self.fqdn,
-            IP=self.ip_address,
+            IP=map(str, self.ip_address),
             DOMAIN=self.domain,
             HOST=self.host,
             REALM=self.realm,
@@ -635,9 +635,10 @@ class BindInstance(service.Service):
             # 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)
+        for ns_ip_address in nameserver_ip_address:
+            add_zone(self.domain, self.zonemgr, dns_backup=self.dns_backup,
+                    ns_hostname=api.env.host, ns_ip_address=ns_ip_address,
+                    force=True)
 
         add_rr(self.domain, "_kerberos", "TXT", self.realm)
 
@@ -646,7 +647,8 @@ class BindInstance(service.Service):
 
     def __setup_reverse_zone(self):
         # Always use force=True as named is not set up yet
-        add_zone(self.reverse_zone, self.zonemgr, ns_hostname=api.env.host,
+        for reverse_zone in self.reverse_zone:
+            add_zone(reverse_zone, self.zonemgr, ns_hostname=api.env.host,
                 dns_backup=self.dns_backup, force=True)
 
     def __add_master_records(self, fqdn, addrs):
@@ -699,7 +701,7 @@ class BindInstance(service.Service):
                 add_ptr_rr(reverse_zone, addr, fqdn)
 
     def __add_self(self):
-        self.__add_master_records(self.fqdn, [self.ip_address])
+        self.__add_master_records(self.fqdn, self.ip_address)
 
     def __add_others(self):
         entries = self.admin_conn.get_entries(
@@ -744,7 +746,7 @@ class BindInstance(service.Service):
             pass
 
     def __add_ipa_ca_record(self):
-        self.__add_ipa_ca_records(self.fqdn, [self.ip_address],
+        self.__add_ipa_ca_records(self.fqdn, self.ip_address,
                                   self.ca_configured)
 
         if self.first_instance:
@@ -832,7 +834,9 @@ class BindInstance(service.Service):
 
     def __setup_resolv_conf(self):
         self.fstore.backup_file(RESOLV_CONF)
-        resolv_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n"
+        resolv_txt = ''
+        for ip_address in map(str, self.ip_address):
+            resolv_txt += "search "+self.domain+"\nnameserver "+ip_address+"\n"
         try:
             resolv_fd = open(RESOLV_CONF, 'w')
             resolv_fd.seek(0)
diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py
index dc98d7a51aa743c87b2d7667246b6f029b8a648b..bd2481c048e3fed6632fdb255ed8b3355cd303fc 100644
--- a/ipaserver/install/installutils.py
+++ b/ipaserver/install/installutils.py
@@ -437,72 +437,70 @@ def get_server_ip_address(host_name, fstore, unattended, options):
 
     ip_add_to_hosts = False
 
+    ip = []
     if len(hostaddr) > 1:
-        print >> sys.stderr, "The server hostname resolves to more than one address:"
-        for addr in hostaddr:
-            print >> sys.stderr, "  %s" % addr
-
-        if options.ip_address:
-            if str(options.ip_address) not in hostaddr:
-                print >> sys.stderr, "Address passed in --ip-address did not match any resolved"
-                print >> sys.stderr, "address!"
-                sys.exit(1)
-            print "Selected IP address:", str(options.ip_address)
-            ip = options.ip_address
-        else:
+        for ha in hostaddr:
+            try:
+                ip.append(ipautil.CheckedIPAddress(ha, match_local=True))
+            except ValueError, e:
+                root_logger.warning("Invalid IP address %s for %s: %s", ha, host_name, unicode(e))
+        if not ip:
             if unattended:
                 print >> sys.stderr, "Please use --ip-address option to specify the address"
                 sys.exit(1)
             else:
-                ip = read_ip_address(host_name, fstore)
+                ip.append(read_ip_address(host_name, fstore))
     elif len(hostaddr) == 1:
         try:
-            ip = ipautil.CheckedIPAddress(hostaddr[0], match_local=True)
+            ip.append(ipautil.CheckedIPAddress(hostaddr[0], match_local=True))
         except ValueError, e:
             sys.exit("Invalid IP Address %s for %s: %s" % (hostaddr[0], host_name, unicode(e)))
-    else:
+    elif options.ip_address:
         # hostname is not resolvable
         ip = options.ip_address
         ip_add_to_hosts = True
 
-    if ip is None:
+    if not ip:
         print "Unable to resolve IP address for host name"
         if unattended:
             sys.exit(1)
 
     if options.ip_address:
-        if options.ip_address != ip and not options.setup_dns:
-            print >>sys.stderr, "Error: the hostname resolves to an IP address that is different"
-            print >>sys.stderr, "from the one provided on the command line.  Please fix your DNS"
-            print >>sys.stderr, "or /etc/hosts file and restart the installation."
-            sys.exit(1)
+        if options.setup_dns:
+            ip = options.ip_address
+        else:
+            # all specified addresses was resolved for this host
+            if set(options.ip_address) <= set(ip):
+                ip = options.ip_address
+            else:
+                print >>sys.stderr, "Error: the hostname resolves to an IP address that is different"
+                print >>sys.stderr, "from the one provided on the command line.  Please fix your DNS"
+                print >>sys.stderr, "or /etc/hosts file and restart the installation."
+                sys.exit(1)
 
-        ip = options.ip_address
-
-    if ip is None:
-        ip = read_ip_address(host_name, fstore)
+    if not ip:
+        ip.append(read_ip_address(host_name, fstore))
         root_logger.debug("read ip_address: %s\n" % str(ip))
 
-    ip_address = str(ip)
+    for ip_address in map(str, ip):
+        # check /etc/hosts sanity, add a record when needed
+        hosts_record = record_in_hosts(ip_address)
 
-    # check /etc/hosts sanity, add a record when needed
-    hosts_record = record_in_hosts(ip_address)
-
-    if hosts_record is None:
-        if ip_add_to_hosts:
-            print "Adding ["+ip_address+" "+host_name+"] to your /etc/hosts file"
-            fstore.backup_file(paths.HOSTS)
-            add_record_to_hosts(ip_address, host_name)
-    else:
-        primary_host = hosts_record[1][0]
-        if primary_host != host_name:
-            print >>sys.stderr, "Error: there is already a record in /etc/hosts for IP address %s:" \
-                    % ip_address
-            print >>sys.stderr, hosts_record[0], " ".join(hosts_record[1])
-            print >>sys.stderr, "Chosen hostname %s does not match configured canonical hostname %s" \
-                    % (host_name, primary_host)
-            print >>sys.stderr, "Please fix your /etc/hosts file and restart the installation."
-            sys.exit(1)
+        if hosts_record is None:
+            if ip_add_to_hosts:
+                print "Adding ["+ip_address+" "+host_name+"] to your /etc/hosts file"
+                fstore.backup_file(paths.HOSTS)
+                add_record_to_hosts(ip_address, host_name)
+        else:
+            primary_host = hosts_record[1][0]
+            if primary_host != host_name:
+                print >>sys.stderr, "Error: there is already a record in /etc/hosts for IP address %s:" \
+                        % ip_address
+                print >>sys.stderr, hosts_record[0], " ".join(hosts_record[1])
+                print >>sys.stderr, "Chosen hostname %s does not match configured canonical hostname %s" \
+                        % (host_name, primary_host)
+                print >>sys.stderr, "Please fix your /etc/hosts file and restart the installation."
+                sys.exit(1)
 
     return ip
 
-- 
1.9.3

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

Reply via email to