When deleting a master, try to prevent orphaning other servers.

If you have a replication topology like A <-> B <-> C and you try
to delete server B that will leave A and C orphaned. It may also
prevent re-installation of a new master on B because the cn=masters
entry for it probably still exists on at least one of the other masters.

Check on each master that it connects to to ensure that it isn't the
last link, and fail if it is. If any of the masters are not up then
warn that this could be a bad thing but let the user continue if
they want.

Add a new option to the del command, --cleanup, which runs the
replica_cleanup() routine to completely clean up references to a master.

https://fedorahosted.org/freeipa/ticket/2797
This commit is contained in:
Rob Crittenden 2012-09-14 15:03:12 -04:00 committed by Martin Kosek
parent c9c55a2845
commit f695f79748
2 changed files with 98 additions and 1 deletions

View File

@ -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)

View File

@ -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.