diff options
author | Martin Babinsky <mbabinsk@redhat.com> | 2016-06-08 18:36:45 +0200 |
---|---|---|
committer | Martin Basti <mbasti@redhat.com> | 2016-06-17 18:55:19 +0200 |
commit | 31ffe1a12922b5118c847cbd6ac1ca9ea232ef94 (patch) | |
tree | 759715582fc0d8eff1e57ba0200277fefacb0e7a | |
parent | 47decc9b843b1b1d292511bcc8a24f8ac85745c0 (diff) | |
download | freeipa-31ffe1a12922b5118c847cbd6ac1ca9ea232ef94.tar.gz freeipa-31ffe1a12922b5118c847cbd6ac1ca9ea232ef94.tar.xz freeipa-31ffe1a12922b5118c847cbd6ac1ca9ea232ef94.zip |
remove the master from managed topology during uninstallation
In managed topology, calling `ipa-server-install --uninstall` will cause the
master to remove itself from the topology by calling `server_del` behind the
scenes.
https://fedorahosted.org/freeipa/ticket/5588
Reviewed-By: Martin Basti <mbasti@redhat.com>
-rw-r--r-- | ipaserver/install/server/install.py | 182 |
1 files changed, 27 insertions, 155 deletions
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 46b7190dc..930cca7b3 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -4,7 +4,6 @@ from __future__ import print_function -import gssapi import os import pickle import pwd @@ -27,15 +26,15 @@ from ipapython.ipautil import ( from ipaplatform import services from ipaplatform.paths import paths from ipaplatform.tasks import tasks -from ipalib import api, create_api, constants, errors, x509 -from ipalib.krb_utils import KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN +from ipalib import api, constants, errors, x509 from ipalib.constants import CACERT from ipalib.util import validate_domain_name import ipaclient.ntpconf from ipaserver.install import ( - bindinstance, ca, cainstance, certs, dns, dsinstance, httpinstance, - installutils, kra, krbinstance, memcacheinstance, ntpinstance, - otpdinstance, custodiainstance, replication, service, sysupgrade) + bindinstance, ca, cainstance, certs, dns, dsinstance, + httpinstance, installutils, kra, krbinstance, memcacheinstance, + ntpinstance, otpdinstance, custodiainstance, replication, service, + sysupgrade) from ipaserver.install.installutils import ( IPA_MODULES, BadHostError, get_fqdn, get_server_ip_address, is_ipa_configured, load_pkcs12, read_password, verify_fqdn, @@ -290,136 +289,21 @@ def common_cleanup(func): return decorated -def check_master_deleted(api, masters, interactive): - """ - Determine whether the IPA master was removed from the domain level 1 - topology. The function first tries to locally lookup the master host entry - and fetches host prinicipal from DS. Then we attempt to acquire host TGT, - contact the other masters one at a time and query for the existence of the - host entry for our IPA master. - - :param api: instance of API object - :param masters: list of masters to contact - :param interactive: whether run in interactive mode. The user will be - prompted for action if the removal status cannot be determined - :return: True if the master is not part of the topology anymore as - determined by the following conditions: - * the host entry does not exist in local DS - * request for host TGT fails due to missing/invalid/revoked creds - * GSSAPI connection to remote DS fails on invalid authentication - * if we are the only master - False otherwise - """ +def remove_master_from_managed_topology(api_instance, options): try: - host_princ = api.Command.host_show( - api.env.host)['result']['krbprincipalname'][0] - except errors.NotFound: - root_logger.debug( - "Host entry for {} already deleted".format(api.env.host) + # we may force the removal + # if the master was already deleted we will just get a warning + server_del_options = dict( + force=True, + ignore_topology_disconnect=options.ignore_topology_disconnect, + ignore_last_of_role=options.ignore_last_of_role ) - return True - except Exception as e: - root_logger.warning("Failed to get host principal name: {0}".format(e)) - return False - - ccache_path = os.path.join('/', 'tmp', 'krb5cc_host') - with ipautil.private_ccache(ccache_path): - # attempt to get host TGT. This can fail if the master contacts remote - # KDCs on other masters that have already cleared our master's - # principal. In that case return True - try: - ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_path) - except gssapi.exceptions.GSSError as e: - min_code = e.min_code # pylint: disable=no-member - if min_code == KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: - root_logger.debug("Host principal not found, assuming that " - "master is removed from topology") - return True - - root_logger.error( - "Kerberos authentication as '{0}' failed: {1}".format( - host_princ, e - ) - ) - return False - - last_server = True - for master in masters: - master_cn = master['cn'][0] - if api.env.host == master_cn: - continue - last_server = False - master_ldap_uri = u'ldap://{0}'.format(master_cn) + replication.run_server_del_as_cli( + api_instance, api_instance.env.host, **server_del_options) - # initialize remote api - remote_api = create_api(mode=None) - remote_api.bootstrap(ldap_uri=master_ldap_uri, in_server=True) - remote_api.finalize() - - root_logger.debug("Connecting to '{0}'...".format(master_ldap_uri)) - try: - remote_api.Backend.ldap2.connect(ccache=ccache_path) - remote_api.Command.server_show(api.env.host) - root_logger.debug( - "Server entry '{0}' present on '{1}'".format( - api.env.host, master_cn - ) - ) - return False - except (errors.NotFound, errors.ACIError): - # this may occur because the node was already deleted from the - # topology and the host principal doesn't exist - root_logger.debug( - "'{0}' was removed from topology".format( - api.env.host - ) - ) - return True - except errors.NetworkError: - # try the next master - root_logger.debug( - "Connection to remote master '{0}' failed".format( - master_cn - ) - ) - except Exception as e: - root_logger.debug( - "Unexpected error when connecting to remote master '{0}': " - "{1}".format( - master_cn, e - ) - ) - finally: - root_logger.debug("Disconnecting from {0}".format(master_cn)) - - if remote_api.Backend.ldap2.isconnected(): - remote_api.Backend.ldap2.disconnect() - - # prompt the user if we are not able to determine whether the IPA master - # was removed from topology - if not last_server: - print("WARNING: Failed to determine whether the IPA master was " - "already removed from topology.") - if (interactive and not user_input("Proceed with uninstallation?", False)): - print("Aborted") - sys.exit(1) - - return False - - return True - - -def check_topology_connectivity(api, masters): - topo_errors = replication.check_last_link_managed( - api, api.env.host, masters) - errors_after_uninstall = [topo_errors[i][1] for i in topo_errors] - - if any(errors_after_uninstall): - print("Uninstallation leads to disconnected topology") - print("Use '--ignore-topology-disconnect' to skip this check") - print("Aborting uninstallation") - sys.exit(1) + except Exception as e: + root_logger.warning("Failed to delete master: {}".format(e)) @common_cleanup @@ -1151,26 +1035,7 @@ def uninstall_check(installer): print("Aborting uninstall operation.") sys.exit(1) else: - masters = api.Command.server_find( - sizelimit=0, no_members=False)['result'] - - if not check_master_deleted(api, masters, - not options.unattended): - print("WARNING: This IPA master is still a part of the " - "replication topology.") - print("To properly remove the master entry and clean " - "up related segments, run:") - print(" $ ipa-replica-manage del {0}".format(api.env.host)) - if (not options.unattended and not user_input( - "Do you want to continue uninstallation?", False)): - print("Aborted") - sys.exit(1) - - if not options.ignore_topology_disconnect: - check_topology_connectivity(api, masters) - else: - print("Ignoring topology errors and forcing uninstall") - + remove_master_from_managed_topology(api, options) installer._fstore = fstore installer._sstore = sstore @@ -1421,6 +1286,11 @@ class Server(BaseServer): description="do not check whether server uninstall disconnects the " "topology (domain level 1+)", ) + ignore_last_of_role = Knob( + bool, False, + description="do not check whether server uninstall removes last " + "CA/DNS server or DNSSec master (domain level 1+)", + ) # dns dnssec_master = None @@ -1465,10 +1335,12 @@ class Server(BaseServer): "You must specify at least one of --forwarder, " "--auto-forwarders, or --no-forwarders options") - if self.ignore_topology_disconnect and not self.uninstalling: + any_ignore_option_true = any( + [self.ignore_topology_disconnect, self.ignore_last_of_role]) + if any_ignore_option_true and not self.uninstalling: raise RuntimeError( - "'--ignore-topology-disconnect' can be used only during " - "uninstallation") + "'--ignore-topology-disconnect/--ignore-last-of-role' options " + "can be used only during uninstallation") if self.idmax < self.idstart: raise RuntimeError( |