diff options
-rwxr-xr-x | install/tools/ipa-dns-install | 71 | ||||
-rwxr-xr-x | install/tools/ipa-replica-install | 59 | ||||
-rwxr-xr-x | install/tools/ipa-server-install | 56 | ||||
-rw-r--r-- | ipaserver/install/bindinstance.py | 110 | ||||
-rw-r--r-- | ipaserver/install/installutils.py | 124 | ||||
-rw-r--r-- | ipaserver/install/ipa_replica_prepare.py | 82 |
6 files changed, 255 insertions, 247 deletions
diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install index c9ea63ce3..ae60f211a 100755 --- a/install/tools/ipa-dns-install +++ b/install/tools/ipa-dns-install @@ -42,13 +42,16 @@ def parse_options(): sensitive=True, help="admin password") parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False, help="print debugging information") - parser.add_option("--ip-address", dest="ip_address", + parser.add_option("--ip-address", dest="ip_addresses", + default=[], action="append", type="ip", ip_local=True, help="Master Server IP Address") parser.add_option("--forwarder", dest="forwarders", action="append", type="ip", help="Add a DNS forwarder") parser.add_option("--no-forwarders", dest="no_forwarders", action="store_true", default=False, help="Do not add any DNS forwarders, use root servers instead") - parser.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use") + parser.add_option("--reverse-zone", dest="reverse_zones", + default=[], action="append", + help="The reverse DNS zone to use") parser.add_option("--no-reverse", dest="no_reverse", action="store_true", default=False, help="Do not create new reverse DNS zone") parser.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback, @@ -62,7 +65,7 @@ def parse_options(): if options.forwarders and options.no_forwarders: parser.error("You cannot specify a --forwarder option together with --no-forwarders") - elif options.reverse_zone and options.no_reverse: + elif options.reverse_zones and options.no_reverse: parser.error("You cannot specify a --reverse-zone option together with --no-reverse") if options.unattended: @@ -130,46 +133,8 @@ def main(): except errors.ACIError: sys.exit("Password is not valid!") - # Check we have a public IP that is associated with the hostname - if options.ip_address: - ip = options.ip_address - else: - hostaddr = resolve_host(api.env.host) - try: - 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: - if options.unattended: - print >> sys.stderr, "Please use --ip-address option to specify the address" - sys.exit(1) - else: - ip = read_ip_address(api.env.host, fstore) - else: - ip = hostaddr and ipautil.CheckedIPAddress(hostaddr[0], match_local=True) - except Exception, e: - print "Error: Invalid IP Address %s: %s" % (ip, e) - ip = None - - if not ip: - if options.unattended: - sys.exit("Unable to resolve IP address for host name") - else: - ip = read_ip_address(api.env.host, fstore) - ip_address = str(ip) - root_logger.debug("will use ip_address: %s\n", ip_address) - - if options.reverse_zone and not bindinstance.verify_reverse_zone(options.reverse_zone, ip): - sys.exit(1) + ip_addresses = get_server_ip_address(api.env.host, fstore, + options.unattended, True, options.ip_addresses) if options.no_forwarders: dns_forwarders = () @@ -186,19 +151,11 @@ def main(): ccache = krbV.default_context().default_ccache() api.Backend.ldap2.connect(ccache) - if options.reverse_zone: - reverse_zone = bindinstance.normalize_zone(options.reverse_zone) - else: - reverse_zone = bindinstance.find_reverse_zone(ip) - if reverse_zone is None and not options.no_reverse: - if options.unattended: - reverse_zone = util.get_reverse_zone_default(ip) - elif bindinstance.create_reverse(): - reverse_zone = util.get_reverse_zone_default(ip) - reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip) + reverse_zones = bindinstance.check_reverse_zones(ip_addresses, + options.reverse_zones, options, options.unattended, True) - if reverse_zone is not None: - print "Using reverse zone %s" % reverse_zone + if reverse_zones is not None: + print "Using reverse zone %s" % ', '.join(reverse_zones) conf_ntp = ntpinstance.NTPInstance(fstore).is_enabled() @@ -208,8 +165,8 @@ def main(): print "Please wait until the prompt is returned." print "" - bind.setup(api.env.host, ip_address, api.env.realm, api.env.domain, - dns_forwarders, conf_ntp, reverse_zone, zonemgr=options.zonemgr) + bind.setup(api.env.host, ip_addresses, api.env.realm, api.env.domain, + dns_forwarders, conf_ntp, reverse_zones, zonemgr=options.zonemgr) bind.create_instance() # Restart http instance to make sure that python-dns has the right resolver diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install index 2986685d0..74781d00d 100755 --- a/install/tools/ipa-replica-install +++ b/install/tools/ipa-replica-install @@ -67,8 +67,8 @@ def parse_options(): default=False, help="configure a dogtag CA") basic_group.add_option("--setup-kra", dest="setup_kra", action="store_true", default=False, help="configure a dogtag KRA") - basic_group.add_option("--ip-address", dest="ip_address", - type="ip", ip_local=True, + basic_group.add_option("--ip-address", dest="ip_addresses", + type="ip", ip_local=True, action="append", default=[], help="Replica server IP Address") basic_group.add_option("-p", "--password", dest="password", sensitive=True, help="Directory Manager (existing master) password") @@ -112,7 +112,8 @@ def parse_options(): type="ip", help="Add a DNS forwarder") dns_group.add_option("--no-forwarders", dest="no_forwarders", action="store_true", default=False, help="Do not add any DNS forwarders, use root servers instead") - dns_group.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use") + dns_group.add_option("--reverse-zone", dest="reverse_zones", default=[], + action="append", help="The reverse DNS zone to use") dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true", default=False, help="Do not create new reverse DNS zone") dns_group.add_option("--no-host-dns", dest="no_host_dns", action="store_true", @@ -133,7 +134,7 @@ def parse_options(): parser.error("You cannot specify a --forwarder option without the --setup-dns option") if options.no_forwarders: parser.error("You cannot specify a --no-forwarders option without the --setup-dns option") - if options.reverse_zone: + if options.reverse_zones: parser.error("You cannot specify a --reverse-zone option without the --setup-dns option") if options.no_reverse: parser.error("You cannot specify a --no-reverse option without the --setup-dns option") @@ -141,7 +142,7 @@ def parse_options(): parser.error("You cannot specify a --forwarder option together with --no-forwarders") elif not options.forwarders and not options.no_forwarders: parser.error("You must specify at least one --forwarder option or --no-forwarders option") - elif options.reverse_zone and options.no_reverse: + elif options.reverse_zones and options.no_reverse: parser.error("You cannot specify a --reverse-zone option together with --no-reverse") return safe_options, options, args[0] @@ -264,23 +265,9 @@ def install_bind(config, options): forwarders = () bind = bindinstance.BindInstance(dm_password=config.dirman_password) - if options.reverse_zone: - if not bindinstance.verify_reverse_zone(options.reverse_zone, config.ip): - sys.exit(1) - reverse_zone = bindinstance.normalize_zone(options.reverse_zone) - else: - reverse_zone = bindinstance.find_reverse_zone(config.ip) - if reverse_zone is None and not options.no_reverse: - reverse_zone = util.get_reverse_zone_default(config.ip) - if not options.unattended and bindinstance.create_reverse(): - reverse_zone = bindinstance.read_reverse_zone(reverse_zone, config.ip) - - if reverse_zone is not None: - print "Using reverse zone %s" % reverse_zone - - bind.setup(config.host_name, config.ip_address, config.realm_name, - config.domain_name, forwarders, options.conf_ntp, reverse_zone, - ca_configured=options.setup_ca) + bind.setup(config.host_name, config.ips, config.realm_name, + config.domain_name, forwarders, options.conf_ntp, + config.reverse_zones, ca_configured=options.setup_ca) bind.create_instance() print "" @@ -326,12 +313,16 @@ def install_dns_records(config, options): config.master_host_name, config.dirman_password): try: bind = bindinstance.BindInstance(dm_password=config.dirman_password) - reverse_zone = bindinstance.find_reverse_zone(config.ip) - - bind.add_master_dns_records(config.host_name, config.ip_address, - config.realm_name, config.domain_name, - reverse_zone, options.conf_ntp, - options.setup_ca) + for ip in config.ips: + reverse_zone = bindinstance.find_reverse_zone(ip) + + bind.add_master_dns_records(config.host_name, + str(ip), + config.realm_name, + config.domain_name, + reverse_zone, + options.conf_ntp, + options.setup_ca) except errors.NotFound, e: root_logger.debug('Replica DNS records could not be added ' 'on master: %s', str(e)) @@ -534,8 +525,16 @@ def main(): # check replica host IP resolution - config.ip = installutils.get_server_ip_address(config.host_name, fstore, True, options) - config.ip_address = str(config.ip) + config.ips = installutils.get_server_ip_address(config.host_name, fstore, + options.unattended, options.setup_dns, options.ip_addresses) + + ip_addresses = [str(ip) for ip in config.ips] + config.reverse_zones = bindinstance.check_reverse_zones(ip_addresses, + options.reverse_zones, options, True) + + if config.reverse_zones is not None: + print "Using reverse zone(s) %s" % ', '.join(config.reverse_zones) + # Create the management framework config file # Note: We must do this before bootstraping and finalizing ipalib.api diff --git a/install/tools/ipa-server-install b/install/tools/ipa-server-install index 86422e332..533023f2e 100755 --- a/install/tools/ipa-server-install +++ b/install/tools/ipa-server-install @@ -175,8 +175,8 @@ def parse_options(): help="create home directories for users " "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, + basic_group.add_option("--ip-address", dest="ip_addresses", + type="ip", ip_local=True, action="append", default=[], 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) @@ -236,7 +236,8 @@ def parse_options(): type="ip", help="Add a DNS forwarder") dns_group.add_option("--no-forwarders", dest="no_forwarders", action="store_true", default=False, help="Do not add any DNS forwarders, use root servers instead") - dns_group.add_option("--reverse-zone", dest="reverse_zone", help="The reverse DNS zone to use") + dns_group.add_option("--reverse-zone", dest="reverse_zones", help="The reverse DNS zone to use", + action="append", default=[]) dns_group.add_option("--no-reverse", dest="no_reverse", action="store_true", default=False, help="Do not create reverse DNS zone") dns_group.add_option("--zonemgr", action="callback", callback=bindinstance.zonemgr_callback, @@ -280,13 +281,13 @@ def parse_options(): parser.error("You cannot specify a --forwarder option without the --setup-dns option") if options.no_forwarders: parser.error("You cannot specify a --no-forwarders option without the --setup-dns option") - if options.reverse_zone: + if options.reverse_zones: parser.error("You cannot specify a --reverse-zone option without the --setup-dns option") if options.no_reverse: parser.error("You cannot specify a --no-reverse option without the --setup-dns option") elif options.forwarders and options.no_forwarders: parser.error("You cannot specify a --forwarder option together with --no-forwarders") - elif options.reverse_zone and options.no_reverse: + elif options.reverse_zones and options.no_reverse: parser.error("You cannot specify a --reverse-zone option together with --no-reverse") if options.uninstall: @@ -829,11 +830,11 @@ def main(): realm_name = "" host_name = "" domain_name = "" - ip_address = "" + ip_addresses = [] master_password = "" dm_password = "" admin_password = "" - reverse_zone = None + reverse_zones = [] if not options.setup_dns and not options.unattended: if ipautil.user_input("Do you want to configure integrated DNS (BIND)?", False): @@ -892,11 +893,8 @@ def main(): domain_name = domain_name.lower() - ip = get_server_ip_address(host_name, fstore, options.unattended, options) - ip_address = str(ip) - - if options.reverse_zone and not bindinstance.verify_reverse_zone(options.reverse_zone, ip): - sys.exit(1) + ip_addresses = get_server_ip_address(host_name, fstore, + options.unattended, options.setup_dns, options.ip_addresses) if not options.realm_name: realm_name = read_realm_name(domain_name, options.unattended) @@ -973,35 +971,29 @@ def main(): else: dns_forwarders = read_dns_forwarders() - if options.reverse_zone: - reverse_zone = bindinstance.normalize_zone(options.reverse_zone) - elif not options.no_reverse: - if options.unattended: - reverse_zone = util.get_reverse_zone_default(ip) - elif bindinstance.create_reverse(): - reverse_zone = util.get_reverse_zone_default(ip) - reverse_zone = bindinstance.read_reverse_zone(reverse_zone, ip) - - if reverse_zone is not None: - print "Using reverse zone %s" % reverse_zone + reverse_zones = bindinstance.check_reverse_zones(ip_addresses, + options.reverse_zones, options, options.unattended) + + if reverse_zones: + print "Using reverse zone(s) %s" % ", ".join(str(rz) for rz in reverse_zones) else: dns_forwarders = () root_logger.debug("will use dns_forwarders: %s\n" % str(dns_forwarders)) print print "The IPA Master Server will be configured with:" - print "Hostname: %s" % host_name - print "IP address: %s" % ip_address - print "Domain name: %s" % domain_name - print "Realm name: %s" % realm_name + print "Hostname: %s" % host_name + print "IP address(es): %s" % ", ".join(str(ip) for ip in ip_addresses) + print "Domain name: %s" % domain_name + print "Realm name: %s" % realm_name print if options.setup_dns: print "BIND DNS server will be configured to serve IPA domain with:" 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) + print "Reverse zone(s): %s" % ("No reverse zone" if options.no_reverse \ + or reverse_zones is None else ", ".join(str(rz) for rz in reverse_zones)) print # If domain name and realm does not match, IPA server will not be able @@ -1112,7 +1104,7 @@ def main(): options.host_name = host_name options.unattended = True options.forwarders = dns_forwarders - options.reverse_zone = reverse_zone + options.reverse_zones = reverse_zones write_cache(vars(options)) ca.configure_instance(host_name, domain_name, dm_password, dm_password, csr_file=paths.ROOT_IPA_CSR, @@ -1206,8 +1198,8 @@ def main(): # Create a BIND instance bind = bindinstance.BindInstance(fstore, dm_password) - bind.setup(host_name, ip_address, realm_name, domain_name, dns_forwarders, - options.conf_ntp, reverse_zone, zonemgr=options.zonemgr, + bind.setup(host_name, ip_addresses, realm_name, domain_name, dns_forwarders, + options.conf_ntp, reverse_zones, zonemgr=options.zonemgr, ca_configured=setup_ca) if options.setup_dns: api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=dm_password) diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index cece85ec6..2e8836ec6 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -22,6 +22,7 @@ import os import pwd import netaddr import re +import sys import ldap @@ -250,7 +251,6 @@ def verify_reverse_zone(zone, ip_address): try: get_reverse_record_name(zone, ip_address) except ValueError: - print "Invalid reverse zone %s" % zone return False return True @@ -276,6 +276,8 @@ def read_reverse_zone(default, ip_address): return None if verify_reverse_zone(zone, ip_address): break + else: + print "Invalid reverse zone %s for IP address %s" % (zone, ip_address) return normalize_zone(zone) @@ -378,6 +380,48 @@ def zonemgr_callback(option, opt_str, value, parser): parser.values.zonemgr = value +def check_reverse_zones(ip_addresses, reverse_zones, options, unattended, search_reverse_zones=False): + reverse_asked = False + + ret_reverse_zones = [] + # check that there is IP address in every reverse zone + if reverse_zones: + for rz in reverse_zones: + for ip in ip_addresses: + if verify_reverse_zone(rz, ip): + ret_reverse_zones.append(normalize_zone(rz)) + break + else: + # no ip matching reverse zone found + sys.exit("There is no IP address matching reverse zone %s." % rz) + if not options.no_reverse: + # check that there is reverse zone for every IP + for ip in ip_addresses: + if search_reverse_zones and find_reverse_zone(str(ip)): + # reverse zone is already in LDAP + continue + for rz in ret_reverse_zones: + if verify_reverse_zone(rz, ip): + # reverse zone was entered by user + break + else: + # no reverse zone for ip found + if not reverse_asked: + if not unattended and not reverse_zones: + # user did not specify reverse_zone nor no_reverse + options.no_reverse = not create_reverse() + if options.no_reverse: + # user decided not to create reverse zone + return [] + reverse_asked = True + rz = get_reverse_zone_default(str(ip)) + if not unattended: + rz = read_reverse_zone(rz, str(ip)) + ret_reverse_zones.append(rz) + + return ret_reverse_zones + + class DnsBackup(object): def __init__(self, service): self.service = service @@ -437,11 +481,11 @@ class BindInstance(service.Service): self.named_user = None self.domain = None self.host = None - self.ip_address = None + self.ip_addresses = [] self.realm = None self.forwarders = None self.sub_dict = None - self.reverse_zone = None + self.reverse_zones = [] self.dm_password = dm_password if fstore: @@ -451,19 +495,19 @@ class BindInstance(service.Service): suffix = ipautil.dn_attribute_property('_suffix') - def setup(self, fqdn, ip_address, realm_name, domain_name, forwarders, ntp, - reverse_zone, named_user="named", zonemgr=None, + def setup(self, fqdn, ip_addresses, realm_name, domain_name, forwarders, ntp, + reverse_zones, named_user="named", zonemgr=None, ca_configured=None): self.named_user = named_user self.fqdn = fqdn - self.ip_address = ip_address + self.ip_addresses = ip_addresses self.realm = realm_name self.domain = domain_name self.forwarders = forwarders self.host = fqdn.split(".")[0] self.suffix = ipautil.realm_to_suffix(self.realm) self.ntp = ntp - self.reverse_zone = reverse_zone + self.reverse_zones = reverse_zones self.ca_configured = ca_configured if not zonemgr: @@ -509,8 +553,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 self.ip_addresses: + if installutils.record_in_hosts(str(ip_address), self.fqdn) is None: + installutils.add_record_to_hosts(str(ip_address), self.fqdn) # Make sure generate-rndc-key.sh runs before named restart self.step("generating rndc key file", self.__generate_rndc_key) @@ -520,8 +565,7 @@ class BindInstance(service.Service): if not dns_zone_exists(self.domain): self.step("setting up our zone", self.__setup_zone) - - if self.reverse_zone is not None: + if self.reverse_zones: self.step("setting up reverse zone", self.__setup_reverse_zone) self.step("setting up our own record", self.__add_self) @@ -574,18 +618,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 self.ip_addresses: + 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=[str(ip) for ip in self.ip_addresses], DOMAIN=self.domain, HOST=self.host, REALM=self.realm, @@ -618,7 +661,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_zones: + 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): @@ -665,7 +709,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_addresses) def __add_others(self): entries = self.admin_conn.get_entries( @@ -710,7 +754,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_addresses, self.ca_configured) if self.first_instance: @@ -798,7 +842,17 @@ 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 = "search "+self.domain+"\n" + + for ip_address in self.ip_addresses: + if ip_address.version == 4: + resolv_txt += "nameserver 127.0.0.1\n" + break + + for ip_address in self.ip_addresses: + if ip_address.version == 6: + resolv_txt += "nameserver ::1\n" + break try: resolv_fd = open(RESOLV_CONF, 'w') resolv_fd.seek(0) @@ -812,16 +866,16 @@ class BindInstance(service.Service): installutils.check_entropy() ipautil.run(['/usr/libexec/generate-rndc-key.sh']) - def add_master_dns_records(self, fqdn, ip_address, realm_name, domain_name, - reverse_zone, ntp=False, ca_configured=None): + def add_master_dns_records(self, fqdn, ip_addresses, realm_name, domain_name, + reverse_zones, ntp=False, ca_configured=None): self.fqdn = fqdn - self.ip_address = ip_address + self.ip_addresses = ip_addresses self.realm = realm_name self.domain = domain_name self.host = fqdn.split(".")[0] self.suffix = ipautil.realm_to_suffix(self.realm) self.ntp = ntp - self.reverse_zone = reverse_zone + self.reverse_zones = reverse_zones self.ca_configured = ca_configured self.first_instance = False self.zonemgr = 'hostmaster.%s' % self.domain diff --git a/ipaserver/install/installutils.py b/ipaserver/install/installutils.py index 34ae30624..c8e1a8de9 100644 --- a/ipaserver/install/installutils.py +++ b/ipaserver/install/installutils.py @@ -222,6 +222,7 @@ def add_record_to_hosts(ip, host_name, conf_file=paths.HOSTS): hosts_fd.write(ip+'\t'+host_name+' '+host_name.split('.')[0]+'\n') hosts_fd.close() +# TODO: Remove when removing usage from ipa-adtrust-install def read_ip_address(host_name, fstore): while True: ip = ipautil.user_input("Please provide the IP address to be used for this host name", allow_empty = False) @@ -235,6 +236,22 @@ def read_ip_address(host_name, fstore): return ip_parsed +def read_ip_addresses(host_name, fstore): + ips = [] + print "Enter the IP address to use, or press Enter to finish." + while True: + ip = ipautil.user_input("Please provide the IP address to be used for this host name", allow_empty = True) + if not ip: + break + try: + ip_parsed = ipautil.CheckedIPAddress(ip, match_local=True) + except Exception, e: + print "Error: Invalid IP Address %s: %s" % (ip, e) + continue + ips.append(ip) + + return ips + def read_dns_forwarders(): addrs = [] if ipautil.user_input("Do you want to configure DNS forwarders?", True): @@ -426,7 +443,7 @@ def get_host_name(no_host_dns): verify_fqdn(hostname, no_host_dns) return hostname -def get_server_ip_address(host_name, fstore, unattended, options): +def get_server_ip_address(host_name, fstore, unattended, setup_dns, ip_addresses): # Check we have a public IP that is associated with the hostname try: hostaddr = resolve_host(host_name) @@ -441,74 +458,59 @@ def get_server_ip_address(host_name, fstore, unattended, options): ip_add_to_hosts = False - 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 + ips = [] + if len(hostaddr): + for ha in hostaddr: + try: + ips.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 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 + if not ips and not ip_addresses: + if not unattended: + ip_addresses = read_ip_addresses(host_name, fstore) + + if ip_addresses: + if setup_dns: + ips = ip_addresses else: - if unattended: - print >> sys.stderr, "Please use --ip-address option to specify the address" - sys.exit(1) + # all specified addresses was resolved for this host + if set(ip_addresses) <= set(ips): + ips = ip_addresses else: - ip = read_ip_address(host_name, fstore) - elif len(hostaddr) == 1: - try: - ip = 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: - # hostname is not resolvable - ip = options.ip_address + print >>sys.stderr, "Error: the hostname resolves to IP address(es) that are different" + print >>sys.stderr, "from those provided on the command line. Please fix your DNS" + print >>sys.stderr, "or /etc/hosts file and restart the installation." + print >>sys.stderr, "Provided but not resolved address(es): %s" % \ + ", ".join(str(ip) for ip in (set(ip_addresses) - set(ips))) + sys.exit(1) ip_add_to_hosts = True - if ip is None: - 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) - - ip = options.ip_address - - if ip is None: - ip = read_ip_address(host_name, fstore) - root_logger.debug("read ip_address: %s\n" % str(ip)) + if not ips: + print >> sys.stderr, "No usable IP address provided nor resolved." + sys.exit(1) - ip_address = str(ip) + for ip_address in ips: + # check /etc/hosts sanity, add a record when needed + hosts_record = record_in_hosts(str(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 ["+str(ip_address)+" "+host_name+"] to your /etc/hosts file" + fstore.backup_file(paths.HOSTS) + add_record_to_hosts(str(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 or options.setup_dns: - 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 + return ips def expand_replica_info(filename, password): """ diff --git a/ipaserver/install/ipa_replica_prepare.py b/ipaserver/install/ipa_replica_prepare.py index 7768fd311..e27eb6dd4 100644 --- a/ipaserver/install/ipa_replica_prepare.py +++ b/ipaserver/install/ipa_replica_prepare.py @@ -54,9 +54,11 @@ class ReplicaPrepare(admintool.AdminTool): parser.add_option("-p", "--password", dest="password", help="Directory Manager password (for the existing master)") - parser.add_option("--ip-address", dest="ip_address", type="ip", + parser.add_option("--ip-address", dest="ip_addresses", type="ip", + action="append", default=[], help="add A and PTR records of the future replica") - parser.add_option("--reverse-zone", dest="reverse_zone", + parser.add_option("--reverse-zone", dest="reverse_zones", + action="append", default=[], help="the reverse DNS zone to use") parser.add_option("--no-reverse", dest="no_reverse", action="store_true", default=False, @@ -95,14 +97,14 @@ class ReplicaPrepare(admintool.AdminTool): super(ReplicaPrepare, self).validate_options(needs_root=True) installutils.check_server_configuration() - if not options.ip_address: - if options.reverse_zone: + if not options.ip_addresses: + if options.reverse_zones: self.option_parser.error("You cannot specify a --reverse-zone " "option without the --ip-address option") if options.no_reverse: self.option_parser.error("You cannot specify a --no-reverse " "option without the --ip-address option") - elif options.reverse_zone and options.no_reverse: + elif options.reverse_zones and options.no_reverse: self.option_parser.error("You cannot specify a --reverse-zone " "option together with --no-reverse") @@ -192,7 +194,7 @@ class ReplicaPrepare(admintool.AdminTool): except installutils.BadHostError, e: msg = str(e) if isinstance(e, installutils.HostLookupError): - if options.ip_address is None: + if not options.ip_addresses: if dns_container_exists( api.env.host, api.env.basedn, dm_password=self.dirman_password, @@ -206,7 +208,7 @@ class ReplicaPrepare(admintool.AdminTool): else: raise - if options.ip_address: + if options.ip_addresses: if not dns_container_exists(api.env.host, api.env.basedn, dm_password=self.dirman_password, ldapi=True, realm=api.env.realm): @@ -215,9 +217,19 @@ class ReplicaPrepare(admintool.AdminTool): "because DNS is not managed by IPA. Please create DNS " "record manually and then omit --ip-address option.") raise admintool.ScriptError("Cannot add DNS record") - if options.reverse_zone and not bindinstance.verify_reverse_zone( - options.reverse_zone, options.ip_address): - raise admintool.ScriptError("Invalid reverse zone") + + disconnect = False + if not api.Backend.ldap2.isconnected(): + api.Backend.ldap2.connect( + bind_dn=DN(('cn', 'Directory Manager')), + bind_pw=self.dirman_password) + disconnect = True + + options.reverse_zones = bindinstance.check_reverse_zones( + options.ip_addresses, options.reverse_zones, options, False, + True) + if disconnect: + api.Backend.ldap2.disconnect() if options.http_pkcs12: if options.http_pin is None: @@ -293,7 +305,7 @@ class ReplicaPrepare(admintool.AdminTool): finally: shutil.rmtree(self.top_dir) - if options.ip_address: + if options.ip_addresses: self.add_dns_records() if options.wait_for_dns: @@ -420,46 +432,38 @@ class ReplicaPrepare(admintool.AdminTool): options = self.options self.log.info("Adding DNS records for %s", self.replica_fqdn) - api.Backend.ldap2.connect( - bind_dn=DN(('cn', 'Directory Manager')), - bind_pw=self.dirman_password) - name, domain = self.replica_fqdn.split(".", 1) - ip = options.ip_address - ip_address = str(ip) - - if options.reverse_zone: - reverse_zone = bindinstance.normalize_zone(options.reverse_zone) - else: - reverse_zone = bindinstance.find_reverse_zone(ip) - if reverse_zone is None and not options.no_reverse: - reverse_zone = bindinstance.get_reverse_zone_default(ip) - + if not api.Backend.ldap2.isconnected(): + api.Backend.ldap2.connect( + bind_dn=DN(('cn', 'Directory Manager')), + bind_pw=self.dirman_password) try: add_zone(domain) except errors.PublicError, e: raise admintool.ScriptError( "Could not create forward DNS zone for the replica: %s" % e) - try: - add_fwd_rr(domain, name, ip_address) - except errors.PublicError, e: - raise admintool.ScriptError( - "Could not add forward DNS record for the replica: %s" % e) + for reverse_zone in options.reverse_zones: + self.log.info("Adding reverse zone %s", reverse_zone) + add_zone(reverse_zone) - if reverse_zone is not None: - self.log.info("Using reverse zone %s", reverse_zone) + for ip in options.ip_addresses: + ip_address = str(ip) try: - add_zone(reverse_zone) + add_fwd_rr(domain, name, ip_address) except errors.PublicError, e: raise admintool.ScriptError( - "Could not create reverse DNS zone for replica: %s" % e) - try: - add_ptr_rr(reverse_zone, ip_address, self.replica_fqdn) - except errors.PublicError, e: - raise admintool.ScriptError( - "Could not add reverse DNS record for the replica: %s" % e) + "Could not add forward DNS record for the replica: %s" % e) + + if not options.no_reverse: + reverse_zone = bindinstance.find_reverse_zone(ip) + try: + add_ptr_rr(reverse_zone, ip_address, self.replica_fqdn) + except errors.PublicError, e: + raise admintool.ScriptError( + "Could not add reverse DNS record for the replica: %s" + % e) def check_dns(self, replica_fqdn): """Return true if the replica hostname is resolvable""" |