Refactor some replication code

This simplifies or rationalizes some code in order to make it easier to change
it to fix bug #690
This commit is contained in:
Simo Sorce committed 2011-01-14 14:06:56 -05:00
1 parent a68b2d2aa3
commit 2255479ef0
3 files changed
+137 -155

No files matched your search

+6 -8
View File
@@ -287,14 +287,12 @@ class DsInstance(service.Service):
def __setup_replica(self):
try:
repl = replication.ReplicationManager(self.fqdn, self.dm_password)
ret = repl.setup_replication(self.master_fqdn, self.realm_name)
except Exception, e:
logging.debug("Connection error: %s" % e)
raise RuntimeError("Unable to connect to LDAP server %s." % self.fqdn)
if ret != 0:
raise RuntimeError("Failed to start replication")
repl = replication.ReplicationManager(self.realm_name,
self.fqdn,
self.dm_password)
repl.setup_replication(self.master_fqdn,
"cn=Directory Manager",
self.dm_password)
def __enable(self):
self.backup_state("enabled", self.is_enabled())
+88 -83
View File
@@ -56,9 +56,11 @@ def check_replication_plugin():
class ReplicationManager:
"""Manage replication agreements between DS servers, and sync
agreements with Windows servers"""
def __init__(self, hostname, dirman_passwd):
def __init__(self, realm, hostname, dirman_passwd):
self.hostname = hostname
self.dirman_passwd = dirman_passwd
tmp = util.realm_to_suffix(realm)
self.suffix = ipaldap.IPAdmin.normalizeDN(tmp)
# If we are passed a password we'll use it as the DM password
# otherwise we'll do a GSSAPI bind.
@@ -74,7 +76,6 @@ class ReplicationManager:
# at runtime if you really want
self.repl_man_dn = "cn=replication manager,cn=config"
self.repl_man_cn = "replication manager"
self.suffix = ""
def _get_replica_id(self, conn, master_conn):
"""
@@ -157,25 +158,25 @@ class ReplicationManager:
return res
def add_replication_manager(self, conn, passwd=None):
def add_replication_manager(self, conn, dn, pw):
"""
Create a pseudo user to use for replication. If no password
is provided the directory manager password will be used.
Create a pseudo user to use for replication.
"""
if passwd:
self.repl_man_passwd = passwd
edn = ldap.dn.str2dn(dn)
rdn_attr = edn[0][0][0]
rdn_val = edn[0][0][1]
ent = ipaldap.Entry(self.repl_man_dn)
ent = ipaldap.Entry(dn)
ent.setValues("objectclass", "top", "person")
ent.setValues("cn", self.repl_man_cn)
ent.setValues("userpassword", self.repl_man_passwd)
ent.setValues(rdn_attr, rdn_val)
ent.setValues("userpassword", pw)
ent.setValues("sn", "replication manager pseudo user")
try:
conn.add_s(ent)
except ldap.ALREADY_EXISTS:
# should we set the password here?
conn.modify_s(dn, [(ldap.MOD_REPLACE, "userpassword", pw)])
pass
def delete_replication_manager(self, conn, dn="cn=replication manager,cn=config"):
@@ -193,7 +194,7 @@ class ReplicationManager:
def replica_dn(self):
return 'cn=replica, cn="%s", cn=mapping tree, cn=config' % self.suffix
def local_replica_config(self, conn, replica_id):
def replica_config(self, conn, replica_id, replica_binddn):
dn = self.replica_dn()
try:
@@ -212,7 +213,7 @@ class ReplicationManager:
entry.setValues('nsds5replicaid', str(replica_id))
entry.setValues('nsds5replicatype', replica_type)
entry.setValues('nsds5flags', "1")
entry.setValues('nsds5replicabinddn', [self.repl_man_dn])
entry.setValues('nsds5replicabinddn', [replica_binddn])
entry.setValues('nsds5replicalegacyconsumer', "off")
conn.add_s(entry)
@@ -338,22 +339,18 @@ class ReplicationManager:
except ldap.TYPE_OR_VALUE_EXISTS:
logging.debug("passsync aci already exists in suffix %s on %s" % (self.suffix, conn.host))
def setup_winsync_agmt(self, entry, **kargs):
def setup_winsync_agmt(self, entry, win_subtree=None):
if win_subtree is None:
win_subtree = WIN_USER_CONTAINER + "," + self.suffix
ds_subtree = IPA_USER_CONTAINER + "," + self.suffix
windomain = '.'.join(ldap.explode_dn(self.suffix, 1))
entry.setValues("objectclass", "nsDSWindowsReplicationAgreement")
entry.setValues("nsds7WindowsReplicaSubtree",
kargs.get("win_subtree",
WIN_USER_CONTAINER + "," + self.suffix))
entry.setValues("nsds7DirectoryReplicaSubtree",
kargs.get("ds_subtree",
IPA_USER_CONTAINER + "," + self.suffix))
entry.setValues("nsds7WindowsReplicaSubtree", win_subtree)
entry.setValues("nsds7DirectoryReplicaSubtree", ds_subtree)
# for now, just sync users and ignore groups
entry.setValues("nsds7NewWinUserSyncEnabled", kargs.get('newwinusers', 'true'))
entry.setValues("nsds7NewWinGroupSyncEnabled", kargs.get('newwingroups', 'false'))
windomain = ''
if kargs.has_key('windomain'):
windomain = kargs['windomain']
else:
windomain = '.'.join(ldap.explode_dn(self.suffix, 1))
entry.setValues("nsds7NewWinUserSyncEnabled", 'true')
entry.setValues("nsds7NewWinGroupSyncEnabled", 'false')
entry.setValues("nsds7WindowsDomain", windomain)
def agreement_dn(self, hostname, port=PORT):
@@ -362,7 +359,9 @@ class ReplicationManager:
return (cn, dn)
def setup_agreement(self, a, b, **kargs):
def setup_agreement(self, a, b,
repl_man_dn=None, repl_man_passwd=None,
iswinsync=False, win_subtree=None):
cn, dn = self.agreement_dn(b.host)
try:
a.getEntry(dn, ldap.SCOPE_BASE)
@@ -370,10 +369,11 @@ class ReplicationManager:
except errors.NotFound:
pass
iswinsync = kargs.get("winsync", False)
repl_man_dn = kargs.get("binddn", self.repl_man_dn)
repl_man_passwd = kargs.get("bindpw", self.repl_man_passwd)
port = kargs.get("port", PORT)
port = PORT
if repl_man_dn is None:
repl_man_dn = self.repl_man_dn
if repl_man_passwd is None:
repl_man_passwd = self.repl_man_passwd
# List of attributes that need to be excluded from replication.
excludes = ('memberof', 'entryusn',
@@ -397,7 +397,7 @@ class ReplicationManager:
'(objectclass=*) $ EXCLUDE %s' % " ".join(excludes))
entry.setValues('description', "me to %s%d" % (b.host, port))
if iswinsync:
self.setup_winsync_agmt(entry, **kargs)
self.setup_winsync_agmt(entry, win_subtree)
a.add_s(entry)
@@ -517,61 +517,70 @@ class ReplicationManager:
return self.wait_for_repl_init(other_conn, dn)
def basic_replication_setup(self, conn, replica_id):
self.add_replication_manager(conn)
self.local_replica_config(conn, replica_id)
def basic_replication_setup(self, conn, replica_id, repldn, replpw):
self.add_replication_manager(conn, repldn, replpw)
self.replica_config(conn, replica_id, repldn)
self.setup_changelog(conn)
def setup_replication(self, other_hostname, realm_name, **kargs):
"""
NOTES:
- the directory manager password needs to be the same on
both directories. Or use the optional binddn and bindpw
"""
iswinsync = kargs.get("winsync", False)
oth_port = kargs.get("port", PORT)
oth_cacert = kargs.get("cacert", CACERT)
oth_binddn = kargs.get("binddn", DIRMAN_CN)
oth_bindpw = kargs.get("bindpw", self.dirman_passwd)
def setup_replication(self, r_hostname, r_binddn=None, r_bindpw=None):
# note - there appears to be a bug in python-ldap - it does not
# allow connections using two different CA certs
other_conn = ipaldap.IPAdmin(other_hostname, port=oth_port, cacert=oth_cacert)
r_conn = ipaldap.IPAdmin(r_hostname, port=PORT, cacert=CACERT)
if r_bindpw:
r_conn.do_simple_bind(binddn=r_binddn, bindpw=r_bindpw)
else:
r_conn.sasl_interactive_bind_s('', SASL_AUTH)
#Setup the first half
l_id = self._get_replica_id(self.conn, r_conn)
self.basic_replication_setup(self.conn, l_id,
self.repl_man_dn, self.repl_man_passwd)
# Now setup the other half
r_id = self._get_replica_id(r_conn, r_conn)
self.basic_replication_setup(r_conn, r_id,
self.repl_man_dn, self.repl_man_passwd)
self.setup_agreement(r_conn, self.conn)
self.setup_agreement(self.conn, r_conn)
#Finally start replication
ret = self.start_replication(r_conn)
if ret != 0:
raise RuntimeError("Failed to start replication")
def setup_winsync_replication(self,
ad_dc_name, ad_binddn, ad_pwd,
passsync_pw, ad_subtree,
cacert=CACERT):
try:
if oth_bindpw:
other_conn.do_simple_bind(binddn=oth_binddn, bindpw=oth_bindpw)
else:
other_conn.sasl_interactive_bind_s('', SASL_AUTH)
# Validate AD connection
ad_conn = ipaldap.IPAdmin(ad_dc_name, port=636, cacert=cacert)
ad_conn.do_simple_bind(binddn=ad_binddn, bindpw=ad_pwd)
except Exception, e:
if iswinsync:
logging.info("Could not validate connection to remote server %s:%d - continuing" %
(other_hostname, oth_port))
logging.info("The error was: %s" % e)
else:
raise e
logging.info("Failed to connect to AD server %s" % ad_dc_name)
logging.info("The error was: %s" % e)
logging.info("Continuning ...")
self.suffix = ipaldap.IPAdmin.normalizeDN(util.realm_to_suffix(realm_name))
# Setup the only half.
# there is no other side to get a replica ID from
# So we generate one locally
replica_id = self._get_replica_id(self.conn, self.conn)
self.basic_replication_setup(self.conn, replica_id)
if not iswinsync:
local_id = self._get_replica_id(self.conn, other_conn)
else:
# there is no other side to get a replica ID from
local_id = self._get_replica_id(self.conn, self.conn)
self.basic_replication_setup(self.conn, local_id)
#now add a passync user allowed to access the AD server
self.add_passsync_user(self.conn, passsync_pw)
self.setup_agreement(self.conn, ad_conn,
repl_man_dn=ad_binddn, repl_man_passwd=ad_pwd,
iswinsync=True, win_subtree=ad_subtree)
logging.info("Added new sync agreement, waiting for it to become ready . . .")
cn, dn = self.agreement_dn(ad_dc_name)
self.wait_for_repl_update(self.conn, dn, 30)
logging.info("Agreement is ready, starting replication . . .")
if not iswinsync:
other_id = self._get_replica_id(other_conn, other_conn)
self.basic_replication_setup(other_conn, other_id)
self.setup_agreement(other_conn, self.conn)
self.setup_agreement(self.conn, other_conn)
return self.start_replication(other_conn)
else:
self.add_passsync_user(self.conn, kargs.get("passsync"))
self.setup_agreement(self.conn, other_conn, **kargs)
logging.info("Added new sync agreement, waiting for it to become ready . . .")
cn, dn = self.agreement_dn(other_hostname)
self.wait_for_repl_update(self.conn, dn, 30)
logging.info("Agreement is ready, starting replication . . .")
return self.start_replication(self.conn, other_conn)
#Finally start replication
return self.start_replication(self.conn, ad_conn,
self.repl_man_dn, self.repl_man_passwd)
def initialize_replication(self, dn, conn):
mod = [(ldap.MOD_ADD, 'nsds5BeginReplicaRefresh', 'start')]
@@ -618,10 +627,6 @@ class ReplicationManager:
if replica == self.hostname:
raise RuntimeError("Can't cleanup self")
if not self.suffix or self.suffix == "":
self.suffix = util.realm_to_suffix(realm)
self.suffix = ipaldap.IPAdmin.normalizeDN(self.suffix)
# delete master kerberos key and all its svc principals
try:
filter='(krbprincipalname=*/%s@%s)' % (replica, realm)