summaryrefslogtreecommitdiffstats
path: root/install
diff options
context:
space:
mode:
authorPetr Vobornik <pvoborni@redhat.com>2015-06-12 15:56:30 +0200
committerPetr Vobornik <pvoborni@redhat.com>2015-06-15 16:06:48 +0200
commite9e4509b10e5064556f0aa9a6f0124f38f14b31b (patch)
tree552cdc5ce570fa919667b653ed88e60e52af7dab /install
parentd58bdf29a514a7868c63b767f4954891b10a574d (diff)
downloadfreeipa-e9e4509b10e5064556f0aa9a6f0124f38f14b31b.tar.gz
freeipa-e9e4509b10e5064556f0aa9a6f0124f38f14b31b.tar.xz
freeipa-e9e4509b10e5064556f0aa9a6f0124f38f14b31b.zip
ipa-replica-manage: adjust del to work with managed topology
Introduces new method for deletion of replica. This method is used if managed topology is enabled. part of https://fedorahosted.org/freeipa/ticket/4302 Reviewed-By: Martin Babinsky <mbabinsk@redhat.com>
Diffstat (limited to 'install')
-rwxr-xr-xinstall/tools/ipa-replica-manage229
1 files changed, 166 insertions, 63 deletions
diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index 36efda88c..57e30bc54 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -25,6 +25,7 @@ import traceback
from urllib2 import urlparse
import ldap
import socket
+import time
from ipapython import ipautil
from ipaserver.install import replication, dsinstance, installutils
@@ -564,6 +565,13 @@ def check_last_link(delrepl, realm, dirman_passwd, force):
else:
return None
+def check_last_link_managed(api, masters, hostname, force):
+ # segments = api.Command.topologysegment_find(u'realm', sizelimit=0).get('result')
+ # replica_names = [m.single_value('cn') for m in masters]
+ # orphaned = []
+ # TODO add proper graph traversing algorithm here
+ return None
+
def enforce_host_existence(host, message=None):
if host is not None and not ipautil.host_exists(host):
if message is None:
@@ -571,8 +579,161 @@ def enforce_host_existence(host, message=None):
sys.exit(message)
+def ensure_last_services(conn, hostname, masters, options):
+ """
+ 1. When deleting master, check if there will be at least one remaining
+ DNS and CA server.
+ 2. Pick CA renewal master
+
+ Return this_services, other_services, ca_hostname
+ """
+
+ this_services = []
+ other_services = []
+ ca_hostname = None
+
+ for master in masters:
+ master_cn = master['cn'][0]
+ try:
+ services = conn.get_entries(master['dn'], conn.SCOPE_ONELEVEL)
+ except errors.NotFound:
+ continue
+ services_cns = [s.single_value['cn'] for s in services]
+ if master_cn == hostname:
+ this_services = services_cns
+ else:
+ other_services.append(services_cns)
+ if ca_hostname is None and 'CA' in services_cns:
+ ca_hostname = master_cn
+
+ if 'CA' in this_services and not any(['CA' in o for o in other_services]):
+ print "Deleting this server is not allowed as it would leave your installation without a CA."
+ sys.exit(1)
+
+ other_dns = True
+ if 'DNS' in this_services and not any(['DNS' in o for o in other_services]):
+ other_dns = False
+ print "Deleting this server will leave your installation without a DNS."
+ if not options.force and not ipautil.user_input("Continue to delete?", False):
+ sys.exit("Deletion aborted")
+
+ # test if replica is not DNSSEC master
+ # allow to delete it if is last DNS server
+ if 'DNS' in this_services and other_dns and not options.force:
+ dnssec_masters = opendnssecinstance.get_dnssec_key_masters(conn)
+ if hostname in dnssec_masters:
+ print "Replica is active DNSSEC key master. Uninstall could break your DNS system."
+ sys.exit("Deletion aborted")
+
+ ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
+ if ca.is_renewal_master(hostname):
+ try:
+ ca.set_renewal_master(options.host)
+ except errors.NotFound:
+ ca.set_renewal_master(ca_hostname)
+
+ return this_services, other_services, ca_hostname
+
+
+def cleanup_server_dns_entries(realm, hostname, suffix, options):
+ try:
+ if bindinstance.dns_container_exists(options.host, suffix,
+ dm_password=options.dirman_passwd):
+ bind = bindinstance.BindInstance()
+ bind.remove_master_dns_records(hostname, realm, realm.lower())
+ bind.remove_ipa_ca_dns_records(hostname, realm.lower())
+ bind.remove_server_ns_records(hostname)
+
+ keysyncd = dnskeysyncinstance.DNSKeySyncInstance()
+ keysyncd.remove_replica_public_keys(hostname)
+ except Exception, e:
+ print "Failed to cleanup %s DNS entries: %s" % (hostname, e)
+ print "You may need to manually remove them from the tree"
+
+
def del_master(realm, hostname, options):
+ if has_managed_topology():
+ del_master_managed(realm, hostname, options)
+ else:
+ del_master_direct(realm, hostname, options)
+
+def del_master_managed(realm, hostname, options):
+ """
+ Removing of master in managed_topology
+ """
+
+ hostname_u = unicode(hostname)
+ if hostname == options.host:
+ print "Can't remove itself: %s" % (options.host)
+ sys.exit(1)
+
+ # 1. Connect to the local server
+ try:
+ thisrepl = replication.ReplicationManager(realm, options.host,
+ options.dirman_passwd)
+ except Exception as e:
+ print "Failed to connect to server %s: %s" % (options.host, e)
+ sys.exit(1)
+
+ # 2. Get all masters
+ masters = api.Command.server_find('', sizelimit=0)['result']
+
+ # 3. Check topology
+ orphans = check_last_link_managed(api, masters, hostname, options.force)
+
+ # 4. Check that we are not leaving the installation without CA and/or DNS
+ # And pick new CA master.
+ ensure_last_services(api.Backend.ldap2, hostname, masters, options)
+
+ # Save the RID value before we start deleting
+ rid = get_rid_by_host(realm, options.host, hostname,
+ options.dirman_passwd, options.nolookup)
+
+ # 5. Remove master entry. Topology plugin will remove replication agreements.
+ try:
+ api.Command.server_del(hostname_u)
+ except errors.NotFound:
+ print "Server entry already deleted: %s" % (hostname)
+
+ # 6. Cleanup
+ try:
+ thisrepl.replica_cleanup(hostname, realm, force=True)
+ except Exception, e:
+ print "Failed to cleanup %s entries: %s" % (hostname, e)
+ print "You may need to manually remove them from the tree"
+
+ # 7. Clean RUV for the deleted master
+ # Wait for topology plugin to delete segments
+ i = 0
+ while True:
+ left = api.Command.topologysegment_find(
+ u'realm', iparepltoposegmentleftnode=hostname_u, sizelimit=0)['result']
+ right = api.Command.topologysegment_find(
+ u'realm', iparepltoposegmentrightnode=hostname_u, sizelimit=0)['result']
+ if not left and not right:
+ print "Agreements deleted"
+ break
+ time.sleep(1)
+ if i == 5: # taking too long, something is wrong, report
+ print "Waiting for removal of replication agreements"
+ i += 1
+
+ # Clean RUV
+ if rid is not None:
+ try:
+ thisrepl.cleanallruv(rid)
+ except KeyboardInterrupt:
+ print "Wait for task interrupted. It will continue to run in the background"
+
+ # 8. And clean up the removed replica DNS entries if any.
+ cleanup_server_dns_entries(realm, hostname, thisrepl.suffix, options)
+
+def del_master_direct(realm, hostname, options):
+ """
+ Removing of master for realm without managed topology (domain level < 1)
+ """
+
force_del = False
delrepl = None
@@ -651,10 +812,8 @@ def del_master(realm, hostname, options):
# Check for orphans if the remote server is up.
if delrepl and not winsync:
- masters_dn = DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ipautil.realm_to_suffix(realm))
try:
- masters = delrepl.conn.get_entries(
- masters_dn, delrepl.conn.SCOPE_ONELEVEL)
+ masters = api.Command.server_find('', sizelimit=0)['result']
except Exception, e:
masters = []
print "Failed to read masters data from '%s': %s" % (
@@ -672,53 +831,9 @@ def del_master(realm, hostname, options):
print "You will need to reconfigure your replication topology to delete this server."
sys.exit(1)
- # Check that we are not leaving the installation without CA and/or DNS
- this_services = []
- other_services = []
- ca_hostname = None
-
- for master_cn in [m.single_value['cn'] for m in masters]:
- master_dn = DN(('cn', master_cn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), ipautil.realm_to_suffix(realm))
- try:
- services = delrepl.conn.get_entries(master_dn,
- delrepl.conn.SCOPE_ONELEVEL)
- except errors.NotFound:
- continue
- services_cns = [s.single_value['cn'] for s in services]
-
- if master_cn == hostname:
- this_services = services_cns
- else:
- other_services.append(services_cns)
- if ca_hostname is None and 'CA' in services_cns:
- ca_hostname = master_cn
-
- if 'CA' in this_services and not any(['CA' in o for o in other_services]):
- print "Deleting this server is not allowed as it would leave your installation without a CA."
- sys.exit(1)
-
- other_dns = True
- if 'DNS' in this_services and not any(['DNS' in o for o in other_services]):
- other_dns = False
- print "Deleting this server will leave your installation without a DNS."
- if not options.force and not ipautil.user_input("Continue to delete?", False):
- sys.exit("Deletion aborted")
-
- # test if replica is not DNSSEC master
- # allow to delete it if is last DNS server
- if 'DNS' in this_services and other_dns and not options.force:
- dnssec_masters = opendnssecinstance.get_dnssec_key_masters(delrepl.conn)
- if hostname in dnssec_masters:
- print "Replica is active DNSSEC key master. Uninstall could break your DNS system."
- sys.exit("Deletion aborted")
-
- # Pick CA renewal master
- ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
- if ca.is_renewal_master(hostname):
- try:
- ca.set_renewal_master(options.host)
- except errors.NotFound:
- ca.set_renewal_master(ca_hostname)
+ # 4. Check that we are not leaving the installation without CA and/or DNS
+ # And pick new CA master.
+ ensure_last_services(thisrepl.conn, hostname, masters, options)
else:
print "Skipping calculation to determine if one or more masters would be orphaned."
@@ -753,19 +868,7 @@ def del_master(realm, hostname, options):
print "You may need to manually remove them from the tree"
# 7. And clean up the removed replica DNS entries if any.
- try:
- if bindinstance.dns_container_exists(options.host, thisrepl.suffix,
- dm_password=options.dirman_passwd):
- bind = bindinstance.BindInstance()
- bind.remove_master_dns_records(hostname, realm, realm.lower())
- bind.remove_ipa_ca_dns_records(hostname, realm.lower())
- bind.remove_server_ns_records(hostname)
-
- keysyncd = dnskeysyncinstance.DNSKeySyncInstance()
- keysyncd.remove_replica_public_keys(hostname)
- except Exception, e:
- print "Failed to cleanup %s DNS entries: %s" % (hostname, e)
- print "You may need to manually remove them from the tree"
+ cleanup_server_dns_entries(realm, hostname, thisrepl.suffix, options)
def add_link(realm, replica1, replica2, dirman_passwd, options):