def test_update_complex(self): # compare two entries created with different methods nsuffix, replid, replicatype = "dc=example,dc=com", 5, dsadmin.REPLICA_RDWR_TYPE binddnlist, legacy = ['uid=pippo, cn=config'], 'off' dn = "dc=example,dc=com" entry = Entry(dn) entry.setValues( 'objectclass', "top", "nsds5replica", "extensibleobject") entry.setValues('cn', "replica") entry.setValues('nsds5replicaroot', nsuffix) entry.setValues('nsds5replicaid', str(replid)) entry.setValues('nsds5replicatype', str(replicatype)) entry.setValues('nsds5flags', "1") entry.setValues('nsds5replicabinddn', binddnlist) entry.setValues('nsds5replicalegacyconsumer', legacy) uentry = Entry(( dn, { 'objectclass': ["top", "nsds5replica", "extensibleobject"], 'cn': ["replica"], }) ) log.debug("Entry created with dict:", uentry) # Entry.update *replaces*, so be careful with multi-valued attrs uentry.update({ 'nsds5replicaroot': nsuffix, 'nsds5replicaid': str(replid), 'nsds5replicatype': str(replicatype), 'nsds5flags': '1', 'nsds5replicabinddn': binddnlist, 'nsds5replicalegacyconsumer': legacy }) uentry_s, entry_s = map(str, (uentry, entry)) assert uentry_s == entry_s, "Mismatching entries [%r] vs [%r]" % ( uentry, entry)
def test_update_complex(self): # compare two entries created with different methods nsuffix, replid, replicatype = "dc=example,dc=com", 5, dsadmin.REPLICA_RDWR_TYPE binddnlist, legacy = ['uid=pippo, cn=config'], 'off' dn = "dc=example,dc=com" entry = Entry(dn) entry.setValues( 'objectclass', "top", "nsds5replica", "extensibleobject") entry.setValues('cn', "replica") entry.setValues('nsds5replicaroot', nsuffix) entry.setValues('nsds5replicaid', str(replid)) entry.setValues('nsds5replicatype', str(replicatype)) entry.setValues('nsds5flags', "1") entry.setValues('nsds5replicabinddn', binddnlist) entry.setValues('nsds5replicalegacyconsumer', legacy) uentry = Entry(( dn, { 'objectclass': ["top", "nsds5replica", "extensibleobject"], 'cn': ["replica"], }) ) print uentry # Entry.update *replaces*, so be careful with multi-valued attrs uentry.update({ 'nsds5replicaroot': nsuffix, 'nsds5replicaid': str(replid), 'nsds5replicatype': str(replicatype), 'nsds5flags': '1', 'nsds5replicabinddn': binddnlist, 'nsds5replicalegacyconsumer': legacy }) uentry_s, entry_s = map(str, (uentry, entry)) assert uentry_s == entry_s, "Mismatching entries [%r] vs [%r]" % ( uentry, entry)
def setup_mt(self, suffix, bename, parent=None): """Setup a suffix with the given backend-name. @param suffix @param bename @param parent - the parent suffix @param verbose - None This method does not create the matching entry in the tree, nor the given backend. Both should be created apart. Ex. setup_mt(suffix='o=addressbook1', bename='addressbook1') creates: - the mapping in "cn=mapping tree,cn=config" you have to create: - the backend - the ldap entry "o=addressbook1" *after* """ nsuffix = normalizeDN(suffix) #escapedn = escapeDNValue(nsuffix) if parent: nparent = normalizeDN(parent) else: nparent = "" filt = suffixfilt(suffix) # if suffix exists, return try: entry = self.conn.getEntry( DN_MAPPING_TREE, ldap.SCOPE_SUBTREE, filt) return entry except NoSuchEntryError: entry = None # fix me when we can actually used escaped DNs #dn = "cn=%s,cn=mapping tree,cn=config" % escapedn dn = ','.join(('cn="%s"' % nsuffix, DN_MAPPING_TREE)) entry = Entry(dn) entry.update({ 'objectclass': ['top', 'extensibleObject', 'nsMappingTree'], 'nsslapd-state': 'backend', # the value in the dn has to be DN escaped # internal code will add the quoted value - unquoted value is useful for searching 'cn': nsuffix, 'nsslapd-backend': bename }) #entry.setValues('cn', [escapedn, nsuffix]) # the value in the dn has to be DN escaped # the other value can be the unescaped value if parent: entry.setValues('nsslapd-parent-suffix', nparent) try: self.log.debug("Creating entry: %r" % entry) self.conn.add_s(entry) except ldap.LDAPError, e: raise ldap.LDAPError("Error adding suffix entry " + dn, e)
def test_update(self): expected = 'pluto minnie' given = {'cn': expected} t = ('o=pippo', { 'o': ['pippo'], 'objectclass': ['organization', 'top'] }) e = Entry(t) e.update(given) assert e.cn == expected, "Bad cn: %s, expected: %s" % (e.cn, expected)
def enable_ssl(self, secport=636, secargs=None): """Configure SSL support into cn=encryption,cn=config. secargs is a dict like { 'nsSSLPersonalitySSL': 'Server-Cert' } """ self.log.debug("configuring SSL with secargs:%r" % secargs) secargs = secargs or {} dn_enc = 'cn=encryption,cn=config' ciphers = '-rsa_null_md5,+rsa_rc4_128_md5,+rsa_rc4_40_md5,+rsa_rc2_40_md5,+rsa_des_sha,' + \ '+rsa_fips_des_sha,+rsa_3des_sha,+rsa_fips_3des_sha,' + \ '+tls_rsa_export1024_with_rc4_56_sha,+tls_rsa_export1024_with_des_cbc_sha' mod = [(ldap.MOD_REPLACE, 'nsSSL3', secargs.get('nsSSL3', 'on')), (ldap.MOD_REPLACE, 'nsSSLClientAuth', secargs.get('nsSSLClientAuth', 'allowed')), (ldap.MOD_REPLACE, 'nsSSL3Ciphers', secargs.get('nsSSL3Ciphers', ciphers))] self.conn.modify_s(dn_enc, mod) dn_rsa = 'cn=RSA,cn=encryption,cn=config' e_rsa = Entry(dn_rsa) e_rsa.update({ 'objectclass': ['top', 'nsEncryptionModule'], 'nsSSLPersonalitySSL': secargs.get('nsSSLPersonalitySSL', 'Server-Cert'), 'nsSSLToken': secargs.get('nsSSLToken', 'internal (software)'), 'nsSSLActivation': secargs.get('nsSSLActivation', 'on') }) try: self.conn.add_s(e_rsa) except ldap.ALREADY_EXISTS: pass mod = [ (ldap.MOD_REPLACE, 'nsslapd-security', secargs.get('nsslapd-security', 'on')), (ldap.MOD_REPLACE, 'nsslapd-ssl-check-hostname', secargs.get('nsslapd-ssl-check-hostname', 'off')), (ldap.MOD_REPLACE, 'nsslapd-secureport', str(secport)) ] self.log.debug("trying to modify %r with %r" % (DN_CONFIG, mod)) self.conn.modify_s(DN_CONFIG, mod) fields = 'nsslapd-security nsslapd-ssl-check-hostname'.split() return self.conn.getEntry(DN_CONFIG, attrlist=fields)
def changelog(self, dbname='changelogdb'): """Add and return the replication changelog entry. If dbname starts with "/" then it's considered a full path, otherwise it's relative to self.dbdir """ dn = DN_CHANGELOG dirpath = os.path.join(self.conn.dbdir, dbname) entry = Entry(dn) entry.update({ 'objectclass': ("top", "extensibleobject"), 'cn': "changelog5", 'nsslapd-changelogdir': dirpath }) self.log.debug("adding changelog entry: %r" % entry) try: self.conn.add_s(entry) except ldap.ALREADY_EXISTS: self.log.warn("entry %s already exists" % dn) return self.conn._test_entry(dn, ldap.SCOPE_BASE)
def setup(): # uses an existing 389 instance # add a suffix # add an agreement # This setup is quite verbose but to test dsadmin method we should # do things manually. A better solution would be to use an LDIF. global conn conn = DSAdmin(**config.auth) conn.verbose = True conn.added_entries = [] conn.added_backends = set(['o=mockbe1']) conn.added_replicas = [] # add a backend for testing ruv and agreements addbackend_harn(conn, 'testReplica') # add another backend for testing replica.add() addbackend_harn(conn, 'testReplicaCreation') # replication needs changelog conn.replica.changelog() # add rmanager entry try: conn.add_s(Entry((DN_RMANAGER, { 'objectclass': "top person inetOrgPerson".split(), 'sn': ["bind dn pseudo user"], 'cn': 'replication manager', 'uid': 'rmanager' })) ) conn.added_entries.append(DN_RMANAGER) except ldap.ALREADY_EXISTS: pass # add a master replica entry # to test ruv and agreements replica_dn = ','.join( ['cn=replica', 'cn="o=testReplica"', DN_MAPPING_TREE]) replica_e = Entry(replica_dn) replica_e.update({ 'objectclass': ["top", "nsds5replica", "extensibleobject"], 'cn': "replica", 'nsds5replicaroot': 'o=testReplica', 'nsds5replicaid': MOCK_REPLICA_ID, 'nsds5replicatype': '3', 'nsds5flags': '1', 'nsds5replicabinddn': DN_RMANAGER }) try: conn.add_s(replica_e) except ldap.ALREADY_EXISTS: pass conn.added_entries.append(replica_dn) agreement_dn = ','.join(('cn=testAgreement', replica_dn)) agreement_e = Entry(agreement_dn) agreement_e.update({ 'objectclass': ["top", "nsds5replicationagreement"], 'cn': 'testAgreement', 'nsds5replicahost': 'localhost', 'nsds5replicaport': '22389', 'nsds5replicatimeout': '120', 'nsds5replicabinddn': DN_RMANAGER, 'nsds5replicacredentials': 'password', 'nsds5replicabindmethod': 'simple', 'nsds5replicaroot': 'o=testReplica', 'nsds5replicaupdateschedule': '0000-2359 0123456', 'description': 'testAgreement' }) try: conn.add_s(agreement_e) except ldap.ALREADY_EXISTS: pass conn.added_entries.append(agreement_dn) conn.agreement_dn = agreement_dn
def add(self, suffix, binddn=None, bindpw=None, urls=None, attrvals=None, benamebase='localdb', setupmt=False, parent=None): """Setup a backend and return its dn. Blank on error XXX should RAISE! @param suffix @param benamebase - the backend common name @param setupmt - eventually setup Mapping Tree entry @param urls - a string of ldapurl - create a chaining backend @oaram binddn - chaining proxy user @param bindpw - chaining proxy password @param attrvals: a dict with further params like for ldbm { 'nsslapd-cachememsize': '1073741824', 'nsslapd-cachesize': '-1', } for chain { 'nsmaxresponsedelay': '60', 'nsslapd-sizelimit': '-1' } ex. benamebase="Isle0-0" urls=[ "ldaps://f0-ldap-vip.example.it:636/", "ldaps://ldap-18.example.it:636/", "ldaps://ldap-19.example.it:636/" ] NOTE: The suffix attribute is a mere string for the backend. the following action will work nicely: c.backend.add(suffix="foo=example,dc=com",benamebase="db1") TODO: rename benamebase with cn TODO: split CHAIN and LDBM ? eg. backend.add_chain """ attrvals = attrvals or {} dnbase = "" # figure out what type of be based on args if binddn and bindpw and urls: # its a chaining be dnbase = DN_CHAIN else: # its a ldbm be dnbase = DN_LDBM nsuffix = normalizeDN(suffix) try: cn = benamebase self.log.debug("create backend with cn: %s" % cn) dn = "cn=" + cn + "," + dnbase entry = Entry(dn) entry.update({ 'objectclass': ['top', 'extensibleObject', 'nsBackendInstance'], 'cn': cn, 'nsslapd-suffix': nsuffix }) if binddn and bindpw and urls: # its a chaining be entry.update({ 'nsfarmserverurl': urls, 'nsmultiplexorbinddn': binddn, 'nsmultiplexorcredentials': bindpw }) # set attrvals (but not cn, because it's in dn) # TODO do it in Entry if attrvals: entry.update(attrvals) self.log.debug("adding entry: %r" % entry) self.conn.add_s(entry) except ldap.ALREADY_EXISTS, e: self.log.error("Entry already exists: %r" % dn) raise ldap.ALREADY_EXISTS("%s : %r" % (e, dn))
def agreement_add(self, consumer, suffix=None, binddn=None, bindpw=None, cn_format=r'meTo_$host:$port', description_format=r'me to $host:$port', timeout=120, auto_init=False, bindmethod='simple', starttls=False, schedule=ALWAYS, args=None): """Create (and return) a replication agreement from self to consumer. - self is the supplier, @param consumer: one of the following (consumer can be a master) * a DSAdmin object if chaining * an object with attributes: host, port, sslport, __str__ @param suffix - eg. 'dc=babel,dc=it' @param binddn - @param bindpw - @param cn_format - string.Template to format the agreement name @param timeout - replica timeout in seconds @param auto_init - start replication immediately @param bindmethod- 'simple' @param starttls - True or False @param schedule - when to schedule the replication. default: ALWAYS @param args - further args dict. Allowed keys: 'fractional', 'stripattrs', 'winsync' @raise NosuchEntryError - if a replica doesn't exist for that suffix @raise ALREADY_EXISTS @raise UNWILLING_TO_PERFORM if the database was previously in read-only state. To create new agreements you need to *restart* the directory server NOTE: this method doesn't cache connection entries TODO: test winsync TODO: test chain """ import string assert binddn and bindpw and suffix args = args or {} othhost, othport, othsslport = ( consumer.host, consumer.port, consumer.sslport) othport = othsslport or othport nsuffix = normalizeDN(suffix) # adding agreement to previously created replica replica_entries = self.list(suffix) if not replica_entries: raise NoSuchEntryError( "Error: no replica set up for suffix " + suffix) replica = replica_entries[0] # define agreement entry cn = string.Template(cn_format).substitute({'host': othhost, 'port': othport}) dn_agreement = ','.join(["cn=%s" % cn, replica.dn]) # This is probably unnecessary because # we can just raise ALREADY_EXISTS try: entry = self.conn.getEntry(dn_agreement, ldap.SCOPE_BASE) self.log.warn("Agreement exists: %r" % dn_agreement) raise ldap.ALREADY_EXISTS except ldap.NO_SUCH_OBJECT: entry = None # In a separate function in this scope? entry = Entry(dn_agreement) entry.update({ 'objectclass': ["top", "nsds5replicationagreement"], 'cn': cn, 'nsds5replicahost': consumer.host, 'nsds5replicatimeout': str(timeout), 'nsds5replicabinddn': binddn, 'nsds5replicacredentials': bindpw, 'nsds5replicabindmethod': bindmethod, 'nsds5replicaroot': nsuffix, 'description': string.Template(description_format).substitute({'host': othhost, 'port': othport}) }) if schedule: if not re.match(r'\d{4}-\d{4} [0-6]{1,7}', schedule): # TODO put the regexp in a separate variable raise ValueError("Bad schedule format %r" % schedule) entry.update({'nsds5replicaupdateschedule': schedule}) if starttls: entry.setValues('nsds5replicatransportinfo', 'TLS') entry.setValues('nsds5replicaport', str(othport)) elif othsslport: entry.setValues('nsds5replicatransportinfo', 'SSL') entry.setValues('nsds5replicaport', str(othsslport)) else: entry.setValues('nsds5replicatransportinfo', 'LDAP') entry.setValues('nsds5replicaport', str(othport)) if auto_init: entry.setValues('nsds5BeginReplicaRefresh', 'start') # further arguments if 'fractional' in args: entry.setValues('nsDS5ReplicatedAttributeList', args['fractional']) if 'stripattrs' in args: entry.setValues('nsds5ReplicaStripAttrs', args['stripattrs']) if 'winsync' in args: # state it clearly! self.conn.setupWinSyncAgmt(args, entry) try: self.log.debug("Adding replica agreement: [%s]" % entry) self.conn.add_s(entry) except: # FIXME check please! raise entry = self.conn.waitForEntry(dn_agreement) if entry: # More verbose but shows what's going on if 'chain' in args: chain_args = { 'suffix': suffix, 'binddn': binddn, 'bindpw': bindpw } # Work on `self` aka producer if replica.nsds5replicatype == MASTER_TYPE: self.setupChainingFarm(**chain_args) # Work on `consumer` # TODO - is it really required? if replica.nsds5replicatype == LEAF_TYPE: chain_args.update({ 'isIntermediate': 0, 'urls': self.conn.toLDAPURL(), 'args': args['chainargs'] }) consumer.setupConsumerChainOnUpdate(**chain_args) elif replica.nsds5replicatype == HUB_TYPE: chain_args.update({ 'isIntermediate': 1, 'urls': self.conn.toLDAPURL(), 'args': args['chainargs'] }) consumer.setupConsumerChainOnUpdate(**chain_args) return dn_agreement
def add(self, suffix, binddn, bindpw, rtype=MASTER_TYPE, rid=None, tombstone_purgedelay=None, purgedelay=None, referrals=None, legacy=False): """Setup a replica entry on an existing suffix. @param suffix - dn of suffix @param binddn - the replication bind dn for this replica can also be a list ["cn=r1,cn=config","cn=r2,cn=config"] @param bindpw - used to eventually provision the replication entry @param rtype - master, hub, leaf (see above for values) - default is master @param rid - replica id or - if not given - an internal sequence number will be assigned # further args @param legacy - true or false - for legacy consumer @param tombstone_purgedelay @param purgedelay - changelog expiration time in seconds @param referrals Ex. replica.add(**{ 'suffix': "dc=example,dc=com", 'type' : dsadmin.MASTER_TYPE, 'binddn': "cn=replication manager,cn=config" }) binddn TODO: this method does not update replica type """ # set default values if rtype == MASTER_TYPE: rtype = REPLICA_RDWR_TYPE else: rtype = REPLICA_RDONLY_TYPE if legacy: legacy = 'on' else: legacy = 'off' # create replica entry in mapping-tree nsuffix = normalizeDN(suffix) mtent = self.conn.getMTEntry(suffix) dn_replica = ','.join(("cn=replica", mtent.dn)) try: entry = self.conn.getEntry(dn_replica, ldap.SCOPE_BASE) self.log.warn("Already setup replica for suffix %r" % suffix) rec = self.conn.suffixes.setdefault(nsuffix, {}) rec.update({'dn': dn_replica, 'type': rtype}) return rec except ldap.NO_SUCH_OBJECT: entry = None # If a replica does not exist binddnlist = [] if hasattr(binddn, '__iter__'): binddnlist = binddn else: binddnlist.append(binddn) entry = Entry(dn_replica) entry.update({ 'objectclass': ("top", "nsds5replica", "extensibleobject"), 'cn': "replica", 'nsds5replicaroot': nsuffix, 'nsds5replicaid': str(rid), 'nsds5replicatype': str(rtype), 'nsds5replicalegacyconsumer': legacy, 'nsds5replicabinddn': binddnlist }) if rtype != LEAF_TYPE: entry.setValues('nsds5flags', "1") # other args if tombstone_purgedelay is not None: entry.setValues( 'nsds5replicatombstonepurgeinterval', str(tombstone_purgedelay)) if purgedelay is not None: entry.setValues('nsds5ReplicaPurgeDelay', str(purgedelay)) if referrals: entry.setValues('nsds5ReplicaReferral', referrals) self.conn.add_s(entry) # check if the entry exists TODO better to raise! self.conn._test_entry(dn_replica, ldap.SCOPE_BASE) self.conn.suffixes[nsuffix] = {'dn': dn_replica, 'type': rtype} return {'dn': dn_replica, 'type': rtype}
def setup(): # uses an existing 389 instance # add a suffix # add an agreement # This setup is quite verbose but to test dsadmin method we should # do things manually. A better solution would be to use an LDIF. global conn conn = DSAdmin(**config.auth) conn.verbose = True conn.added_entries = [] conn.added_backends = set(['o=mockbe1']) conn.added_replicas = [] # add a backend for testing ruv and agreements addbackend_harn(conn, 'testReplica') # add another backend for testing replica.add() addbackend_harn(conn, 'testReplicaCreation') # replication needs changelog conn.replica.changelog() # add rmanager entry try: conn.add_s( Entry((DN_RMANAGER, { 'objectclass': "top person inetOrgPerson".split(), 'sn': ["bind dn pseudo user"], 'cn': 'replication manager', 'uid': 'rmanager' }))) conn.added_entries.append(DN_RMANAGER) except ldap.ALREADY_EXISTS: pass # add a master replica entry # to test ruv and agreements replica_dn = ','.join( ['cn=replica', 'cn="o=testReplica"', DN_MAPPING_TREE]) replica_e = Entry(replica_dn) replica_e.update({ 'objectclass': ["top", "nsds5replica", "extensibleobject"], 'cn': "replica", 'nsds5replicaroot': 'o=testReplica', 'nsds5replicaid': MOCK_REPLICA_ID, 'nsds5replicatype': '3', 'nsds5flags': '1', 'nsds5replicabinddn': DN_RMANAGER }) try: conn.add_s(replica_e) except ldap.ALREADY_EXISTS: pass conn.added_entries.append(replica_dn) agreement_dn = ','.join(('cn=testAgreement', replica_dn)) agreement_e = Entry(agreement_dn) agreement_e.update({ 'objectclass': ["top", "nsds5replicationagreement"], 'cn': 'testAgreement', 'nsds5replicahost': 'localhost', 'nsds5replicaport': '22389', 'nsds5replicatimeout': '120', 'nsds5replicabinddn': DN_RMANAGER, 'nsds5replicacredentials': 'password', 'nsds5replicabindmethod': 'simple', 'nsds5replicaroot': 'o=testReplica', 'nsds5replicaupdateschedule': '0000-2359 0123456', 'description': 'testAgreement' }) try: conn.add_s(agreement_e) except ldap.ALREADY_EXISTS: pass conn.added_entries.append(agreement_dn) conn.agreement_dn = agreement_dn
def agreement_add(self, consumer, suffix=None, binddn=None, bindpw=None, cn_format=r'meTo_$host:$port', description_format=r'me to $host:$port', timeout=120, auto_init=False, bindmethod='simple', starttls=False, schedule=ALWAYS, args=None): """Create (and return) a replication agreement from self to consumer. - self is the supplier, @param consumer: one of the following (consumer can be a master) * a DSAdmin object if chaining * an object with attributes: host, port, sslport, __str__ @param suffix - eg. 'dc=babel,dc=it' @param binddn - @param bindpw - @param cn_format - string.Template to format the agreement name @param timeout - replica timeout in seconds @param auto_init - start replication immediately @param bindmethod- 'simple' @param starttls - True or False @param schedule - when to schedule the replication. default: ALWAYS @param args - further args dict. Allowed keys: 'fractional', 'stripattrs', 'winsync' @raise NosuchEntryError - if a replica doesn't exist for that suffix @raise ALREADY_EXISTS @raise UNWILLING_TO_PERFORM if the database was previously in read-only state. To create new agreements you need to *restart* the directory server NOTE: this method doesn't cache connection entries TODO: test winsync TODO: test chain """ import string assert binddn and bindpw and suffix args = args or {} othhost, othport, othsslport = ( consumer.host, consumer.port, consumer.sslport) othport = othsslport or othport nsuffix = normalizeDN(suffix) # adding agreement to previously created replica replica_entries = self.list(suffix) if not replica_entries: raise NoSuchEntryError( "Error: no replica set up for suffix " + suffix) replica = replica_entries[0] # define agreement entry cn = string.Template(cn_format).substitute({'host': othhost, 'port': othport}) dn_agreement = ','.join(["cn=%s" % cn, replica.dn]) # This is probably unnecessary because # we can just raise ALREADY_EXISTS try: entry = self.conn.getEntry(dn_agreement, ldap.SCOPE_BASE) self.log.warn("Agreement exists: %r" % dn_agreement) raise ldap.ALREADY_EXISTS except ldap.NO_SUCH_OBJECT: entry = None # In a separate function in this scope? entry = Entry(dn_agreement) entry.update({ 'objectclass': ["top", "nsds5replicationagreement"], 'cn': cn, 'nsds5replicahost': consumer.host, 'nsds5replicatimeout': str(timeout), 'nsds5replicabinddn': binddn, 'nsds5replicacredentials': bindpw, 'nsds5replicabindmethod': bindmethod, 'nsds5replicaroot': nsuffix, 'description': string.Template(description_format).substitute({'host': othhost, 'port': othport}) }) if schedule: if not re.match(r'\d{4}-\d{4} [0-6]{1,7}', schedule): # TODO put the regexp in a separate variable raise ValueError("Bad schedule format %r" % schedule) entry.update({'nsds5replicaupdateschedule': schedule}) if starttls: entry.setValues('nsds5replicatransportinfo', 'TLS') entry.setValues('nsds5replicaport', str(othport)) elif othsslport: entry.setValues('nsds5replicatransportinfo', 'SSL') entry.setValues('nsds5replicaport', str(othsslport)) else: entry.setValues('nsds5replicatransportinfo', 'LDAP') entry.setValues('nsds5replicaport', str(othport)) if auto_init: entry.setValues('nsds5BeginReplicaRefresh', 'start') # further arguments if 'fractional' in args: entry.setValues('nsDS5ReplicatedAttributeList', args['fractional']) # use the specified fractional total - if not specified, use the # specified fractional - if not specified, skip frac_total = args.get('fractional_total', args.get('fractional', None)) if frac_total: entry.setValues('nsDS5ReplicatedAttributeListTotal', frac_total) if 'stripattrs' in args: entry.setValues('nsds5ReplicaStripAttrs', args['stripattrs']) if 'winsync' in args: # state it clearly! self.conn.setupWinSyncAgmt(args, entry) try: self.log.debug("Adding replica agreement: [%s]" % entry) self.conn.add_s(entry) except: # FIXME check please! raise entry = self.conn.waitForEntry(dn_agreement) if entry: # More verbose but shows what's going on if 'chain' in args: chain_args = { 'suffix': suffix, 'binddn': binddn, 'bindpw': bindpw } # Work on `self` aka producer if replica.nsds5replicatype == MASTER_TYPE: self.setupChainingFarm(**chain_args) # Work on `consumer` # TODO - is it really required? if replica.nsds5replicatype == LEAF_TYPE: chain_args.update({ 'isIntermediate': 0, 'urls': self.conn.toLDAPURL(), 'args': args['chainargs'] }) consumer.setupConsumerChainOnUpdate(**chain_args) elif replica.nsds5replicatype == HUB_TYPE: chain_args.update({ 'isIntermediate': 1, 'urls': self.conn.toLDAPURL(), 'args': args['chainargs'] }) consumer.setupConsumerChainOnUpdate(**chain_args) return dn_agreement