Fix CS replication management.

The master side should be on the local side, replica1, not the
remote. This required reversing a few master variables. This impacts
the naming of the replication agreements.

When deleting an agreement pass in the DN of that agreement
rather than trying to calculate what it is on-the-fly. We cannot
be sure which side is the master/clone and since we search for it
anyway to determine if the agreement exists it is more correct
to use what we find.

The force flag wasn't being passed into del_link so there was no way
to force a deletion.

https://fedorahosted.org/freeipa/ticket/2858
This commit is contained in:
Rob Crittenden 2012-08-30 16:24:10 -04:00
parent b3606e3d92
commit 2ca7bb3134
2 changed files with 57 additions and 23 deletions

View File

@ -176,7 +176,7 @@ def list_replicas(realm, host, replica, dirman_passwd, verbose):
peers[ent.getValue('cn')] = ['CA not configured', '']
except Exception, e:
sys.exit("Failed to get data from '%s': %s" % (host, convert_error(e)))
sys.exit("Failed to get data from '%s' while trying to list replicas: %s" % (host, convert_error(e)))
finally:
conn.unbind_s()
@ -205,18 +205,21 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
repl1 = CSReplicationManager(realm, replica1, dirman_passwd, PORT, True)
repl1.hostnames = [replica1, replica2]
type1 = repl1.get_agreement_type(replica2)
repl_list = repl1.find_ipa_replication_agreements()
if not force and len(repl_list) <= 1:
print "Cannot remove the last replication link of '%s'" % replica1
print "Please use the 'del' command to remove it from the domain"
sys.exit(1)
repl_list1 = repl1.find_replication_agreements()
# Find the DN of the replication agreement to remove
replica1_dn = None
for e in repl_list1:
if e.getValue('nsDS5ReplicaHost') == replica2:
replica1_dn = e.dn
break
if replica1_dn is None:
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
repl1.hostnames = [replica1, replica2]
except ldap.NO_SUCH_OBJECT:
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
except errors.NotFound:
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
except ldap.SERVER_DOWN, e:
sys.exit("Unable to connect to %s: %s" % (ipautil.format_netloc(replica1, PORT), convert_error(e)))
except Exception, e:
@ -226,12 +229,31 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
repl2 = CSReplicationManager(realm, replica2, dirman_passwd, PORT, True)
repl2.hostnames = [replica1, replica2]
repl_list = repl1.find_ipa_replication_agreements()
repl_list = repl2.find_replication_agreements()
# Now that we've confirmed that both hostnames are vaild, make sure
# that we aren't removing the last link from either side.
if not force and len(repl_list) <= 1:
print "Cannot remove the last replication link of '%s'" % replica2
print "Please use the 'del' command to remove it from the domain"
sys.exit(1)
if not force and len(repl_list1) <= 1:
print "Cannot remove the last replication link of '%s'" % replica1
print "Please use the 'del' command to remove it from the domain"
sys.exit(1)
# Find the DN of the replication agreement to remove
replica2_dn = None
for e in repl_list:
if e.getValue('nsDS5ReplicaHost') == replica1:
replica2_dn = e.dn
break
# This should never happen
if replica2_dn is None:
sys.exit("'%s' has no replication agreement for '%s'" % (replica1, replica2))
except ldap.NO_SUCH_OBJECT:
print "'%s' has no replication agreement for '%s'" % (replica2, replica1)
if not force:
@ -248,7 +270,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
if repl2:
failed = False
try:
repl2.delete_agreement(replica1)
repl2.delete_agreement(replica1, replica2_dn)
repl2.delete_referral(replica1)
except Exception, e:
print "Unable to remove agreement on %s: %s" % (replica2, convert_error(e))
@ -263,7 +285,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
if not repl2 and force:
print "Forcing removal on '%s'" % replica1
repl1.delete_agreement(replica2)
repl1.delete_agreement(replica2, replica1_dn)
repl1.delete_referral(replica2)
print "Deleted replication agreement from '%s' to '%s'" % (replica1, replica2)
@ -324,13 +346,13 @@ def add_link(realm, replica1, replica2, dirman_passwd, options):
except ldap.SERVER_DOWN, e:
sys.exit("Unable to connect to %s: %s" % (ipautil.format_netloc(replica2, 636), convert_error(e)))
except Exception, e:
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
sys.exit("Failed to get data while trying to bind to '%s': %s" % (replica1, convert_error(e)))
try:
repl1 = CSReplicationManager(realm, replica1, dirman_passwd, PORT, True)
entries = repl1.find_replication_agreements()
for e in entries:
if replica1 in e.dn or replica2 in e.dn:
if e.getValue('nsDS5ReplicaHost') == replica2:
sys.exit('This replication agreement already exists.')
repl1.hostnames = [replica1, replica2]
@ -339,7 +361,7 @@ def add_link(realm, replica1, replica2, dirman_passwd, options):
except ldap.SERVER_DOWN, e:
sys.exit("Unable to connect to %s %s" % (ipautil.format_netloc(replica1, PORT), convert_error(e)))
except Exception, e:
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
sys.exit("Failed to get data from '%s' while trying to get current agreements: %s" % (replica1, convert_error(e)))
repl1.setup_replication(replica2, PORT, 0, DN(('cn', 'Directory Manager')), dirman_passwd, True, True)
print "Connected '%s' to '%s'" % (replica1, replica2)
@ -436,7 +458,7 @@ def main():
elif len(args) == 2:
replica1 = host
replica2 = args[1]
del_link(realm, replica1, replica2, dirman_passwd)
del_link(realm, replica1, replica2, dirman_passwd, options.force)
try:
main()

View File

@ -648,8 +648,20 @@ class ReplicationManager(object):
except ldap.NO_SUCH_OBJECT:
pass
def delete_agreement(self, hostname):
cn, dn = self.agreement_dn(hostname)
def delete_agreement(self, hostname, dn=None):
"""
Delete a replication agreement.
@hostname: the hostname of the agreement to remove
@dn: optional dn of the agreement to remove
For IPA agreements we can easily calculate the DN of the agreement
to remove. Dogtag agreements are another matter, its agreement
names depend entirely on where it is created. In this case it is
better to pass the DN in directly.
"""
if dn is None:
cn, dn = self.agreement_dn(hostname)
return self.conn.deleteEntry(dn)
def delete_referral(self, hostname):
@ -808,11 +820,11 @@ class ReplicationManager(object):
self.setup_agreement(r_conn, self.conn.host, port=r_port,
repl_man_dn=self.repl_man_dn,
repl_man_passwd=self.repl_man_passwd,
master=True)
master=False)
self.setup_agreement(self.conn, r_hostname, port=r_port,
repl_man_dn=self.repl_man_dn,
repl_man_passwd=self.repl_man_passwd,
master=False)
master=True)
else:
self.setup_agreement(r_conn, self.conn.host, port=r_port,
repl_man_dn=self.repl_man_dn,
@ -822,7 +834,7 @@ class ReplicationManager(object):
repl_man_passwd=self.repl_man_passwd)
#Finally start replication
ret = self.start_replication(r_conn, master=True)
ret = self.start_replication(r_conn, master=False)
if ret != 0:
raise RuntimeError("Failed to start replication")