diff options
Diffstat (limited to 'ipaserver/install/replication.py')
-rw-r--r-- | ipaserver/install/replication.py | 204 |
1 files changed, 106 insertions, 98 deletions
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py index 38abfe210..8fe73ca77 100644 --- a/ipaserver/install/replication.py +++ b/ipaserver/install/replication.py @@ -28,17 +28,16 @@ from ipapython import services as ipaservices from ldap import modlist from ipalib import api, util, errors from ipapython import ipautil -from ipalib.dn import DN +from ipapython.dn import DN -DIRMAN_CN = "cn=directory manager" CACERT = "/etc/ipa/ca.crt" # the default container used by AD for user entries -WIN_USER_CONTAINER = "cn=Users" +WIN_USER_CONTAINER = DN(('cn', 'Users')) # the default container used by IPA for user entries -IPA_USER_CONTAINER = "cn=users,cn=accounts" +IPA_USER_CONTAINER = DN(('cn', 'users'), ('cn', 'accounts')) PORT = 636 TIMEOUT = 120 -REPL_MAN_DN = "cn=replication manager,cn=config" +REPL_MAN_DN = DN(('cn', 'replication manager'), ('cn', 'config')) IPA_REPLICA = 1 WINSYNC = 2 @@ -94,9 +93,10 @@ def enable_replication_version_checking(hostname, realm, dirman_passwd): conn.do_simple_bind(bindpw=dirman_passwd) else: conn.do_sasl_gssapi_bind() - entry = conn.search_s('cn=IPA Version Replication,cn=plugins,cn=config', ldap.SCOPE_BASE, 'objectclass=*') - if entry[0].getValue('nsslapd-pluginenabled') == 'off': - conn.modify_s(entry[0].dn, [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'on')]) + entry = conn.getEntry(DN(('cn', 'IPA Version Replication'), ('cn', 'plugins'), ('cn', 'config')), + ldap.SCOPE_BASE, 'objectclass=*') + if entry.getValue('nsslapd-pluginenabled') == 'off': + conn.modify_s(entry.dn, [(ldap.MOD_REPLACE, 'nsslapd-pluginenabled', 'on')]) conn.unbind() serverid = "-".join(realm.split(".")) ipaservices.knownservices.dirsrv.restart(instance_name=serverid) @@ -112,8 +112,7 @@ class ReplicationManager(object): self.dirman_passwd = dirman_passwd self.realm = realm self.starttls = starttls - tmp = ipautil.realm_to_suffix(realm) - self.suffix = str(DN(tmp)).lower() + self.suffix = ipautil.realm_to_suffix(realm) self.need_memberof_fixup = False # The caller is allowed to pass in an existing IPAdmin connection. @@ -150,25 +149,28 @@ class ReplicationManager(object): """ # First see if there is already one set dn = self.replica_dn() + assert isinstance(dn, DN) try: - replica = conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0] + replica = conn.getEntry(dn, ldap.SCOPE_BASE, "objectclass=*") + except errors.NotFound: + pass + else: if replica.getValue('nsDS5ReplicaId'): return int(replica.getValue('nsDS5ReplicaId')) - except ldap.NO_SUCH_OBJECT: - pass # Ok, either the entry doesn't exist or the attribute isn't set # so get it from the other master retval = -1 - dn = str(DN(('cn','replication'),('cn','etc'), self.suffix)) + dn = DN(('cn','replication'),('cn','etc'), self.suffix) try: - replica = master_conn.search_s(dn, ldap.SCOPE_BASE, "objectclass=*")[0] - if not replica.getValue('nsDS5ReplicaId'): - root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server") - raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server") - except ldap.NO_SUCH_OBJECT: + replica = master_conn.getEntry(dn, ldap.SCOPE_BASE, "objectclass=*") + except errors.NotFound: root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server") raise + else: + if replica.getValue('nsDS5ReplicaId') is None: + root_logger.debug("Unable to retrieve nsDS5ReplicaId from remote server") + raise RuntimeError("Unable to retrieve nsDS5ReplicaId from remote server") # Now update the value on the master retval = int(replica.getValue('nsDS5ReplicaId')) @@ -195,8 +197,9 @@ class ReplicationManager(object): """ filt = "(|(objectclass=nsDSWindowsReplicationAgreement)(objectclass=nsds5ReplicationAgreement))" try: - ents = self.conn.search_s("cn=mapping tree,cn=config", ldap.SCOPE_SUBTREE, filt) - except ldap.NO_SUCH_OBJECT: + ents = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')), + ldap.SCOPE_SUBTREE, filt) + except errors.NotFound: ents = [] return ents @@ -212,13 +215,13 @@ class ReplicationManager(object): filt = "(objectclass=nsds5ReplicationAgreement)" try: - ents = self.conn.search_s("cn=mapping tree,cn=config", - ldap.SCOPE_SUBTREE, filt) - except ldap.NO_SUCH_OBJECT: + ents = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')), + ldap.SCOPE_SUBTREE, filt) + except errors.NotFound: return res for ent in ents: - res.append(ent.nsds5replicahost) + res.append(ent.getValue('nsds5replicahost')) return res @@ -234,24 +237,23 @@ class ReplicationManager(object): filt = "(&(|(objectclass=nsds5ReplicationAgreement)(objectclass=nsDSWindowsReplicationAgreement))(nsDS5ReplicaHost=%s))" % hostname try: - entry = self.conn.search_s("cn=mapping tree,cn=config", - ldap.SCOPE_SUBTREE, filt) - except ldap.NO_SUCH_OBJECT: + entries = self.conn.getList(DN(('cn', 'mapping tree'), ('cn', 'config')), + ldap.SCOPE_SUBTREE, filt) + except errors.NotFound: return None - if len(entry) == 0: + if len(entries) == 0: return None else: - return entry[0] # There can be only one + return entries[0] # There can be only one def add_replication_manager(self, conn, dn, pw): """ Create a pseudo user to use for replication. """ - - edn = ldap.dn.str2dn(dn) - rdn_attr = edn[0][0][0] - rdn_val = edn[0][0][1] + assert isinstance(dn, DN) + rdn_attr = dn[0].attr + rdn_val = dn[0].value ent = ipaldap.Entry(dn) ent.setValues("objectclass", "top", "person") @@ -266,6 +268,7 @@ class ReplicationManager(object): pass def delete_replication_manager(self, conn, dn=REPL_MAN_DN): + assert isinstance(dn, DN) try: conn.delete_s(dn) except ldap.NO_SUCH_OBJECT: @@ -278,16 +281,18 @@ class ReplicationManager(object): return "2" def replica_dn(self): - return str(DN(('cn','replica'),('cn',self.suffix),('cn','mapping tree'),('cn','config'))) + return DN(('cn','replica'),('cn',self.suffix),('cn','mapping tree'),('cn','config')) def replica_config(self, conn, replica_id, replica_binddn): + assert isinstance(replica_binddn, DN) dn = self.replica_dn() + assert isinstance(dn, DN) try: entry = conn.getEntry(dn, ldap.SCOPE_BASE) managers = entry.getValues('nsDS5ReplicaBindDN') for m in managers: - if DN(replica_binddn) == DN(m): + if replica_binddn == DN(m): return # Add the new replication manager mod = [(ldap.MOD_ADD, 'nsDS5ReplicaBindDN', replica_binddn)] @@ -303,7 +308,7 @@ class ReplicationManager(object): entry = ipaldap.Entry(dn) entry.setValues('objectclass', "top", "nsds5replica", "extensibleobject") entry.setValues('cn', "replica") - entry.setValues('nsds5replicaroot', self.suffix) + entry.setValues('nsds5replicaroot', str(self.suffix)) entry.setValues('nsds5replicaid', str(replica_id)) entry.setValues('nsds5replicatype', replica_type) entry.setValues('nsds5flags', "1") @@ -313,7 +318,7 @@ class ReplicationManager(object): conn.addEntry(entry) def setup_changelog(self, conn): - dn = "cn=changelog5, cn=config" + dn = DN(('cn', 'changelog5'), ('cn', 'config')) dirpath = conn.dbdir + "/cldb" entry = ipaldap.Entry(dn) entry.setValues('objectclass', "top", "extensibleobject") @@ -325,7 +330,7 @@ class ReplicationManager(object): return def setup_chaining_backend(self, conn): - chaindn = "cn=chaining database, cn=plugins, cn=config" + chaindn = DN(('cn', 'chaining database'), ('cn', 'plugins'), ('cn', 'config')) benamebase = "chaindb" urls = [self.to_ldap_url(conn)] cn = "" @@ -334,11 +339,11 @@ class ReplicationManager(object): while not done: try: cn = benamebase + str(benum) # e.g. localdb1 - dn = "cn=" + cn + ", " + chaindn + dn = DN(('cn', cn), chaindn) entry = ipaldap.Entry(dn) entry.setValues('objectclass', 'top', 'extensibleObject', 'nsBackendInstance') entry.setValues('cn', cn) - entry.setValues('nsslapd-suffix', self.suffix) + entry.setValues('nsslapd-suffix', str(self.suffix)) entry.setValues('nsfarmserverurl', urls) entry.setValues('nsmultiplexorbinddn', self.repl_man_dn) entry.setValues('nsmultiplexorcredentials', self.repl_man_passwd) @@ -365,7 +370,7 @@ class ReplicationManager(object): def get_mapping_tree_entry(self): try: - entry = self.conn.getEntry("cn=mapping tree,cn=config", ldap.SCOPE_ONELEVEL, + entry = self.conn.getEntry(DN(('cn', 'mapping tree'), ('cn', 'config')), ldap.SCOPE_ONELEVEL, "(cn=\"%s\")" % (self.suffix)) except errors.NotFound, e: root_logger.debug("failed to find mappting tree entry for %s" % self.suffix) @@ -378,7 +383,7 @@ class ReplicationManager(object): mtent = self.get_mapping_tree_entry() dn = mtent.dn - plgent = self.conn.getEntry("cn=Multimaster Replication Plugin,cn=plugins,cn=config", + plgent = self.conn.getEntry(DN(('cn', 'Multimaster Replication Plugin'), ('cn', 'plugins'), ('cn', 'config')), ldap.SCOPE_BASE, "(objectclass=*)", ['nsslapd-pluginPath']) path = plgent.getValue('nsslapd-pluginPath') @@ -397,7 +402,7 @@ class ReplicationManager(object): self.enable_chain_on_update(chainbe) def add_passsync_user(self, conn, password): - pass_dn = "uid=passsync,cn=sysaccounts,cn=etc,%s" % self.suffix + pass_dn = DN(('uid', 'passsync'), ('cn', 'sysaccounts'), ('cn', 'etc'), self.suffix) print "The user for the Windows PassSync service is %s" % pass_dn try: conn.getEntry(pass_dn, ldap.SCOPE_BASE) @@ -414,7 +419,7 @@ class ReplicationManager(object): conn.addEntry(entry) # Add it to the list of users allowed to bypass password policy - extop_dn = "cn=ipa_pwd_extop,cn=plugins,cn=config" + extop_dn = DN(('cn', 'ipa_pwd_extop'), ('cn', 'plugins'), ('cn', 'config')) entry = conn.getEntry(extop_dn, ldap.SCOPE_BASE) pass_mgrs = entry.getValues('passSyncManagersDNs') if not pass_mgrs: @@ -435,9 +440,9 @@ class ReplicationManager(object): def setup_winsync_agmt(self, entry, win_subtree=None): if win_subtree is None: - win_subtree = WIN_USER_CONTAINER + "," + self.ad_suffix - ds_subtree = IPA_USER_CONTAINER + "," + self.suffix - windomain = '.'.join(ldap.explode_dn(self.suffix, 1)) + win_subtree = DN(WIN_USER_CONTAINER, self.ad_suffix) + ds_subtree = DN(IPA_USER_CONTAINER, self.suffix) + windomain = ipautil.suffix_to_realm(self.suffix) entry.setValues("objectclass", "nsDSWindowsReplicationAgreement") entry.setValues("nsds7WindowsReplicaSubtree", win_subtree) @@ -454,7 +459,7 @@ class ReplicationManager(object): tell which side we want. """ cn = "meTo%s" % (hostname) - dn = "cn=%s, %s" % (cn, self.replica_dn()) + dn = DN(('cn', cn), self.replica_dn()) return (cn, dn) @@ -469,6 +474,9 @@ class ReplicationManager(object): isn't a dogtag replication agreement. """ + if repl_man_dn is not None: + assert isinstance(repl_man_dn, DN) + cn, dn = self.agreement_dn(b_hostname, master=master) try: a_conn.getEntry(dn, ldap.SCOPE_BASE) @@ -482,7 +490,7 @@ class ReplicationManager(object): entry.setValues('nsds5replicahost', b_hostname) entry.setValues('nsds5replicaport', str(port)) entry.setValues('nsds5replicatimeout', str(TIMEOUT)) - entry.setValues('nsds5replicaroot', self.suffix) + entry.setValues('nsds5replicaroot', str(self.suffix)) if master is None: entry.setValues('nsDS5ReplicatedAttributeList', '(objectclass=*) $ EXCLUDE %s' % " ".join(EXCLUDES)) @@ -538,10 +546,15 @@ class ReplicationManager(object): while (retries > 0 ): root_logger.info('Getting ldap service principals for conversion: %s and %s' % (filter_a, filter_b)) - a_entry = b.search_s(self.suffix, ldap.SCOPE_SUBTREE, - filterstr=filter_a) - b_entry = a.search_s(self.suffix, ldap.SCOPE_SUBTREE, - filterstr=filter_b) + try: + a_entry = b.search_s(self.suffix, ldap.SCOPE_SUBTREE, filterstr=filter_a) + except errors.NotFound: + pass + + try: + b_entry = a.search_s(self.suffix, ldap.SCOPE_SUBTREE, filterstr=filter_b) + except errors.NotFound: + pass if a_entry and b_entry: root_logger.debug('Found both principals.') @@ -578,7 +591,10 @@ class ReplicationManager(object): """ rep_dn = self.replica_dn() + assert isinstance(rep_dn, DN) (a_dn, b_dn) = self.get_replica_principal_dns(a, b, retries=10) + assert isinstance(a_dn, DN) + assert isinstance(b_dn, DN) # Add kerberos principal DNs as valid bindDNs for replication try: @@ -623,12 +639,10 @@ class ReplicationManager(object): return self.conn.deleteEntry(dn) def delete_referral(self, hostname): - esc1_suffix = self.suffix.replace('=', '\\3D').replace(',', '\\2C') - esc2_suffix = self.suffix.replace('=', '%3D').replace(',', '%2C') - dn = 'cn=%s,cn=mapping tree,cn=config' % esc1_suffix + dn = DN(('cn', self.suffix), ('cn', 'mapping tree'), ('cn', 'config')) # TODO: should we detect proto/port somehow ? mod = [(ldap.MOD_DELETE, 'nsslapd-referral', - 'ldap://%s/%s' % (ipautil.format_netloc(hostname, 389), esc2_suffix))] + 'ldap://%s/%s' % (ipautil.format_netloc(hostname, 389), self.suffix))] try: self.conn.modify_s(dn, mod) @@ -648,9 +662,9 @@ class ReplicationManager(object): print "Error reading status from agreement", agmtdn hasError = 1 else: - refresh = entry.nsds5BeginReplicaRefresh - inprogress = entry.nsds5replicaUpdateInProgress - status = entry.nsds5ReplicaLastInitStatus + refresh = entry.getValue('nsds5BeginReplicaRefresh') + inprogress = entry.getValue('nsds5replicaUpdateInProgress') + status = entry.getValue('nsds5ReplicaLastInitStatus') if not refresh: # done - check status if not status: print "No status yet" @@ -683,10 +697,10 @@ class ReplicationManager(object): print "Error reading status from agreement", agmtdn hasError = 1 else: - inprogress = entry.nsds5replicaUpdateInProgress - status = entry.nsds5ReplicaLastUpdateStatus - start = entry.nsds5ReplicaLastUpdateStart - end = entry.nsds5ReplicaLastUpdateEnd + inprogress = entry.getValue('nsds5replicaUpdateInProgress') + status = entry.getValue('nsds5ReplicaLastUpdateStatus') + start = entry.getValue('nsds5ReplicaLastUpdateStart') + end = entry.getValue('nsds5ReplicaLastUpdateEnd') # incremental update is done if inprogress is false and end >= start done = inprogress and inprogress.lower() == 'false' and start and end and (start <= end) root_logger.info("Replication Update in progress: %s: status: %s: start: %s: end: %s" % @@ -733,6 +747,7 @@ class ReplicationManager(object): return self.wait_for_repl_init(conn, dn) def basic_replication_setup(self, conn, replica_id, repldn, replpw): + assert isinstance(repldn, DN) if replpw is not None: self.add_replication_manager(conn, repldn, replpw) self.replica_config(conn, replica_id, repldn) @@ -741,6 +756,7 @@ class ReplicationManager(object): def setup_replication(self, r_hostname, r_port=389, r_sslport=636, r_binddn=None, r_bindpw=None, starttls=False, is_cs_replica=False): + assert isinstance(r_binddn, DN) # note - there appears to be a bug in python-ldap - it does not # allow connections using two different CA certs if starttls: @@ -836,7 +852,7 @@ class ReplicationManager(object): root_logger.info("Agreement is ready, starting replication . . .") # Add winsync replica to the public DIT - dn = str(DN(('cn',ad_dc_name),('cn','replicas'),('cn','ipa'),('cn','etc'), self.suffix)) + dn = DN(('cn',ad_dc_name),('cn','replicas'),('cn','ipa'),('cn','etc'), self.suffix) entry = ipaldap.Entry(dn) entry.setValues("objectclass", ["nsContainer", "ipaConfigObject"]) entry.setValues("cn", ad_dc_name) @@ -910,17 +926,17 @@ class ReplicationManager(object): filter = '(&(nsDS5ReplicaHost=%s)' \ '(|(objectclass=nsDSWindowsReplicationAgreement)' \ '(objectclass=nsds5ReplicationAgreement)))' % hostname - entry = conn.search_s("cn=config", ldap.SCOPE_SUBTREE, filter) - if len(entry) == 0: + entries = conn.getList(DN(('cn', 'config')), ldap.SCOPE_SUBTREE, filter) + if len(entries) == 0: root_logger.error("Unable to find replication agreement for %s" % (hostname)) raise RuntimeError("Unable to proceed") - if len(entry) > 1: + if len(entries) > 1: root_logger.error("Found multiple agreements for %s" % hostname) - root_logger.error("Using the first one only (%s)" % entry[0].dn) + root_logger.error("Using the first one only (%s)" % entries[0].dn) - dn = entry[0].dn - schedule = entry[0].nsds5replicaupdateschedule + dn = entries[0].dn + schedule = entries[0].getValue('nsds5replicaupdateschedule') # On the remote chance of a match. We force a synch to happen right # now by setting the schedule to something and quickly removing it. @@ -965,16 +981,14 @@ class ReplicationManager(object): # delete master kerberos key and all its svc principals try: filter='(krbprincipalname=*/%s@%s)' % (replica, realm) - entries = self.conn.search_s(self.suffix, ldap.SCOPE_SUBTREE, - filterstr=filter) + entries = self.conn.getList(self.suffix, ldap.SCOPE_SUBTREE, + filterstr=filter) if len(entries) != 0: dnset = self.conn.get_dns_sorted_by_length(entries, reverse=True) for dns in dnset: for dn in dns: self.conn.deleteEntry(dn) - except ldap.NO_SUCH_OBJECT: - pass except errors.NotFound: pass except Exception, e: @@ -984,18 +998,18 @@ class ReplicationManager(object): err = e # remove replica memberPrincipal from s4u2proxy configuration - dn1 = DN(u'cn=ipa-http-delegation', api.env.container_s4u2proxy, self.suffix) + dn1 = DN(('cn', 'ipa-http-delegation'), api.env.container_s4u2proxy, self.suffix) member_principal1 = "HTTP/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm) - dn2 = DN(u'cn=ipa-ldap-delegation-targets', api.env.container_s4u2proxy, self.suffix) + dn2 = DN(('cn', 'ipa-ldap-delegation-targets'), api.env.container_s4u2proxy, self.suffix) member_principal2 = "ldap/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm) - dn3 = DN(u'cn=ipa-cifs-delegation-targets', api.env.container_s4u2proxy, self.suffix) + dn3 = DN(('cn', 'ipa-cifs-delegation-targets'), api.env.container_s4u2proxy, self.suffix) member_principal3 = "cifs/%(fqdn)s@%(realm)s" % dict(fqdn=replica, realm=realm) - for (dn, member_principal) in ((str(dn1), member_principal1), - (str(dn2), member_principal2), - (str(dn3), member_principal3)): + for (dn, member_principal) in ((dn1, member_principal1), + (dn2, member_principal2), + (dn3, member_principal3)): try: mod = [(ldap.MOD_DELETE, 'memberPrincipal', member_principal)] self.conn.modify_s(dn, mod) @@ -1010,16 +1024,14 @@ class ReplicationManager(object): # delete master entry with all active services try: - dn = 'cn=%s,cn=masters,cn=ipa,cn=etc,%s' % (replica, self.suffix) - entries = self.conn.search_s(dn, ldap.SCOPE_SUBTREE) + dn = DN(('cn', replica), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), self.suffix) + entries = self.conn.getList(dn, ldap.SCOPE_SUBTREE) if len(entries) != 0: dnset = self.conn.get_dns_sorted_by_length(entries, reverse=True) for dns in dnset: for dn in dns: self.conn.deleteEntry(dn) - except ldap.NO_SUCH_OBJECT: - pass except errors.NotFound: pass except Exception, e: @@ -1029,15 +1041,13 @@ class ReplicationManager(object): err = e try: - basedn = 'cn=etc,%s' % self.suffix + basedn = DN(('cn', 'etc'), self.suffix) filter = '(dnaHostname=%s)' % replica - entries = self.conn.search_s(basedn, ldap.SCOPE_SUBTREE, - filterstr=filter) + entries = self.conn.getList(basedn, ldap.SCOPE_SUBTREE, + filterstr=filter) if len(entries) != 0: for e in entries: self.conn.deleteEntry(e.dn) - except ldap.NO_SUCH_OBJECT: - pass except errors.NotFound: pass except Exception, e: @@ -1047,18 +1057,16 @@ class ReplicationManager(object): err = e try: - dn = 'cn=default,ou=profile,%s' % self.suffix - ret = self.conn.search_s(dn, ldap.SCOPE_BASE, - '(objectclass=*)')[0] - srvlist = ret.data.get('defaultServerList') - if len(srvlist) > 0: - srvlist = srvlist[0].split() + dn = DN(('cn', 'default'), ('ou', 'profile'), self.suffix) + ret = self.conn.getEntry(dn, ldap.SCOPE_BASE, '(objectclass=*)') + srvlist = ret.getValue('defaultServerList', '') + srvlist = srvlist[0].split() if replica in srvlist: srvlist.remove(replica) attr = ' '.join(srvlist) mod = [(ldap.MOD_REPLACE, 'defaultServerList', attr)] self.conn.modify_s(dn, mod) - except ldap.NO_SUCH_OBJECT: + except errors.NotFound: pass except ldap.NO_SUCH_ATTRIBUTE: pass |