summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPetr Viktorin <pviktori@redhat.com>2012-11-20 07:47:00 -0500
committerMartin Kosek <mkosek@redhat.com>2012-11-23 12:19:20 +0100
commitbef251a13ca075718afe7503b5bbe8caa439a1ea (patch)
tree2333a05bf890711abbd9eca48b4ad6ada23466fe
parent994563bfe9df3ee37f1ccc54ec0f26ccb11f39fe (diff)
downloadfreeipa-bef251a13ca075718afe7503b5bbe8caa439a1ea.tar.gz
freeipa-bef251a13ca075718afe7503b5bbe8caa439a1ea.tar.xz
freeipa-bef251a13ca075718afe7503b5bbe8caa439a1ea.zip
Make ipa-csreplica-manage work with both merged and non-merged DBs
The ipa-csreplica-manage tool often assumed that the port numbers are the same on both sides of a replication agreement. This assumption doesn't hold in a cluster with both old-style hosts and ones with merged DBs. When managing agreements, determine the port with the PKI (or merged) DS on each master, and use it. Also, in CSReplicationManager, always use starttls rather than ldaps://.
-rwxr-xr-xinstall/tools/ipa-csreplica-manage93
-rw-r--r--ipaserver/install/replication.py24
2 files changed, 83 insertions, 34 deletions
diff --git a/install/tools/ipa-csreplica-manage b/install/tools/ipa-csreplica-manage
index 55edd1a23..db368c6fa 100755
--- a/install/tools/ipa-csreplica-manage
+++ b/install/tools/ipa-csreplica-manage
@@ -34,7 +34,6 @@ from ipalib import api, errors, util
from ipapython.dn import DN
CACERT = "/etc/ipa/ca.crt"
-PORT = dogtag.install_constants.DS_PORT
# dict of command name and tuples of min/max num of args needed
commands = {
@@ -60,10 +59,48 @@ def convert_error(exc):
else:
return str(exc)
+
+def get_cs_replication_manager(realm, host, dirman_passwd):
+ """Get a CSReplicationManager for a remote host
+
+ Detects if the host has a merged database, connects to appropriate port.
+ """
+
+ # Try merged database port first. If it has the ipaca tree, return
+ # corresponding replication manager
+ # If we can't connect to it at all, we're not dealing with an IPA master
+ # anyway; let the exception propagate up
+ # Fall back to the old PKI-only DS port. Check that it has the ipaca tree
+ # (IPA with merged DB theoretically leaves port 7389 free for anyone).
+ # If it doesn't, raise exception.
+ ports = [
+ dogtag.Dogtag10Constants.DS_PORT,
+ dogtag.Dogtag9Constants.DS_PORT,
+ ]
+ for port in ports:
+ root_logger.debug('Looking for PKI DS on %s:%s' % (host, port))
+ replication_manager = CSReplicationManager(
+ realm, host, dirman_passwd, port)
+ if replication_manager.has_ipaca():
+ root_logger.debug('PKI DS found on %s:%s' % (host, port))
+ return replication_manager
+ else:
+ root_logger.debug('PKI tree not found on %s:%s' % (host, port))
+ sys.exit('Cannot reach PKI DS at %s on ports %s' % (host, ports))
+
+
class CSReplicationManager(replication.ReplicationManager):
+ """ReplicationManager specific to CA agreements
- def __init__(self, realm, hostname, dirman_passwd, port=PORT, starttls=True):
- super(CSReplicationManager, self).__init__(realm, hostname, dirman_passwd, port, starttls)
+ Note that in most cases we don't know if we're connecting to an old-style
+ separate PKI DS, or to a host with a merged DB.
+ Use the get_cs_replication_manager function to determine this and return
+ an appropriate CSReplicationManager.
+ """
+
+ def __init__(self, realm, hostname, dirman_passwd, port):
+ super(CSReplicationManager, self).__init__(
+ realm, hostname, dirman_passwd, port, starttls=True)
self.suffix = DN(('o', 'ipaca'))
self.hostnames = [] # set before calling or agreement_dn() will fail
@@ -106,17 +143,26 @@ class CSReplicationManager(replication.ReplicationManager):
raise errors.NotFound(reason='No agreement found for %s' % hostname)
- def delete_referral(self, hostname):
+ def delete_referral(self, hostname, port):
dn = DN(('cn', self.suffix), ('cn', 'mapping tree'), ('cn', 'config'))
- # TODO: should we detect proto/port somehow ?
+ # TODO: should we detect proto somehow ?
mod = [(ldap.MOD_DELETE, 'nsslapd-referral',
- 'ldap://%s/%s' % (ipautil.format_netloc(hostname, PORT), self.suffix))]
+ 'ldap://%s/%s' % (ipautil.format_netloc(hostname, port), self.suffix))]
try:
self.conn.modify_s(dn, mod)
except Exception, e:
root_logger.debug("Failed to remove referral value: %s" % convert_error(e))
+ def has_ipaca(self):
+ try:
+ entry = self.conn.getEntry(self.suffix, ldap.SCOPE_BASE)
+ except errors.NotFound:
+ return False
+ else:
+ return True
+
+
def parse_options():
from optparse import OptionParser
@@ -185,7 +231,7 @@ def list_replicas(realm, host, replica, dirman_passwd, verbose):
print '%s: %s' % (k, p[0])
return
- repl = CSReplicationManager(realm, replica, dirman_passwd, PORT, True)
+ repl = get_cs_replication_manager(realm, replica, dirman_passwd)
entries = repl.find_replication_agreements()
for entry in entries:
@@ -202,7 +248,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
repl2 = None
try:
- repl1 = CSReplicationManager(realm, replica1, dirman_passwd, PORT, True)
+ repl1 = get_cs_replication_manager(realm, replica1, dirman_passwd)
repl1.hostnames = [replica1, replica2]
@@ -221,12 +267,13 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
repl1.hostnames = [replica1, replica2]
except ldap.SERVER_DOWN, e:
- sys.exit("Unable to connect to %s: %s" % (ipautil.format_netloc(replica1, PORT), convert_error(e)))
+ sys.exit("Unable to connect to %s: %s" % (replica1, convert_error(e)))
except Exception, e:
sys.exit("Failed to get data from '%s': %s" % (replica1, convert_error(e)))
try:
- repl2 = CSReplicationManager(realm, replica2, dirman_passwd, PORT, True)
+ repl2 = get_cs_replication_manager(realm, replica2, dirman_passwd)
+
repl2.hostnames = [replica1, replica2]
repl_list = repl2.find_replication_agreements()
@@ -271,7 +318,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
failed = False
try:
repl2.delete_agreement(replica1, replica2_dn)
- repl2.delete_referral(replica1)
+ repl2.delete_referral(replica1, repl1.port)
except Exception, e:
print "Unable to remove agreement on %s: %s" % (replica2, convert_error(e))
failed = True
@@ -286,7 +333,7 @@ def del_link(realm, replica1, replica2, dirman_passwd, force=False):
print "Forcing removal on '%s'" % replica1
repl1.delete_agreement(replica2, replica1_dn)
- repl1.delete_referral(replica2)
+ repl1.delete_referral(replica2, repl2.port)
print "Deleted replication agreement from '%s' to '%s'" % (replica1, replica2)
@@ -298,8 +345,8 @@ def del_master(realm, hostname, options):
# 1. Connect to the local dogtag DS server
try:
- thisrepl = CSReplicationManager(realm, options.host,
- options.dirman_passwd)
+ thisrepl = get_cs_replication_manager(realm, options.host,
+ options.dirman_passwd)
except Exception, e:
sys.exit("Failed to connect to server %s: %s" % (options.host, convert_error(e)))
@@ -309,7 +356,8 @@ def del_master(realm, hostname, options):
# 3. Connect to the dogtag DS to be removed.
try:
- delrepl = CSReplicationManager(realm, hostname, options.dirman_passwd)
+ delrepl = get_cs_replication_manager(realm, hostname,
+ options.dirman_passwd)
except Exception, e:
if not options.force:
print "Unable to delete replica %s: %s" % (hostname, convert_error(e))
@@ -333,6 +381,7 @@ def del_master(realm, hostname, options):
sys.exit("There were issues removing a connection: %s" % convert_error(e))
def add_link(realm, replica1, replica2, dirman_passwd, options):
+ repl2 = get_cs_replication_manager(realm, replica2, dirman_passwd)
try:
conn = ipaldap.IPAdmin(replica2, 636, cacert=CACERT)
conn.do_simple_bind(bindpw=dirman_passwd)
@@ -349,7 +398,7 @@ def add_link(realm, replica1, replica2, dirman_passwd, options):
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)
+ repl1 = get_cs_replication_manager(realm, replica1, dirman_passwd)
entries = repl1.find_replication_agreements()
for e in entries:
if e.getValue('nsDS5ReplicaHost') == replica2:
@@ -359,11 +408,13 @@ def add_link(realm, replica1, replica2, dirman_passwd, options):
except ldap.NO_SUCH_OBJECT:
sys.exit("Cannot find replica '%s'" % replica1)
except ldap.SERVER_DOWN, e:
- sys.exit("Unable to connect to %s %s" % (ipautil.format_netloc(replica1, PORT), convert_error(e)))
+ sys.exit("Unable to connect to %s: %s" % (replica1, convert_error(e)))
except Exception, 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)
+ repl1.setup_replication(
+ replica2, repl2.port, 0, DN(('cn', 'Directory Manager')),
+ dirman_passwd, is_cs_replica=True, local_port=repl1.port)
print "Connected '%s' to '%s'" % (replica1, replica2)
def re_initialize(realm, options):
@@ -371,8 +422,8 @@ def re_initialize(realm, options):
if not options.fromhost:
sys.exit("re-initialize requires the option --from <host name>")
- repl = CSReplicationManager(realm, options.fromhost, options.dirman_passwd,
- PORT, True)
+ repl = get_cs_replication_manager(realm, options.fromhost,
+ options.dirman_passwd)
thishost = installutils.get_fqdn()
@@ -389,7 +440,7 @@ def re_initialize(realm, options):
def force_sync(realm, thishost, fromhost, dirman_passwd):
- repl = CSReplicationManager(realm, fromhost, dirman_passwd, PORT, True)
+ repl = get_cs_replication_manager(realm, fromhost, dirman_passwd)
try:
repl.force_sync(repl.conn, thishost)
except Exception, e:
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index dfc3c7716..1c90173d2 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -128,8 +128,8 @@ class ReplicationManager(object):
# If we are passed a password we'll use it as the DM password
# otherwise we'll do a GSSAPI bind.
if starttls:
- self.conn = ipaldap.IPAdmin(hostname, port=port)
- ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
+ self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT,
+ protocol='ldap')
self.conn.start_tls_s()
else:
self.conn = ipaldap.IPAdmin(hostname, port=port, cacert=CACERT)
@@ -815,17 +815,16 @@ class ReplicationManager(object):
self.setup_changelog(conn)
def setup_replication(self, r_hostname, r_port=389, r_sslport=636,
- r_binddn=None, r_bindpw=None, starttls=False,
- is_cs_replica=False):
+ r_binddn=None, r_bindpw=None,
+ is_cs_replica=False, local_port=None):
assert isinstance(r_binddn, DN)
+ if local_port is None:
+ local_port = r_port
# note - there appears to be a bug in python-ldap - it does not
# allow connections using two different CA certs
- if starttls:
- r_conn = ipaldap.IPAdmin(r_hostname, port=r_port)
- ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, CACERT)
- r_conn.start_tls_s()
- else:
- r_conn = ipaldap.IPAdmin(r_hostname, port=r_sslport, cacert=CACERT)
+ r_conn = ipaldap.IPAdmin(r_hostname, port=r_port, cacert=CACERT,
+ protocol='ldap')
+ r_conn.start_tls_s()
if r_bindpw:
r_conn.do_simple_bind(binddn=r_binddn, bindpw=r_bindpw)
@@ -843,7 +842,7 @@ class ReplicationManager(object):
self.repl_man_dn, self.repl_man_passwd)
if is_cs_replica:
- self.setup_agreement(r_conn, self.conn.host, port=r_port,
+ self.setup_agreement(r_conn, self.conn.host, port=local_port,
repl_man_dn=self.repl_man_dn,
repl_man_passwd=self.repl_man_passwd,
master=False)
@@ -852,7 +851,7 @@ class ReplicationManager(object):
repl_man_passwd=self.repl_man_passwd,
master=True)
else:
- self.setup_agreement(r_conn, self.conn.host, port=r_port,
+ self.setup_agreement(r_conn, self.conn.host, port=local_port,
repl_man_dn=self.repl_man_dn,
repl_man_passwd=self.repl_man_passwd)
self.setup_agreement(self.conn, r_hostname, port=r_port,
@@ -1207,4 +1206,3 @@ class ReplicationManager(object):
print "This may be safely interrupted with Ctrl+C"
self.conn.checkTask(dn, dowait=True)
-