f65af0
From 4caf0c14ad38a1dae494489b13f4109cdb3ba340 Mon Sep 17 00:00:00 2001
979ee0
From: Christian Heimes <cheimes@redhat.com>
979ee0
Date: Fri, 6 Jul 2018 00:04:39 +0200
979ee0
Subject: [PATCH] Delay enabling services until end of installer
979ee0
979ee0
Service entries in cn=FQDN,cn=masters,cn=ipa,cn=etc are no longer
979ee0
created as enabled. Instead they are flagged as configuredService. At
979ee0
the very end of the installer, the service entries are switched from
979ee0
configured to enabled service.
979ee0
979ee0
- SRV records are created at the very end of the installer.
979ee0
- Dogtag installer only picks fully installed servers
979ee0
- Certmonger ignores all configured but not yet enabled servers.
979ee0
979ee0
Fixes: https://pagure.io/freeipa/issue/7566
979ee0
Signed-off-by: Christian Heimes <cheimes@redhat.com>
979ee0
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
979ee0
---
979ee0
 install/tools/ipa-adtrust-install          |  6 +-
979ee0
 install/tools/ipa-ca-install               | 12 ++-
979ee0
 install/tools/ipa-dns-install              |  4 +
979ee0
 ipaserver/dns_data_management.py           | 18 +++--
979ee0
 ipaserver/install/adtrustinstance.py       |  6 +-
f65af0
 ipaserver/install/bindinstance.py          |  8 +-
979ee0
 ipaserver/install/cainstance.py            |  2 +-
979ee0
 ipaserver/install/dnskeysyncinstance.py    |  4 +-
979ee0
 ipaserver/install/httpinstance.py          |  4 +-
979ee0
 ipaserver/install/ipa_kra_install.py       |  7 ++
979ee0
 ipaserver/install/krainstance.py           |  2 +-
979ee0
 ipaserver/install/krbinstance.py           |  4 +-
979ee0
 ipaserver/install/odsexporterinstance.py   |  4 +-
979ee0
 ipaserver/install/opendnssecinstance.py    |  6 +-
979ee0
 ipaserver/install/server/install.py        | 18 +++--
f65af0
 ipaserver/install/server/replicainstall.py |  8 +-
f65af0
 ipaserver/install/service.py               | 88 ++++++++++++++++++++--
979ee0
 ipaserver/plugins/serverrole.py            |  7 +-
f65af0
 18 files changed, 161 insertions(+), 47 deletions(-)
979ee0
979ee0
diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
f65af0
index d4e5d4c09cf6b7c1521bcecb79bb6fd7235fc799..a870d136e242affe6627cd4c44a173a80a9ab1c6 100755
979ee0
--- a/install/tools/ipa-adtrust-install
979ee0
+++ b/install/tools/ipa-adtrust-install
f65af0
@@ -32,7 +32,7 @@ import six
979ee0
 from optparse import SUPPRESS_HELP  # pylint: disable=deprecated-module
979ee0
 
979ee0
 from ipalib.install import sysrestore
