From ae23432ef520850de820aff099679f3f639d1d2f Mon Sep 17 00:00:00 2001 From: Martin Basti Date: Oct 15 2015 16:37:52 +0000 Subject: Add option to specify LDIF file that contains DS configuration changes This allows to user modify configuration changes of the directory server instance during installation of DS https://fedorahosted.org/freeipa/ticket/4949 Also fixes: https://fedorahosted.org/freeipa/ticket/4048 https://fedorahosted.org/freeipa/ticket/1930 Reviewed-By: Martin Babinsky --- diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1 index ff4d7d9..12a5dd7 100644 --- a/install/tools/man/ipa-replica-install.1 +++ b/install/tools/man/ipa-replica-install.1 @@ -74,6 +74,9 @@ Enable debug logging when more verbose output is needed .TP \fB\-U\fR, \fB\-\-unattended\fR An unattended installation that will never prompt for user input +.TP +\fB\-\-dirsrv-config-mods\fR +The path to LDIF file that will be used to modify configuration of dse.ldif during installation of the directory server instance .SS "CERTIFICATE SYSTEM OPTIONS" .TP diff --git a/install/tools/man/ipa-server-install.1 b/install/tools/man/ipa-server-install.1 index 2e0ff80..ba43c80 100644 --- a/install/tools/man/ipa-server-install.1 +++ b/install/tools/man/ipa-server-install.1 @@ -78,7 +78,9 @@ Enable debug logging when more verbose output is needed .TP \fB\-U\fR, \fB\-\-unattended\fR An unattended installation that will never prompt for user input - +.TP +\fB\-\-dirsrv-config-mods\fR +The path to LDIF file that will be used to modify configuration of dse.ldif during installation of the directory server instance .SS "CERTIFICATE SYSTEM OPTIONS" .TP diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 1e0c839..f572c87 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -206,7 +206,7 @@ info: IPA V2.0 class DsInstance(service.Service): def __init__(self, realm_name=None, domain_name=None, dm_password=None, - fstore=None, domainlevel=None): + fstore=None, domainlevel=None, config_ldif=None): service.Service.__init__(self, "dirsrv", service_desc="directory server", dm_password=dm_password, @@ -229,6 +229,7 @@ class DsInstance(service.Service): self.subject_base = None self.open_ports = [] self.run_init_memberof = True + self.config_ldif = config_ldif # updates for dse.ldif self.domainlevel = domainlevel if realm_name: self.suffix = ipautil.realm_to_suffix(self.realm) @@ -248,6 +249,9 @@ class DsInstance(service.Service): self.step("creating directory server user", create_ds_user) self.step("creating directory server instance", self.__create_instance) + if self.config_ldif: + self.step("updating configuration in dse.ldif", self.__update_dse_ldif) + self.step("restarting directory server", self.__restart_instance) self.step("adding default schema", self.__add_default_schemas) self.step("enabling memberof plugin", self.__add_memberof_module) self.step("enabling winsync plugin", self.__add_winsync_module) @@ -544,16 +548,39 @@ class DsInstance(service.Service): # check for open port 389 from now on self.open_ports.append(389) - root_logger.debug("restarting ds instance") - try: - self.__restart_instance() - root_logger.debug("done restarting ds instance") - except ipautil.CalledProcessError as e: - print("failed to restart ds instance", e) - root_logger.debug("failed to restart ds instance %s" % e) inf_fd.close() os.remove(paths.DIRSRV_BOOT_LDIF) + def __update_dse_ldif(self): + """ + This method updates dse.ldif right after instance creation. This is + supposed to allow admin modify configuration of the DS which has to be + done before IPA is fully installed (for example: settings for + replication on replicas) + DS must be turned off. + """ + self.stop() + + dse_filename = os.path.join( + paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % self.serverid, + 'dse.ldif' + ) + + with tempfile.NamedTemporaryFile(delete=False) as new_dse_ldif: + temp_filename = new_dse_ldif.name + with open(dse_filename, "r") as input_file: + parser = installutils.ModifyLDIF(input_file, new_dse_ldif) + # parse modification from config ldif + with open(self.config_ldif, "r") as config_ldif: + parser.modifications_from_ldif(config_ldif) + parser.parse() + new_dse_ldif.flush() + shutil.copy2(temp_filename, dse_filename) + try: + os.remove(temp_filename) + except OSError as e: + root_logger.debug("Failed to clean temporary file: %s" % e) + def __add_default_schemas(self): pent = pwd.getpwnam(DS_USER) for schema_fname in IPA_SCHEMA_FILES: diff --git a/ipaserver/install/server/common.py b/ipaserver/install/server/common.py index 3eb7279..a09f394 100644 --- a/ipaserver/install/server/common.py +++ b/ipaserver/install/server/common.py @@ -343,6 +343,20 @@ class BaseServer(common.Installable, common.Interactive, core.Composite): description="Do not automatically create DNS SSHFP records", ) + dirsrv_config_mods = Knob( + str, None, + description="The path to LDIF file that will be used to modify " + "configuration of dse.ldif during installation of the " + "directory server instance", + cli_metavar='FILE', + ) + + @dirsrv_config_mods.validator + def dirsrv_config_mods(self, value): + if not os.path.exists(value): + raise ValueError("File %s does not exist." % value) + + def __init__(self, **kwargs): super(BaseServer, self).__init__(**kwargs) diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 3164d0b..566af69 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -734,7 +734,8 @@ def install(installer): if options.dirsrv_cert_files: ds = dsinstance.DsInstance(fstore=fstore, - domainlevel=options.domainlevel) + domainlevel=options.domainlevel, + config_ldif=options.dirsrv_config_mods) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, dirsrv_pkcs12_info, @@ -743,7 +744,8 @@ def install(installer): hbac_allow=not options.no_hbac_allow) else: ds = dsinstance.DsInstance(fstore=fstore, - domainlevel=options.domainlevel) + domainlevel=options.domainlevel, + config_ldif=options.dirsrv_config_mods) installer._ds = ds ds.create_instance(realm_name, host_name, domain_name, dm_password, diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index 06e9368..071b12b 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -87,7 +87,7 @@ def install_http_certs(config, fstore): # FIXME: need Signing-Cert too ? -def install_replica_ds(config, promote=False): +def install_replica_ds(config, options, promote=False): dsinstance.check_ports() # if we have a pkcs12 file, create the cert db from @@ -95,7 +95,8 @@ def install_replica_ds(config, promote=False): # cert pkcs12_info = make_pkcs12_info(config.dir, "dscert.p12", "dirsrv_pin.txt") - ds = dsinstance.DsInstance() + ds = dsinstance.DsInstance( + config_ldif=options.dirsrv_config_mods) ds.create_replica( realm_name=config.realm_name, master_fqdn=config.master_host_name, @@ -668,7 +669,7 @@ def install(installer): ntp.create_instance() # Configure dirsrv - ds = install_replica_ds(config) + ds = install_replica_ds(config, options) # Always try to install DNS records install_dns_records(config, options, remote_api) @@ -1015,7 +1016,7 @@ def promote(installer): ntp.create_instance() # Configure dirsrv - ds = install_replica_ds(config, promote=True) + ds = install_replica_ds(config, options, promote=True) # Always try to install DNS records install_dns_records(config, options, api)