mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
Delay enabling services until end of installer
Service entries in cn=FQDN,cn=masters,cn=ipa,cn=etc are no longer created as enabled. Instead they are flagged as configuredService. At the very end of the installer, the service entries are switched from configured to enabled service. - SRV records are created at the very end of the installer. - Dogtag installer only picks fully installed servers - Certmonger ignores all configured but not yet enabled servers. Fixes: https://pagure.io/freeipa/issue/7566 Signed-off-by: Christian Heimes <cheimes@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
This commit is contained in:
parent
10457a01bf
commit
7284097eed
@ -32,7 +32,7 @@ import six
|
||||
from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
|
||||
|
||||
from ipalib.install import sysrestore
|
||||
from ipaserver.install import adtrust
|
||||
from ipaserver.install import adtrust, service
|
||||
from ipaserver.install.installutils import (
|
||||
read_password,
|
||||
check_server_configuration,
|
||||
@ -212,6 +212,10 @@ def main():
|
||||
adtrust.install_check(True, options, api)
|
||||
adtrust.install(True, options, fstore, api)
|
||||
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(api.env.host)
|
||||
api.Command.dns_update_system_records()
|
||||
|
||||
print("""
|
||||
=============================================================================
|
||||
Setup complete
|
||||
|
@ -342,18 +342,26 @@ def main():
|
||||
)
|
||||
api.finalize()
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
domain_level = dsinstance.get_domain_level(api)
|
||||
|
||||
if domain_level > DOMAIN_LEVEL_0:
|
||||
promote(safe_options, options, filename)
|
||||
else:
|
||||
install(safe_options, options, filename)
|
||||
|
||||
# pki-spawn restarts 389-DS, reconnect
|
||||
api.Backend.ldap2.close()
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(api.env.host)
|
||||
api.Command.dns_update_system_records()
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
||||
# execute ipactl to refresh services status
|
||||
ipautil.run([paths.IPACTL, 'start', '--ignore-service-failures'],
|
||||
raiseonerr=False)
|
||||
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
||||
fail_message = '''
|
||||
Your system may be partly configured.
|
||||
|
@ -35,6 +35,7 @@ from ipapython.config import IPAOptionParser
|
||||
from ipapython.ipa_log_manager import standard_logging_setup
|
||||
|
||||
from ipaserver.install import dns as dns_installer
|
||||
from ipaserver.install import service
|
||||
|
||||
logger = logging.getLogger(os.path.basename(__file__))
|
||||
|
||||
@ -141,6 +142,9 @@ def main():
|
||||
|
||||
dns_installer.install_check(True, api, False, options, hostname=api.env.host)
|
||||
dns_installer.install(True, False, options)
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(api.env.host)
|
||||
api.Command.dns_update_system_records()
|
||||
|
||||
# execute ipactl to refresh services status
|
||||
ipautil.run([paths.IPACTL, 'start', '--ignore-service-failures'],
|
||||
|
@ -68,11 +68,11 @@ class IPASystemRecords(object):
|
||||
PRIORITY_HIGH = 0
|
||||
PRIORITY_LOW = 50
|
||||
|
||||
def __init__(self, api_instance):
|
||||
def __init__(self, api_instance, all_servers=False):
|
||||
self.api_instance = api_instance
|
||||
self.domain_abs = DNSName(self.api_instance.env.domain).make_absolute()
|
||||
self.servers_data = {}
|
||||
self.__init_data()
|
||||
self.__init_data(all_servers=all_servers)
|
||||
|
||||
def reload_data(self):
|
||||
"""
|
||||
@ -92,14 +92,16 @@ class IPASystemRecords(object):
|
||||
def __get_location_suffix(self, location):
|
||||
return location + DNSName('_locations') + self.domain_abs
|
||||
|
||||
def __init_data(self):
|
||||
def __init_data(self, all_servers=False):
|
||||
self.servers_data = {}
|
||||
|
||||
servers_result = self.api_instance.Command.server_find(
|
||||
no_members=False,
|
||||
servrole=u"IPA master", # only active, fully installed masters
|
||||
)['result']
|
||||
for s in servers_result:
|
||||
kwargs = dict(no_members=False)
|
||||
if not all_servers:
|
||||
# only active, fully installed masters]
|
||||
kwargs["servrole"] = u"IPA master"
|
||||
servers = self.api_instance.Command.server_find(**kwargs)
|
||||
|
||||
for s in servers['result']:
|
||||
weight, location, roles = self.__get_server_attrs(s)
|
||||
self.servers_data[s['cn'][0]] = {
|
||||
'weight': weight,
|
||||
|
@ -583,7 +583,7 @@ class ADTRUSTInstance(service.Service):
|
||||
self.print_msg(err_msg)
|
||||
self.print_msg("Add the following service records to your DNS " \
|
||||
"server for DNS zone %s: " % zone)
|
||||
system_records = IPASystemRecords(api)
|
||||
system_records = IPASystemRecords(api, all_servers=True)
|
||||
adtrust_records = system_records.get_base_records(
|
||||
[self.fqdn], ["AD trust controller"],
|
||||
include_master_role=False, include_kerberos_realm=False)
|
||||
@ -738,12 +738,12 @@ class ADTRUSTInstance(service.Service):
|
||||
# Note that self.dm_password is None for ADTrustInstance because
|
||||
# we ensure to be called as root and using ldapi to use autobind
|
||||
try:
|
||||
self.ldap_enable('ADTRUST', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('ADTRUST', self.fqdn, None, self.suffix)
|
||||
except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
|
||||
logger.info("ADTRUST Service startup entry already exists.")
|
||||
|
||||
try:
|
||||
self.ldap_enable('EXTID', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('EXTID', self.fqdn, None, self.suffix)
|
||||
except (ldap.ALREADY_EXISTS, errors.DuplicateEntry):
|
||||
logger.info("EXTID Service startup entry already exists.")
|
||||
|
||||
|
@ -688,7 +688,7 @@ class BindInstance(service.Service):
|
||||
return normalize_zone(self.host_domain) == normalize_zone(self.domain)
|
||||
|
||||
def create_file_with_system_records(self):
|
||||
system_records = IPASystemRecords(self.api)
|
||||
system_records = IPASystemRecords(self.api, all_servers=True)
|
||||
text = u'\n'.join(
|
||||
IPASystemRecords.records_list_from_zone(
|
||||
system_records.get_base_records()
|
||||
@ -765,7 +765,7 @@ class BindInstance(service.Service):
|
||||
# Instead we reply on the IPA init script to start only enabled
|
||||
# components as found in our LDAP configuration tree
|
||||
try:
|
||||
self.ldap_enable('DNS', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('DNS', self.fqdn, None, self.suffix)
|
||||
except errors.DuplicateEntry:
|
||||
# service already exists (forced DNS reinstall)
|
||||
# don't crash, just report error
|
||||
@ -1210,7 +1210,7 @@ class BindInstance(service.Service):
|
||||
|
||||
installutils.rmtree(paths.BIND_LDAP_DNS_IPA_WORKDIR)
|
||||
|
||||
# disabled by default, by ldap_enable()
|
||||
# disabled by default, by ldap_configure()
|
||||
if enabled:
|
||||
self.enable()
|
||||
else:
|
||||
|
@ -1266,7 +1266,7 @@ class CAInstance(DogtagInstance):
|
||||
config = ['caRenewalMaster']
|
||||
else:
|
||||
config = []
|
||||
self.ldap_enable('CA', self.fqdn, None, basedn, config)
|
||||
self.ldap_configure('CA', self.fqdn, None, basedn, config)
|
||||
|
||||
def setup_lightweight_ca_key_retrieval(self):
|
||||
if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
|
||||
|
@ -383,8 +383,8 @@ class DNSKeySyncInstance(service.Service):
|
||||
|
||||
def __enable(self):
|
||||
try:
|
||||
self.ldap_enable('DNSKeySync', self.fqdn, None,
|
||||
self.suffix, self.extra_config)
|
||||
self.ldap_configure('DNSKeySync', self.fqdn, None,
|
||||
self.suffix, self.extra_config)
|
||||
except errors.DuplicateEntry:
|
||||
logger.error("DNSKeySync service already exists")
|
||||
|
||||
|
@ -156,7 +156,7 @@ class HTTPInstance(service.Service):
|
||||
# We do not let the system start IPA components on its own,
|
||||
# Instead we reply on the IPA init script to start only enabled
|
||||
# components as found in our LDAP configuration tree
|
||||
self.ldap_enable('HTTP', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('HTTP', self.fqdn, None, self.suffix)
|
||||
|
||||
def configure_selinux_for_httpd(self):
|
||||
try:
|
||||
@ -566,7 +566,7 @@ class HTTPInstance(service.Service):
|
||||
if running:
|
||||
self.restart()
|
||||
|
||||
# disabled by default, by ldap_enable()
|
||||
# disabled by default, by ldap_configure()
|
||||
if enabled:
|
||||
self.enable()
|
||||
|
||||
|
@ -226,4 +226,11 @@ class KRAInstaller(KRAInstall):
|
||||
logger.error('%s', dedent(self.FAIL_MESSAGE))
|
||||
raise
|
||||
|
||||
# pki-spawn restarts 389-DS, reconnect
|
||||
api.Backend.ldap2.close()
|
||||
api.Backend.ldap2.connect()
|
||||
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(api.env.host)
|
||||
api.Command.dns_update_system_records()
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
@ -393,4 +393,4 @@ class KRAInstance(DogtagInstance):
|
||||
directives[nickname], cert)
|
||||
|
||||
def __enable_instance(self):
|
||||
self.ldap_enable('KRA', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('KRA', self.fqdn, None, self.suffix)
|
||||
|
@ -242,7 +242,7 @@ class KrbInstance(service.Service):
|
||||
# We do not let the system start IPA components on its own,
|
||||
# Instead we reply on the IPA init script to start only enabled
|
||||
# components as found in our LDAP configuration tree
|
||||
self.ldap_enable('KDC', self.fqdn, None, self.suffix)
|
||||
self.ldap_configure('KDC', self.fqdn, None, self.suffix)
|
||||
|
||||
def __start_instance(self):
|
||||
try:
|
||||
@ -619,7 +619,7 @@ class KrbInstance(service.Service):
|
||||
except ValueError as error:
|
||||
logger.debug("%s", error)
|
||||
|
||||
# disabled by default, by ldap_enable()
|
||||
# disabled by default, by ldap_configure()
|
||||
if enabled:
|
||||
self.enable()
|
||||
|
||||
|
@ -74,8 +74,8 @@ class ODSExporterInstance(service.Service):
|
||||
def __enable(self):
|
||||
|
||||
try:
|
||||
self.ldap_enable('DNSKeyExporter', self.fqdn, None,
|
||||
self.suffix)
|
||||
self.ldap_configure('DNSKeyExporter', self.fqdn, None,
|
||||
self.suffix)
|
||||
except errors.DuplicateEntry:
|
||||
logger.error("DNSKeyExporter service already exists")
|
||||
|
||||
|
@ -140,8 +140,8 @@ class OpenDNSSECInstance(service.Service):
|
||||
|
||||
def __enable(self):
|
||||
try:
|
||||
self.ldap_enable('DNSSEC', self.fqdn, None,
|
||||
self.suffix, self.extra_config)
|
||||
self.ldap_configure('DNSSEC', self.fqdn, None,
|
||||
self.suffix, self.extra_config)
|
||||
except errors.DuplicateEntry:
|
||||
logger.error("DNSSEC service already exists")
|
||||
|
||||
@ -372,7 +372,7 @@ class OpenDNSSECInstance(service.Service):
|
||||
|
||||
self.restore_state("kasp_db_configured") # just eat state
|
||||
|
||||
# disabled by default, by ldap_enable()
|
||||
# disabled by default, by ldap_configure()
|
||||
if enabled:
|
||||
self.enable()
|
||||
|
||||
|
@ -901,14 +901,6 @@ def install(installer):
|
||||
|
||||
if options.setup_dns:
|
||||
dns.install(False, False, options)
|
||||
else:
|
||||
# Create a BIND instance
|
||||
bind = bindinstance.BindInstance(fstore)
|
||||
bind.setup(host_name, ip_addresses, realm_name,
|
||||
domain_name, (), 'first', (),
|
||||
zonemgr=options.zonemgr,
|
||||
no_dnssec_validation=options.no_dnssec_validation)
|
||||
bind.create_file_with_system_records()
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install(False, options, fstore, api)
|
||||
@ -941,6 +933,16 @@ def install(installer):
|
||||
except Exception:
|
||||
raise ScriptError("Configuration of client side components failed!")
|
||||
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(host_name)
|
||||
api.Command.dns_update_system_records()
|
||||
|
||||
if not options.setup_dns:
|
||||
# After DNS and AD trust are configured and services are
|
||||
# enabled, create a dummy instance to dump DNS configuration.
|
||||
bind = bindinstance.BindInstance(fstore)
|
||||
bind.create_file_with_system_records()
|
||||
|
||||
# Everything installed properly, activate ipa service.
|
||||
services.knownservices.ipa.enable()
|
||||
|
||||
|
@ -1519,14 +1519,11 @@ def install(installer):
|
||||
|
||||
if options.setup_dns:
|
||||
dns.install(False, True, options, api)
|
||||
else:
|
||||
api.Command.dns_update_system_records()
|
||||
|
||||
if options.setup_adtrust:
|
||||
adtrust.install(False, options, fstore, api)
|
||||
|
||||
ca_servers = service.find_providing_servers('CA', api.Backend.ldap2, api)
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
||||
if not promote:
|
||||
# Call client install script
|
||||
@ -1555,6 +1552,11 @@ def install(installer):
|
||||
# remove the extracted replica file
|
||||
remove_replica_info_dir(installer)
|
||||
|
||||
# Enable configured services and update DNS SRV records
|
||||
service.enable_services(config.host_name)
|
||||
api.Command.dns_update_system_records()
|
||||
api.Backend.ldap2.disconnect()
|
||||
|
||||
# Everything installed properly, activate ipa service.
|
||||
services.knownservices.ipa.enable()
|
||||
|
||||
|
@ -27,6 +27,7 @@ import socket
|
||||
import time
|
||||
import traceback
|
||||
import tempfile
|
||||
import warnings
|
||||
|
||||
import six
|
||||
|
||||
@ -61,6 +62,10 @@ SERVICE_LIST = {
|
||||
'DNSKeySync': ('ipa-dnskeysyncd', 110),
|
||||
}
|
||||
|
||||
CONFIGURED_SERVICE = u'configuredService'
|
||||
ENABLED_SERVICE = 'enabledService'
|
||||
|
||||
|
||||
def print_msg(message, output_fd=sys.stdout):
|
||||
logger.debug("%s", message)
|
||||
output_fd.write(message)
|
||||
@ -122,7 +127,7 @@ def find_providing_servers(svcname, conn, api):
|
||||
"""
|
||||
dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)
|
||||
query_filter = conn.make_filter({'objectClass': 'ipaConfigObject',
|
||||
'ipaConfigString': 'enabledService',
|
||||
'ipaConfigString': ENABLED_SERVICE,
|
||||
'cn': svcname}, rules='&')
|
||||
try:
|
||||
entries, _trunc = conn.find_entries(filter=query_filter, base_dn=dn)
|
||||
@ -231,6 +236,51 @@ def set_service_entry_config(name, fqdn, config_values,
|
||||
raise e
|
||||
|
||||
|
||||
def enable_services(fqdn):
|
||||
"""Change all configured services to enabled
|
||||
|
||||
Server.ldap_configure() only marks a service as configured. Services
|
||||
are enabled at the very end of installation.
|
||||
|
||||
Note: DNS records must be updated with dns_update_system_records, too.
|
||||
|
||||
:param fqdn: hostname of server
|
||||
"""
|
||||
ldap2 = api.Backend.ldap2
|
||||
search_base = DN(('cn', fqdn), api.env.container_masters, api.env.basedn)
|
||||
search_filter = ldap2.make_filter(
|
||||
{
|
||||
'objectClass': 'ipaConfigObject',
|
||||
'ipaConfigString': CONFIGURED_SERVICE
|
||||
},
|
||||
rules='&'
|
||||
)
|
||||
entries = ldap2.get_entries(
|
||||
search_base,
|
||||
filter=search_filter,
|
||||
scope=api.Backend.ldap2.SCOPE_ONELEVEL,
|
||||
attrs_list=['cn', 'ipaConfigString']
|
||||
)
|
||||
for entry in entries:
|
||||
name = entry['cn']
|
||||
cfgstrings = entry.setdefault('ipaConfigString', [])
|
||||
for value in list(cfgstrings):
|
||||
if value.lower() == CONFIGURED_SERVICE.lower():
|
||||
cfgstrings.remove(value)
|
||||
if not case_insensitive_attr_has_value(cfgstrings, ENABLED_SERVICE):
|
||||
cfgstrings.append(ENABLED_SERVICE)
|
||||
|
||||
try:
|
||||
ldap2.update_entry(entry)
|
||||
except errors.EmptyModlist:
|
||||
logger.debug("Nothing to do for service %s", name)
|
||||
except Exception:
|
||||
logger.exception("failed to set service %s config values", name)
|
||||
raise
|
||||
else:
|
||||
logger.debug("Enabled service %s for %s", name, fqdn)
|
||||
|
||||
|
||||
class Service(object):
|
||||
def __init__(self, service_name, service_desc=None, sstore=None,
|
||||
fstore=None, api=api, realm_name=None,
|
||||
@ -578,7 +628,35 @@ class Service(object):
|
||||
self.steps = []
|
||||
|
||||
def ldap_enable(self, name, fqdn, dm_password=None, ldap_suffix='',
|
||||
config=[]):
|
||||
config=()):
|
||||
"""Legacy function, all services should use ldap_configure()
|
||||
"""
|
||||
warnings.warn(
|
||||
"ldap_enable is deprecated, use ldap_configure instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
self._ldap_enable(ENABLED_SERVICE, name, fqdn, ldap_suffix, config)
|
||||
|
||||
def ldap_configure(self, name, fqdn, dm_password=None, ldap_suffix='',
|
||||
config=()):
|
||||
"""Create or modify service entry in cn=masters,cn=ipa,cn=etc
|
||||
|
||||
Contrary to ldap_enable(), the method only sets
|
||||
ipaConfigString=configuredService. ipaConfigString=enabledService
|
||||
is set at the very end of the installation process, to ensure that
|
||||
other machines see this master/replica after it is fully installed.
|
||||
|
||||
To switch all configured services to enabled, use::
|
||||
|
||||
ipaserver.install.service.enable_services(api.env.host)
|
||||
api.Command.dns_update_system_records()
|
||||
"""
|
||||
self._ldap_enable(
|
||||
CONFIGURED_SERVICE, name, fqdn, ldap_suffix, config
|
||||
)
|
||||
|
||||
def _ldap_enable(self, value, name, fqdn, ldap_suffix, config):
|
||||
extra_config_opts = [
|
||||
' '.join([u'startOrder', unicode(SERVICE_LIST[name][1])])
|
||||
]
|
||||
@ -589,7 +667,7 @@ class Service(object):
|
||||
set_service_entry_config(
|
||||
name,
|
||||
fqdn,
|
||||
[u'enabledService'],
|
||||
[value],
|
||||
ldap_suffix=ldap_suffix,
|
||||
post_add_config=extra_config_opts)
|
||||
|
||||
@ -615,7 +693,7 @@ class Service(object):
|
||||
|
||||
# case insensitive
|
||||
for value in entry.get('ipaConfigString', []):
|
||||
if value.lower() == u'enabledservice':
|
||||
if value.lower() == ENABLED_SERVICE:
|
||||
entry['ipaConfigString'].remove(value)
|
||||
break
|
||||
|
||||
@ -728,7 +806,7 @@ class SimpleServiceInstance(Service):
|
||||
if self.gensvc_name == None:
|
||||
self.enable()
|
||||
else:
|
||||
self.ldap_enable(self.gensvc_name, self.fqdn, None, self.suffix)
|
||||
self.ldap_configure(self.gensvc_name, self.fqdn, None, self.suffix)
|
||||
|
||||
def is_installed(self):
|
||||
return self.service.is_installed()
|
||||
|
@ -15,16 +15,21 @@ IPA server roles
|
||||
""") + _("""
|
||||
Get status of roles (DNS server, CA, etc.) provided by IPA masters.
|
||||
""") + _("""
|
||||
The status of a role is either enabled, configured, or absent.
|
||||
""") + _("""
|
||||
EXAMPLES:
|
||||
""") + _("""
|
||||
Show status of 'DNS server' role on a server:
|
||||
ipa server-role-show ipa.example.com "DNS server"
|
||||
""") + _("""
|
||||
Show status of all roles containing 'AD' on a server:
|
||||
ipa server-role-find --server ipa.example.com --role='AD'
|
||||
ipa server-role-find --server ipa.example.com --role="AD trust controller"
|
||||
""") + _("""
|
||||
Show status of all configured roles on a server:
|
||||
ipa server-role-find ipa.example.com
|
||||
""") + _("""
|
||||
Show implicit IPA master role:
|
||||
ipa server-role-find --include-master
|
||||
""")
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user