diff options
-rw-r--r-- | install/tools/ipa-upgradeconfig | 88 | ||||
-rw-r--r-- | ipaserver/install/bindinstance.py | 94 |
2 files changed, 174 insertions, 8 deletions
diff --git a/install/tools/ipa-upgradeconfig b/install/tools/ipa-upgradeconfig index 0cf59f293..07c8466cd 100644 --- a/install/tools/ipa-upgradeconfig +++ b/install/tools/ipa-upgradeconfig @@ -25,14 +25,18 @@ Upgrade configuration files to a newer template. import sys try: - from ipapython import ipautil, sysrestore + from ipapython import ipautil, sysrestore, version + from ipapython.config import IPAOptionParser + from ipapython.ipa_log_manager import * from ipaserver.install import installutils from ipaserver.install import dsinstance from ipaserver.install import httpinstance from ipaserver.install import memcacheinstance + from ipaserver.install import bindinstance from ipaserver.install import service from ipaserver.install import cainstance from ipaserver.install import certs + from ipaserver.install import sysupgrade import ldap import krbV import re @@ -49,6 +53,16 @@ error was: """ % sys.exc_value sys.exit(1) +def parse_options(): + parser = IPAOptionParser(version=version.VERSION) + parser.add_option("-d", "--debug", dest="debug", action="store_true", + default=False, help="print debugging information") + + options, args = parser.parse_args() + safe_options = parser.get_safe_opts(options) + + return safe_options, options + class KpasswdInstance(service.SimpleServiceInstance): def __init__(self): service.SimpleServiceInstance.__init__(self, "ipa_kpasswd") @@ -249,6 +263,70 @@ def upgrade_httpd_selinux(fstore): http = httpinstance.HTTPInstance(fstore) http.configure_selinux_for_httpd() +def enable_psearch_for_named(): + """ + From IPA 3.0, persistent search is a preferred mechanism for new DNS zone + detection and is also needed for other features (DNSSEC, SOA serial + updates). Enable psearch and make sure connections attribute is right. + This step is done just once for a case when user switched the persistent + search back to disabled. + + When some change in named.conf is done, this functions returns True + """ + changed = False + + if not bindinstance.named_conf_exists(): + # DNS service may not be configured + return + + try: + psearch = bindinstance.named_conf_get_directive('psearch').lower() + except IOError, e: + root_logger.debug('Cannot retrieve psearch option from %s: %s', + bindinstance.NAMED_CONF, e) + return + if not sysupgrade.get_upgrade_state('named.conf', 'psearch_enabled'): + if psearch != "yes": + try: + bindinstance.named_conf_set_directive('zone_refresh', 0) + bindinstance.named_conf_set_directive('psearch', 'yes') + except IOError, e: + root_logger.error('Cannot enable psearch in %s: %s', + bindinstance.NAMED_CONF, e) + else: + changed = True + sysupgrade.set_upgrade_state('named.conf', 'psearch_enabled', True) + + # make sure number of connections is right + minimum_connections = 2 + if psearch == 'yes': + minimum_connections = 3 + try: + connections = bindinstance.named_conf_get_directive('connections') + except IOError, e: + root_logger.debug('Cannot retrieve connections option from %s: %s', + bindinstance.NAMED_CONF, e) + return + if connections is not None: + try: + connections = int(connections) + except ValueError: + # this should not happend, but there is some bad value in + # "connections" option, bail out + pass + else: + if connections < minimum_connections: + try: + bindinstance.named_conf_set_directive('connections', + minimum_connections) + except IOError, e: + root_logger.error('Cannot update connections in %s: %s', + bindinstance.NAMED_CONF, e) + else: + changed = True + + return changed + def main(): """ Get some basics about the system. If getting those basics fail then @@ -259,6 +337,10 @@ def main(): if not os.geteuid()==0: sys.exit("\nYou must be root to run this script.\n") + safe_options, options = parse_options() + + standard_logging_setup(None, debug=options.debug) + fstore = sysrestore.FileStore('/var/lib/ipa/sysrestore') try: @@ -304,6 +386,10 @@ def main(): cleanup_kdc(fstore) upgrade_ipa_profile(krbctx.default_realm) + changed = enable_psearch_for_named() + if changed: + # configuration has changed, restart the name server + bindinstance.BindInstance(fstore).restart() if __name__ == '__main__': installutils.run_script(main, operation_name='ipa-upgradeconfig') diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index 623e39738..3ff593298 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -21,6 +21,7 @@ import tempfile import os import pwd import netaddr +import re import installutils import ldap @@ -38,6 +39,12 @@ from ipapython.ipa_log_manager import * import ipalib from ipalib import api, util, errors +NAMED_CONF = '/etc/named.conf' +RESOLV_CONF = '/etc/resolv.conf' + +named_conf_ipa_re = re.compile(r'(?P<indent>\s*)arg\s+"(?P<name>\S+)\s(?P<value>[^"]+)";') +named_conf_ipa_template = "%(indent)sarg \"%(name)s %(value)s\";\n" + def check_inst(unattended): has_bind = True # So far this file is always present in both RHEL5 and Fedora if all the necessary @@ -57,7 +64,7 @@ def check_inst(unattended): if not has_bind: return False - if not unattended and os.path.exists('/etc/named.conf'): + if not unattended and os.path.exists(NAMED_CONF): msg = "Existing BIND configuration detected, overwrite?" return ipautil.user_input(msg, False) @@ -73,7 +80,10 @@ def create_reverse(): return ipautil.user_input("Do you want to configure the reverse zone?", True) def named_conf_exists(): - named_fd = open('/etc/named.conf', 'r') + try: + named_fd = open(NAMED_CONF, 'r') + except IOError: + return False lines = named_fd.readlines() named_fd.close() for line in lines: @@ -81,6 +91,76 @@ def named_conf_exists(): return True return False +def named_conf_get_directive(name): + """Get a configuration option in bind-dyndb-ldap section of named.conf""" + + with open(NAMED_CONF, 'r') as f: + ipa_section = False + for line in f: + if line.startswith('dynamic-db "ipa"'): + ipa_section = True + continue + if line.startswith('};'): + if ipa_section: + break + + if ipa_section: + match = named_conf_ipa_re.match(line) + + if match and name == match.group('name'): + return match.group('value') + +def named_conf_set_directive(name, value): + """ + Set configuration option in bind-dyndb-ldap section of named.conf. + + When the configuration option with given name does not exist, it + is added at the end of ipa section in named.conf. + + If the value is set to None, the configuration option is removed + from named.conf. + """ + new_lines = [] + + with open(NAMED_CONF, 'r') as f: + ipa_section = False + matched = False + last_indent = "\t" + for line in f: + if line.startswith('dynamic-db "ipa"'): + ipa_section = True + if line.startswith('};'): + if ipa_section and not matched: + # create a new conf + new_conf = named_conf_ipa_template \ + % dict(indent=last_indent, + name=name, + value=value) + new_lines.append(new_conf) + ipa_section = False + + if ipa_section and not matched: + match = named_conf_ipa_re.match(line) + + if match: + last_indent = match.group('indent') + if name == match.group('name'): + matched = True + if value is not None: + if not isinstance(value, basestring): + value = str(value) + new_conf = named_conf_ipa_template \ + % dict(indent=last_indent, + name=name, + value=value) + new_lines.append(new_conf) + continue + new_lines.append(line) + + # write new configuration + with open(NAMED_CONF, 'w') as f: + f.write("".join(new_lines)) + def dns_container_exists(fqdn, suffix, dm_password=None, ldapi=False, realm=None): """ Test whether the dns container exists. @@ -610,18 +690,18 @@ class BindInstance(service.Service): raise def __setup_named_conf(self): - self.fstore.backup_file('/etc/named.conf') + self.fstore.backup_file(NAMED_CONF) named_txt = ipautil.template_file(ipautil.SHARE_DIR + "bind.named.conf.template", self.sub_dict) - named_fd = open('/etc/named.conf', 'w') + named_fd = open(NAMED_CONF, 'w') named_fd.seek(0) named_fd.truncate(0) named_fd.write(named_txt) named_fd.close() def __setup_resolv_conf(self): - self.fstore.backup_file('/etc/resolv.conf') + self.fstore.backup_file(RESOLV_CONF) resolv_txt = "search "+self.domain+"\nnameserver "+self.ip_address+"\n" - resolv_fd = open('/etc/resolv.conf', 'w') + resolv_fd = open(RESOLV_CONF, 'w') resolv_fd.seek(0) resolv_fd.truncate(0) resolv_fd.write(resolv_txt) @@ -704,7 +784,7 @@ class BindInstance(service.Service): if not running is None: self.stop() - for f in ["/etc/named.conf", "/etc/resolv.conf"]: + for f in [NAMED_CONF, RESOLV_CONF]: try: self.fstore.restore_file(f) except ValueError, error: |