An old way to a new way - lib389 cheatsheet

Historically, lib389 had two object-oriented approaches on top of python-ldap. They help to get rid of a boilerplate code that we are end up with while working with different parts of 389-ds-base server. While using the original API we found out what we are missing and what can be improved, so we has started developing a new approach.

A cornerstone object of the new API is DSLdapObjects. It can be found in lib389/_mapped_object.py module. It represents the idea of a group of entries united by one purpose (i.e. users, groups, agreements, etc.) The entries are united by common ObjectClasses, must attributes and the rdn attribute. Also, it has a child - DSLdapObject, it represents a single ldap entry.

Examples

Users

The same way works for groups (but you have ou=groups instead of ou=people)

A python-ldap direct way:

dn = 'uid=test_user_1,ou=people,%s' % DEFAULT_SUFFIX
rdn = b'test_user_1'

attrs = {'objectclass': [b'top', b'person', b'organizationalPerson', b'inetOrgPerson'],
         'uid': [rdn],
         'sn' : [rdn],
         'cn' : [rdn],
         'description': b'a new entry'}
ldif = modlist.addModlist(attrs)

# Add a user
server.add_s(dn, ldif)

# Modify the user
server.modify_s(dn, [(ldap.MOD_DELETE, 'description', b'a new entry'),
                     (ldap.MOD_ADD, 'description', b'a modified entry')])

A new DSLdapObjects way:

from lib389.idm.user import UserAccounts

# Define all users under 'ou=people,%s' % DEFAULT_SUFFIX
users = UserAccounts(server, DEFAULT_SUFFIX)

# Add a simple user for the case when we don't care about exact attributes
user = users.create_test_user(uid=1000, gid=2000) # both attributes have default values so specifying them is optional

# Add a user with the exact attributes
# (objectclasses are taken from DSLdapObject definition but you can add some more)
user = users.create(properties={'uid': 'test_user_1',
                                'cn': 'test_user_1',
                                'sn': 'test_user_1',
                                'uidNumber': 1000,
                                'gidNumber': 2000,
                                'homeDirectory': '/home/test_user_1',
                                'description': 'a new entry'})

# Then we can work with the user entry
user.remove('description', 'a new entry')
user.add('description', 'a new entry')
description_attr_value = user.get_attr_val_utf8('description')

# You can search the users
users.list() # a list of DSLdapObject
# or get the user
user = users.get('test_user_1')

Agreements and pausing replication

An old lib389 object which is outdated:

M1 = topology_m4.ms['master1']
M2 = topology_m4.ms['master2']
M3 = topology_m4.ms['master3']

agreement_m1_m2 = M1.agreement.list(suffix=DEFAULT_SUFFIX, consumer_host=M2.host, consumer_port=M2.port)
agreement_m1_m3 = M1.agreement.list(suffix=DEFAULT_SUFFIX, consumer_host=M3.host, consumer_port=M3.port)
agreement_m2_m1 = M2.agreement.list(suffix=DEFAULT_SUFFIX, consumer_host=M1.host, consumer_port=M1.port)
agreement_m2_m3 = M2.agreement.list(suffix=DEFAULT_SUFFIX, consumer_host=M3.host, consumer_port=M3.port)

M1.agreement.pause(agreement_m1_m2[0].dn)
M1.agreement.pause(agreement_m1_m3[0].dn)
M2.agreement.pause(agreement_m2_m1[0].dn)
M2.agreement.pause(agreement_m2_m3[0].dn)

DSLdapObjects Agreements which is currently fully supported and all the fixes go there:

from lib389.agreement import Agreements

agmts = Agreements(M1)
agmt = agmts.list()[0]
agmt.replace('nsDS5ReplicaCredentials', 'Secret123')

But if you want to pause/resume replication, it is easier to use other method:

from lib389.replica import ReplicationManager

repl = ReplicationManager(DEFAULT_SUFFIX)

# Pause/resume one replica direction
repl.disable_to_master(to_instance=M3, from_instances=[M1, M2])
repl.enable_to_master(to_instance=M3, from_instances=[M1, M2])

# Pause/resume all replication in the topology
topology_m4.pause_all_replicas()
topology_m4.resume_all_replicas()