979ee0
-from ipaserver.install import adtrust
979ee0
+from ipaserver.install import adtrust, service
979ee0
 from ipaserver.install.installutils import (
979ee0
     read_password,
979ee0
     check_server_configuration,
f65af0
@@ -212,6 +212,10 @@ def main():
979ee0
     adtrust.install_check(True, options, api)
979ee0
     adtrust.install(True, options, fstore, api)
979ee0
 
979ee0
+    # Enable configured services and update DNS SRV records
979ee0
+    service.enable_services(api.env.host)
979ee0
+    api.Command.dns_update_system_records()
979ee0
+
979ee0
     print("""
979ee0
 =============================================================================
979ee0
 Setup complete
979ee0
diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install
f65af0
index e4e24fb8c5261e77dd9d3e89cbe42dba519b932e..f78f43d94981d29939a247f3c492c5e7340298ea 100755
979ee0
--- a/install/tools/ipa-ca-install
979ee0
+++ b/install/tools/ipa-ca-install
f65af0
@@ -332,18 +332,26 @@ def main():
979ee0
     )
979ee0
     api.finalize()
979ee0
     api.Backend.ldap2.connect()
979ee0
-
979ee0
     domain_level = dsinstance.get_domain_level(api)
979ee0
+
979ee0
     if domain_level > DOMAIN_LEVEL_0:
979ee0
         promote(safe_options, options, filename)
979ee0
     else:
979ee0
         install(safe_options, options, filename)
979ee0
 
979ee0
+    # pki-spawn restarts 389-DS, reconnect
979ee0
+    api.Backend.ldap2.close()
979ee0
+    api.Backend.ldap2.connect()
979ee0
+
979ee0
+    # Enable configured services and update DNS SRV records
979ee0
+    service.enable_services(api.env.host)
979ee0
+    api.Command.dns_update_system_records()
979ee0
+    api.Backend.ldap2.disconnect()
979ee0
+
979ee0
     # execute ipactl to refresh services status
979ee0
     ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
979ee0
                 raiseonerr=False)
979ee0
 
979ee0
-    api.Backend.ldap2.disconnect()
979ee0
 
979ee0
 fail_message = '''
979ee0
 Your system may be partly configured.
979ee0
diff --git a/install/tools/ipa-dns-install b/install/tools/ipa-dns-install
f65af0
index a7f136b16ab4871518f5fd776d49e55c2364c54e..57dde5a5da4fad162c93e9e0416b54961de4c1e3 100755
979ee0
--- a/install/tools/ipa-dns-install
979ee0
+++ b/install/tools/ipa-dns-install
f65af0
@@ -37,6 +37,7 @@ from ipapython.config import IPAOptionParser
f65af0
 from ipapython.ipa_log_manager import standard_logging_setup
979ee0
 
979ee0
 from ipaserver.install import dns as dns_installer
979ee0
+from ipaserver.install import service
979ee0
 
f65af0
 logger = logging.getLogger(os.path.basename(__file__))
979ee0
 
f65af0
@@ -148,6 +149,9 @@ def main():
979ee0
 
979ee0
     dns_installer.install_check(True, api, False, options, hostname=api.env.host)
979ee0
     dns_installer.install(True, False, options)
979ee0
+    # Enable configured services and update DNS SRV records
979ee0
+    service.enable_services(api.env.host)
979ee0
+    api.Command.dns_update_system_records()
979ee0
 
979ee0
     # execute ipactl to refresh services status
979ee0
     ipautil.run(['ipactl', 'start', '--ignore-service-failures'],
979ee0
diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
f65af0
index 675dd481b461aa14d8adf8393a2168ac84ecac86..673397ef2b2252f431eec1f3e1f71dc45ff87511 100644
979ee0
--- a/ipaserver/dns_data_management.py
979ee0
+++ b/ipaserver/dns_data_management.py
f65af0
@@ -68,11 +68,11 @@ class IPASystemRecords(object):
979ee0
     PRIORITY_HIGH = 0
979ee0
     PRIORITY_LOW = 50
979ee0
 
979ee0
-    def __init__(self, api_instance):
979ee0
+    def __init__(self, api_instance, all_servers=False):
979ee0
         self.api_instance = api_instance
979ee0
         self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute()
979ee0
         self.servers_data = {}
979ee0
-        self.__init_data()
979ee0
+        self.__init_data(all_servers=all_servers)
979ee0
 
979ee0
     def reload_data(self):
979ee0
         """
f65af0
@@ -92,14 +92,16 @@ class IPASystemRecords(object):
979ee0
     def __get_location_suffix(self, location):
979ee0
         return location + DNSName('_locations') + self.domain_abs
979ee0
 
979ee0
-    def __init_data(self):
979ee0
+    def __init_data(self, all_servers=False):
979ee0
         self.servers_data = {}
979ee0
 
979ee0
-        servers_result = self.api_instance.Command.server_find(
979ee0
-            no_members=False,
979ee0
-            servrole=u"IPA master",  # only active, fully installed masters
979ee0
-        )['result']
979ee0
-        for s in servers_result:
979ee0
+        kwargs = dict(no_members=False)
979ee0
+        if not all_servers:
979ee0
+            # only active, fully installed masters]
979ee0
+            kwargs["servrole"] = u"IPA master"
979ee0
+        servers = self.api_instance.Command.server_find(**kwargs)
979ee0
+
979ee0
+        for s in servers['result']:
979ee0
             weight, location, roles = self.__get_server_attrs(s)
979ee0
             self.servers_data[s['cn'][0]] = {
979ee0
                 'weight': weight,
979ee0
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
f65af0
index a075801ebec20ea8277445e0bac788c06e0b0a91..2da46d67495014fb38e5ee8c6a98ede93ef8762d 100644
979ee0
--- a/ipaserver/install/adtrustinstance.py
979ee0
+++ b/ipaserver/install/adtrustinstance.py
979ee0
@@ -581,7 +581,7 @@ class ADTRUSTInstance(service.Service):
979ee0
             self.print_msg(err_msg)
979ee0
             self.print_msg("Add the following service records to your DNS " \
979ee0
                            "server for DNS zone %s: " % zone)
979ee0
-            system_records = IPASystemRecords(api)
979ee0
+            system_records = IPASystemRecords(api, all_servers=True)
979ee0
             adtrust_records = system_records.get_base_records(
979ee0
                 [self.fqdn], ["AD trust controller"],
979ee0
                 include_master_role=False, include_kerberos_realm=False)
f65af0
@@ -736,12 +736,12 @@ class ADTRUSTInstance(service.Service):
979ee0
         # Note that self.dm_password is None for ADTrustInstance because
979ee0
         # we ensure to be called as root and using ldapi to use autobind
979ee0
         try:
979ee0
-            self.ldap_enable('ADTRUST', self.fqdn, None, self.suffix)
979ee0
+            self.ldap_configure('ADTRUST', self.fqdn, None, self.suffix)
979ee0
         except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
f65af0
             logger.info("ADTRUST Service startup entry already exists.")
979ee0
 
979ee0
         try:
979ee0
-            self.ldap_enable('EXTID', self.fqdn, None, self.suffix)
979ee0
+            self.ldap_configure('EXTID', self.fqdn, None, self.suffix)
979ee0
         except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
f65af0
             logger.info("EXTID Service startup entry already exists.")
979ee0
 
979ee0
diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py
f65af0
index 203a6405f815d47c0dc33977e77012a2c85916ff..7c858aab4417ccf3a4999fcaaa1c7e0f93464e4d 100644
979ee0
--- a/ipaserver/install/bindinstance.py
979ee0
+++ b/ipaserver/install/bindinstance.py
f65af0
@@ -669,7 +669,7 @@ class BindInstance(service.Service):
979ee0
         return normalize_zone(self.host_domain) == normalize_zone(self.domain)
979ee0
 
979ee0
     def create_file_with_system_records(self):
979ee0
-        system_records = IPASystemRecords(self.api)
979ee0
+        system_records = IPASystemRecords(self.api, all_servers=True)
979ee0
         text = u'\n'.join(
979ee0
             IPASystemRecords.records_list_from_zone(
979ee0
                 system_records.get_base_records()
f65af0
@@ -746,7 +746,7 @@ class BindInstance(service.Service):
979ee0
         # Instead we reply on the IPA init script to start only enabled
979ee0
         # components as found in our LDAP configuration tree
979ee0
         try:
979ee0
-            self.ldap_enable('DNS', self.fqdn, None, self.suffix)
979ee0
+            self.ldap_configure('DNS', self.fqdn, None, self.suffix)
979ee0
         except errors.DuplicateEntry:
979ee0
             # service already exists (forced DNS reinstall)
979ee0
             # don't crash, just report error
f65af0
@@ -1180,7 +1180,9 @@ class BindInstance(service.Service):
979ee0
             except ValueError as error:
f65af0
                 logger.debug('%s', error)
979ee0
 
979ee0
-        # disabled by default, by ldap_enable()
f65af0
+        installutils.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR)
f65af0
+
979ee0
+        # disabled by default, by ldap_configure()
979ee0
         if enabled:
979ee0
             self.enable()
f65af0
         else:
979ee0
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
f65af0
index b58fbb4c881d247d6b5fb661f4085ec82c3cc811..51fdbe9c61e06ab9d72d78aee8786f9bceca137b 100644
979ee0
--- a/ipaserver/install/cainstance.py
979ee0
+++ b/ipaserver/install/cainstance.py
f65af0
@@ -1258,7 +1258,7 @@ class CAInstance(DogtagInstance):
979ee0
             config = ['caRenewalMaster']
979ee0
         else:
979ee0
             config = []
979ee0
-        self.ldap_enable('CA', self.fqdn, None, basedn, config)
979ee0
+        self.ldap_configure('CA', self.fqdn, None, basedn, config)
979ee0
 
979ee0
     def setup_lightweight_ca_key_retrieval(self):
979ee0
         if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
979ee0
diff --git a/ipaserver/install/dnskeysyncinstance.py b/ipaserver/install/dnskeysyncinstance.py
f65af0
index b865ee8aa79c17502a3784878f8f6f45d05213a6..2e773f3adae8130f578e6f3fbfe8c3a414d523cb 100644
979ee0
--- a/ipaserver/install/dnskeysyncinstance.py
979ee0
+++ b/ipaserver/install/dnskeysyncinstance.py
f65af0
@@ -382,8 +382,8 @@ class DNSKeySyncInstance(service.Service):
979ee0
 
979ee0
     def __enable(self):
979ee0
         try:
979ee0
-            self.ldap_enable('DNSKeySync', self.fqdn, None,
979ee0
-                             self.suffix, self.extra_config)
979ee0
+            self.ldap_configure('DNSKeySync', self.fqdn, None,
979ee0
+                                self.suffix, self.extra_config)
979ee0
         except errors.DuplicateEntry:
f65af0
             logger.error("DNSKeySync service already exists")
979ee0
 
979ee0
diff --git a/ipaserver/install/httpinstance.py b/ipaserver/install/httpinstance.py
f65af0
index bdd79b1dafda7de664eed664a18bf36c541212bc..0b7023c2f1b0feb996e0dd0adbefbd49c51da757 100644
979ee0
--- a/ipaserver/install/httpinstance.py
979ee0
+++ b/ipaserver/install/httpinstance.py
f65af0
@@ -196,7 +196,7 @@ class HTTPInstance(service.Service):
979ee0
         # We do not let the system start IPA components on its own,
979ee0
         # Instead we reply on the IPA init script to start only enabled
979ee0
         # components as found in our LDAP configuration tree
979ee0
-        self.ldap_enable('HTTP', self.fqdn, None, self.suffix)
979ee0
+        self.ldap_configure('HTTP', self.fqdn, None, self.suffix)
979ee0
 
979ee0
     def configure_selinux_for_httpd(self):
979ee0
         try:
f65af0
@@ -609,7 +609,7 @@ class HTTPInstance(service.Service):
979ee0
         if running:
979ee0
             self.restart()
979ee0
 
979ee0
-        # disabled by default, by ldap_enable()
979ee0
+        # disabled by default, by ldap_configure()
979ee0
         if enabled:
979ee0
             self.enable()
979ee0
 
979ee0
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
f65af0
index 07e11ea69ded8832015dd69ea43ff338c5f9df95..b536685f5f1f3fccab07fd37aa001958e2d38420 100644
979ee0
--- a/ipaserver/install/ipa_kra_install.py
979ee0
+++ b/ipaserver/install/ipa_kra_install.py
f65af0
@@ -227,4 +227,11 @@ class KRAInstaller(KRAInstall):
f65af0
             logger.error('%s', dedent(self.FAIL_MESSAGE))
979ee0
             raise
979ee0
 
979ee0
+        # pki-spawn restarts 389-DS, reconnect
979ee0
+        api.Backend.ldap2.close()
979ee0
+        api.Backend.ldap2.connect()
979ee0
+
979ee0
+        # Enable configured services and update DNS SRV records
979ee0
+        service.enable_services(api.env.host)
979ee0
+        api.Command.dns_update_system_records()
979ee0
         api.Backend.ldap2.disconnect()
979ee0
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
f65af0
index 9483f0ec4edbabea0f7eff0dd5dd223377653536..c1daa2869b7cba79e29d2db61c090c145304397f 100644
979ee0
--- a/ipaserver/install/krainstance.py
979ee0
+++ b/ipaserver/install/krainstance.py
f65af0
@@ -392,4 +392,4 @@ class KRAInstance(DogtagInstance):
f65af0
                 directives[nickname], cert)
979ee0
 
979ee0
     def __enable_instance(self):
979ee0
-        self.ldap_enable('KRA', self.fqdn, None, self.suffix)
979ee0
+        self.ldap_configure('KRA', self.fqdn, None, self.suffix)
979ee0
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
f65af0
index a356d5e0c1b96dc6511c335fc22a326a2133bdd8..33d66fb94b0a1f7571b22120e5159a0e0ad2e675 100644
979ee0
--- a/ipaserver/install/krbinstance.py
979ee0
+++ b/ipaserver/install/krbinstance.py
f65af0
@@ -242,7 +242,7 @@ class KrbInstance(service.Service):
979ee0
         # We do not let the system start IPA components on its own,
979ee0
         # Instead we reply on the IPA init script to start only enabled
979ee0
         # components as found in our LDAP configuration tree
979ee0
-        self.ldap_enable('KDC', self.fqdn, None, self.suffix)
979ee0
+        self.ldap_configure('KDC', self.fqdn, None, self.suffix)
979ee0
 
979ee0
     def __start_instance(self):
979ee0
         try:
f65af0
@@ -607,7 +607,7 @@ class KrbInstance(service.Service):
979ee0
             except ValueError as error:
f65af0
                 logger.debug("%s", error)
979ee0
 
979ee0
-        # disabled by default, by ldap_enable()
979ee0
+        # disabled by default, by ldap_configure()
979ee0
         if enabled:
979ee0
             self.enable()
979ee0
 
979ee0
diff --git a/ipaserver/install/odsexporterinstance.py b/ipaserver/install/odsexporterinstance.py
f65af0
index b301a167f80d171c0dd0e6282a6021fcbdca8f9e..4856f5642d8e2c4c9e9089cd44a85e759c4c6ca0 100644
979ee0
--- a/ipaserver/install/odsexporterinstance.py
979ee0
+++ b/ipaserver/install/odsexporterinstance.py
f65af0
@@ -73,8 +73,8 @@ class ODSExporterInstance(service.Service):
979ee0
     def __enable(self):
979ee0
 
979ee0
         try:
979ee0
-            self.ldap_enable('DNSKeyExporter', self.fqdn, None,
979ee0
-                             self.suffix)
979ee0
+            self.ldap_configure('DNSKeyExporter', self.fqdn, None,
979ee0
+                                self.suffix)
979ee0
         except errors.DuplicateEntry:
f65af0
             logger.error("DNSKeyExporter service already exists")
979ee0
 
979ee0
diff --git a/ipaserver/install/opendnssecinstance.py b/ipaserver/install/opendnssecinstance.py
f65af0
index d608294cbbab9179d95b2333323f5d378940a936..0337bb22fea44f95ee9077423136353a991325db 100644
979ee0
--- a/ipaserver/install/opendnssecinstance.py
979ee0
+++ b/ipaserver/install/opendnssecinstance.py
f65af0
@@ -140,8 +140,8 @@ class OpenDNSSECInstance(service.Service):
979ee0
 
979ee0
     def __enable(self):
979ee0
         try:
979ee0
-            self.ldap_enable('DNSSEC', self.fqdn, None,
979ee0
-                             self.suffix, self.extra_config)
979ee0
+            self.ldap_configure('DNSSEC', self.fqdn, None,
979ee0
+                                self.suffix, self.extra_config)
979ee0
         except errors.DuplicateEntry:
f65af0
             logger.error("DNSSEC service already exists")
979ee0
 
f65af0
@@ -372,7 +372,7 @@ class OpenDNSSECInstance(service.Service):
979ee0
 
979ee0
         self.restore_state("kasp_db_configured")  # just eat state
979ee0
 
979ee0
-        # disabled by default, by ldap_enable()
979ee0
+        # disabled by default, by ldap_configure()
979ee0
         if enabled:
979ee0
             self.enable()
979ee0
 
979ee0
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
f65af0
index e96ae97c74ee1598683d1ef3f2570e8de93c9943..a341408f78f24055d807ae49c8a0cda81bfb3ec4 100644
979ee0
--- a/ipaserver/install/server/install.py
979ee0
+++ b/ipaserver/install/server/install.py
f65af0
@@ -870,14 +870,6 @@ def install(installer):
979ee0
 
979ee0
     if options.setup_dns:
979ee0
         dns.install(False, False, options)
979ee0
-    else:
979ee0
-        # Create a BIND instance
979ee0
-        bind = bindinstance.BindInstance(fstore)
979ee0
-        bind.setup(host_name, ip_addresses, realm_name,
979ee0
-                   domain_name, (), 'first', (),
979ee0
-                   zonemgr=options.zonemgr,
979ee0
-                   no_dnssec_validation=options.no_dnssec_validation)
979ee0
-        bind.create_file_with_system_records()
979ee0
 
979ee0
     if options.setup_adtrust:
979ee0
         adtrust.install(False, options, fstore, api)
f65af0
@@ -906,6 +898,16 @@ def install(installer):
f65af0
     except Exception:
f65af0
         raise ScriptError("Configuration of client side components failed!")
979ee0
 
979ee0
+    # Enable configured services and update DNS SRV records
979ee0
+    service.enable_services(host_name)
979ee0
+    api.Command.dns_update_system_records()
979ee0
+
979ee0
+    if not options.setup_dns:
979ee0
+        # After DNS and AD trust are configured and services are
979ee0
+        # enabled, create a dummy instance to dump DNS configuration.
979ee0
+        bind = bindinstance.BindInstance(fstore)
979ee0
+        bind.create_file_with_system_records()
979ee0
+
979ee0
     # Everything installed properly, activate ipa service.
979ee0
     services.knownservices.ipa.enable()
979ee0
 
979ee0
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
f65af0
index 33f3ae9e616b34a3ab0ff8e4257552855e817e7c..0bf3568a300a133fa505dc8fc339c6677f9c5f73 100644
979ee0
--- a/ipaserver/install/server/replicainstall.py
979ee0
+++ b/ipaserver/install/server/replicainstall.py
f65af0
@@ -1520,14 +1520,11 @@ def install(installer):
979ee0
 
979ee0
     if options.setup_dns:
979ee0
         dns.install(False, True, options, api)
979ee0
-    else:
979ee0
-        api.Command.dns_update_system_records()
979ee0
 
979ee0
     if options.setup_adtrust:
979ee0
         adtrust.install(False, options, fstore, api)
979ee0
 
f65af0
     ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
979ee0
-    api.Backend.ldap2.disconnect()
f65af0
 
979ee0
     if not promote:
979ee0
         # Call client install script
f65af0
@@ -1556,6 +1553,11 @@ def install(installer):
f65af0
         # remove the extracted replica file
f65af0
         remove_replica_info_dir(installer)
979ee0
 
979ee0
+    # Enable configured services and update DNS SRV records
979ee0
+    service.enable_services(config.host_name)
979ee0
+    api.Command.dns_update_system_records()
979ee0
+    api.Backend.ldap2.disconnect()
979ee0
+
979ee0
     # Everything installed properly, activate ipa service.
979ee0
     services.knownservices.ipa.enable()
979ee0
 
979ee0
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
f65af0
index 4c320de9d64676373cd41ff839889f2448a19a46..0106379ea38e4a3fef8436256d6f315f524b8dee 100644
979ee0
--- a/ipaserver/install/service.py
979ee0
+++ b/ipaserver/install/service.py
f65af0
@@ -27,6 +27,7 @@ import socket
979ee0
 import datetime
979ee0
 import traceback
979ee0
 import tempfile
979ee0
+import warnings
979ee0
 
979ee0
 import six
979ee0
 
f65af0
@@ -62,6 +63,10 @@ SERVICE_LIST = {
979ee0
     'DNSKeySync': ('ipa-dnskeysyncd', 110),
979ee0
 }
979ee0
 
979ee0
+CONFIGURED_SERVICE = u'configuredService'
979ee0
+ENABLED_SERVICE = 'enabledService'
979ee0
+
979ee0
+
979ee0
 def print_msg(message, output_fd=sys.stdout):
f65af0
     logger.debug("%s", message)
979ee0
     output_fd.write(message)
f65af0
@@ -123,7 +128,7 @@ def find_providing_servers(svcname, conn, api):
979ee0
     """
979ee0
     dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
979ee0
     query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
979ee0
-                                     'ipaConfigString': 'enabledService',
979ee0
+                                     'ipaConfigString': ENABLED_SERVICE,
979ee0
                                      'cn': svcname}, rules='&')
979ee0
     try:
979ee0
         entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn)
f65af0
@@ -232,6 +237,51 @@ def set_service_entry_config(name, fqdn, config_values,
979ee0
         raise e
979ee0
 
979ee0
 
979ee0
+def enable_services(fqdn):
979ee0
+    """Change all configured services to enabled
979ee0
+
979ee0
+    Server.ldap_configure() only marks a service as configured. Services
979ee0
+    are enabled at the very end of installation.
979ee0
+
979ee0
+    Note: DNS records must be updated with dns_update_system_records, too.
979ee0
+
979ee0
+    :param fqdn: hostname of server
979ee0
+    """
979ee0
+    ldap2 = api.Backend.ldap2
979ee0
+    search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn)
979ee0
+    search_filter = ldap2.make_filter(
979ee0
+        {
979ee0
+            'objectClass': 'ipaConfigObject',
979ee0
+            'ipaConfigString': CONFIGURED_SERVICE
979ee0
+        },
979ee0
+        rules='&'
979ee0
+    )
979ee0
+    entries = ldap2.get_entries(
979ee0
+        search_base,
979ee0
+        filter=search_filter,
979ee0
+        scope=api.Backend.ldap2.SCOPE_ONELEVEL,
979ee0
+        attrs_list=['cn', 'ipaConfigString']
979ee0
+    )
979ee0
+    for entry in entries:
979ee0
+        name = entry['cn']
979ee0
+        cfgstrings = entry.setdefault('ipaConfigString', [])
979ee0
+        for value in list(cfgstrings):
979ee0
+            if value.lower() == CONFIGURED_SERVICE.lower():
979ee0
+                cfgstrings.remove(value)
979ee0
+        if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE):
979ee0
+            cfgstrings.append(ENABLED_SERVICE)
979ee0
+
979ee0
+        try:
979ee0
+            ldap2.update_entry(entry)
979ee0
+        except errors.EmptyModlist:
f65af0
+            logger.debug("Nothing to do for service %s", name)
979ee0
+        except Exception:
f65af0
+            logger.exception("failed to set service %s config values", name)
979ee0
+            raise
979ee0
+        else:
f65af0
+            logger.debug("Enabled service %s for %s", name, fqdn)
979ee0
+
979ee0
+
979ee0
 class Service(object):
979ee0
     def __init__(self, service_name, service_desc=None, sstore=None,
979ee0
                  fstore=None, api=api, realm_name=None,
f65af0
@@ -538,7 +588,35 @@ class Service(object):
979ee0
         self.steps = []
979ee0
 
979ee0
     def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='',
979ee0
-                    config=[]):
979ee0
+                    config=()):
979ee0
+        """Legacy function, all services should use ldap_configure()
979ee0
+        """
979ee0
+        warnings.warn(
979ee0
+            "ldap_enable is deprecated, use ldap_configure instead.",
979ee0
+            DeprecationWarning,
979ee0
+            stacklevel=2
979ee0
+        )
979ee0
+        self._ldap_enable(ENABLED_SERVICE, name, fqdn, ldap_suffix, config)
979ee0
+
979ee0
+    def ldap_configure(self, name, fqdn, dm_password=None, ldap_suffix='',
979ee0
+                       config=()):
979ee0
+        """Create or modify service entry in cn=masters,cn=ipa,cn=etc
979ee0
+
979ee0
+        Contrary to ldap_enable(), the method only sets
979ee0
+        ipaConfigString=configuredService. ipaConfigString=enabledService
979ee0
+        is set at the very end of the installation process, to ensure that
979ee0
+        other machines see this master/replica after it is fully installed.
979ee0
+
979ee0
+        To switch all configured services to enabled, use::
979ee0
+
979ee0
+            ipaserver.install.service.enable_services(api.env.host)
979ee0
+            api.Command.dns_update_system_records()
979ee0
+        """
979ee0
+        self._ldap_enable(
979ee0
+            CONFIGURED_SERVICE, name, fqdn, ldap_suffix, config
979ee0
+        )
979ee0
+
979ee0
+    def _ldap_enable(self, value, name, fqdn, ldap_suffix, config):
979ee0
         extra_config_opts = [
979ee0
             ' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])])
979ee0
         ]
f65af0
@@ -549,7 +627,7 @@ class Service(object):
979ee0
         set_service_entry_config(
979ee0
             name,
979ee0
             fqdn,
979ee0
-            [u'enabledService'],
979ee0
+            [value],
979ee0
             ldap_suffix=ldap_suffix,
979ee0
             post_add_config=extra_config_opts)
979ee0
 
f65af0
@@ -575,7 +653,7 @@ class Service(object):
979ee0
 
979ee0
         # case insensitive
979ee0
         for value in entry.get('ipaConfigString', []):
979ee0
-            if value.lower() == u'enabledservice':
979ee0
+            if value.lower() == ENABLED_SERVICE:
979ee0
                 entry['ipaConfigString'].remove(value)
979ee0
                 break
979ee0
 
f65af0
@@ -688,7 +766,7 @@ class SimpleServiceInstance(Service):
979ee0
         if self.gensvc_name == None:
979ee0
             self.enable()
979ee0
         else:
979ee0
-            self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix)
979ee0
+            self.ldap_configure(self.gensvc_name, self.fqdn, None, self.suffix)
979ee0
 
979ee0
     def is_installed(self):
979ee0
         return self.service.is_installed()
979ee0
diff --git a/ipaserver/plugins/serverrole.py b/ipaserver/plugins/serverrole.py
f65af0
index 5b7ccfb342d0a54bfd6f2cdc53c7d31201ed5989..199978000ce8cf783bda50c46b7c9fa109f70ad6 100644
979ee0
--- a/ipaserver/plugins/serverrole.py
979ee0
+++ b/ipaserver/plugins/serverrole.py
979ee0
@@ -15,16 +15,21 @@ IPA server roles
979ee0
 """) + _("""
979ee0
 Get status of roles (DNS server, CA, etc.) provided by IPA masters.
979ee0
 """) + _("""
979ee0
+The status of a role is either enabled, configured, or absent.
979ee0
+""") + _("""
979ee0
 EXAMPLES:
979ee0
 """) + _("""
979ee0
   Show status of 'DNS server' role on a server:
979ee0
     ipa server-role-show ipa.example.com "DNS server"
979ee0
 """) + _("""
979ee0
   Show status of all roles containing 'AD' on a server:
979ee0
-    ipa server-role-find --server ipa.example.com --role='AD'
979ee0
+    ipa server-role-find --server ipa.example.com --role="AD trust controller"
979ee0
 """) + _("""
979ee0
   Show status of all configured roles on a server:
979ee0
     ipa server-role-find ipa.example.com
979ee0
+""") + _("""
979ee0
+  Show implicit IPA master role:
979ee0
+    ipa server-role-find --include-master
979ee0
 """)
979ee0
 
979ee0
 
979ee0
-- 
979ee0
2.17.1
979ee0