diff options
-rwxr-xr-x | install/tools/ipa-replica-manage | 85 | ||||
-rw-r--r-- | install/tools/man/ipa-replica-manage.1 | 14 |
2 files changed, 98 insertions, 1 deletions
diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage index dcd44f3c7..897d11768 100755 --- a/install/tools/ipa-replica-manage +++ b/install/tools/ipa-replica-manage @@ -72,6 +72,8 @@ def parse_options(): help="provide additional information") parser.add_option("-f", "--force", dest="force", action="store_true", default=False, help="ignore some types of errors") + parser.add_option("-c", "--cleanup", dest="cleanup", action="store_true", default=False, + help="DANGER: clean up references to a ghost master") parser.add_option("--binddn", dest="binddn", default=None, type="dn", help="Bind DN to use with remote server") parser.add_option("--bindpw", dest="bindpw", default=None, @@ -463,9 +465,53 @@ def list_clean_ruv(realm, host, dirman_passwd, verbose): print str(dn) print entry.getValue('nstasklog') +def check_last_link(delrepl, realm, dirman_passwd, force): + """ + We don't want to orphan a server when deleting another one. If you have + a topology that looks like this: + + A B + | | + | | + | | + C---- D + + If we try to delete host D it will orphan host B. + + What we need to do is if the master being deleted has only a single + agreement, connect to that master and make sure it has agreements with + more than just this master. + + @delrepl: a ReplicationManager object of the master being deleted + + returns: hostname of orphaned server or None + """ + replica_names = delrepl.find_ipa_replication_agreements() + + orphaned = [] + # Connect to each remote server and see what agreements it has + for replica in replica_names: + try: + repl = replication.ReplicationManager(realm, replica, dirman_passwd) + except ldap.SERVER_DOWN, e: + print "Unable to validate that '%s' will not be orphaned." % replica + + if not force and not ipautil.user_input("Continue to delete?", False): + sys.exit("Aborted") + continue + names = repl.find_ipa_replication_agreements() + if len(names) == 1 and names[0] == delrepl.hostname: + orphaned.append(replica) + + if len(orphaned): + return ', '.join(orphaned) + else: + return None + def del_master(realm, hostname, options): force_del = False + delrepl = None # 1. Connect to the local server try: @@ -478,7 +524,21 @@ def del_master(realm, hostname, options): # 2. Ensure we have an agreement with the master agreement = thisrepl.get_replication_agreement(hostname) if agreement is None: - sys.exit("'%s' has no replication agreement for '%s'" % (options.host, hostname)) + if options.cleanup: + """ + We have no agreement with the current master, so this is a + candidate for cleanup. This is VERY dangerous to do because it + removes that master from the list of masters. If the master + were to try to come back online it wouldn't work at all. + """ + print "Cleaning a master is irreversible." + print "This should not normally be require, so use cautiously." + if not ipautil.user_input("Continue to clean master?", False): + sys.exit("Cleanup aborted") + thisrepl.replica_cleanup(hostname, realm, force=True) + sys.exit(0) + else: + sys.exit("'%s' has no replication agreement for '%s'" % (options.host, hostname)) # 3. If an IPA agreement connect to the master to be removed. repltype = thisrepl.get_agreement_type(hostname) @@ -516,6 +576,29 @@ def del_master(realm, hostname, options): if not ipautil.user_input("Continue to delete?", False): sys.exit("Deletion aborted") + # 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.getList(masters_dn, ldap.SCOPE_ONELEVEL) + except Exception, e: + masters = [] + print "Failed to read masters data from '%s': %s" % (delrepl.hostname, convert_error(e)) + print "Skipping calculation to determine if one or more masters would be orphaned." + if not options.force: + sys.exit(1) + + # This only applies if we have more than 2 IPA servers, otherwise + # there is no chance of an orphan. + if len(masters) > 2: + orphaned_server = check_last_link(delrepl, realm, options.dirman_passwd, options.force) + if orphaned_server is not None: + print "Deleting this server will orphan '%s'. " % orphaned_server + print "You will need to reconfigure your replication topology to delete this server." + sys.exit(1) + else: + print "Skipping calculation to determine if one or more masters would be orphaned." + # Save the RID value before we start deleting if repltype == replication.IPA_REPLICA: rid = get_rid_by_host(realm, options.host, hostname, options.dirman_passwd) diff --git a/install/tools/man/ipa-replica-manage.1 b/install/tools/man/ipa-replica-manage.1 index 98d70c6fd..b750f8fc9 100644 --- a/install/tools/man/ipa-replica-manage.1 +++ b/install/tools/man/ipa-replica-manage.1 @@ -65,6 +65,14 @@ Each IPA master server has a unique replication ID. This ID is used by 389\-ds\- When a master is removed, all other masters need to remove its replication ID from the list of masters. Normally this occurs automatically when a master is deleted with ipa\-replica\-manage. If one or more masters was down or unreachable when ipa\-replica\-manage was executed then this replica ID may still exist. The clean\-ruv command may be used to clean up an unused replication ID. .TP \fBNOTE\fR: clean\-ruv is \fBVERY DANGEROUS\fR. Execution against the wrong replication ID can result in inconsistent data on that master. The master should be re\-initialized from another if this happens. +.TP +The replication topology is examined when a master is deleted and will attempt to prevent a master from being orphaned. For example, if your topology is A <\-> B <\-> C and you attempt to delete master B it will fail because that would leave masters and A and C orphaned. +.TP +The list of masters is stored in cn=masters,cn=ipa,cn=etc,dc=example,dc=com. This should be cleaned up automatically when a master is deleted. If it occurs that you have deleted the master and all the agreements but these entries still exist then you will not be able to re\-install IPA on it, the installation will fail with: +.TP +An IPA master host cannot be deleted or disabled using standard commands (host\-del, for example). +.TP +An orphaned master may be cleaned up using the del directive with the \-\-cleanup option. This will remove the entries from cn=masters,cn=ipa,cn=etc that otherwise prevent host\-del from working, its dna profile, s4u2proxy configuration, service principals and remove it from the default DUA profile defaultServerList. .SH "OPTIONS" .TP \fB\-H\fR \fIHOST\fR, \fB\-\-host\fR=\fIHOST\fR @@ -81,6 +89,9 @@ Provide additional information \fB\-f\fR, \fB\-\-force\fR Ignore some types of errors, don't prompt when deleting a master .TP +\fB\-c\fR, \fB\-\-cleanup\fR +When deleting a master with the --force flag, remove leftover references to an already deleted master. +.TP \fB\-\-binddn\fR=\fIADMIN_DN\fR Bind DN to use with remote server (default is cn=Directory Manager) \- Be careful to quote this value on the command line .TP @@ -135,6 +146,9 @@ List the replication IDs in use: # ipa\-replica\-manage list\-ruv srv1.example.com:389: 7 srv2.example.com:389: 4 +.TP +Remove references to an orphaned and deleted master: + # ipa\-replica\-manage del \-\-force \-\-cleanup master.example.com .SH "WINSYNC" Creating a Windows AD Synchronization agreement is similar to creating an IPA replication agreement, there are just a couple of extra steps. |