Skip to content

Commit

Permalink
Replication suffixes and subsuffixes
Browse files Browse the repository at this point in the history
  • Loading branch information
droideck committed Jan 13, 2025
1 parent f5732e1 commit 5dc055b
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 5 deletions.
126 changes: 126 additions & 0 deletions dirsrvtests/tests/suites/replication/acceptance_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,132 @@ def test_default_cl_trimming_enabled(topo_m2):
assert cl.get_attr_val_utf8("nsslapd-changelogmaxage") == "7d"


def test_multi_subsuffix_replication(topo_m4):
"""Check that replication works with multiple subsuffixes
:id: ac1aaeae-173e-48e7-847f-03b9867443c4
:setup: Four suppliers replication setup
:steps:
1. Create additional suffixes
2. Setup replication for all suppliers
3. Generate test data for each suffix (add, modify, remove)
4. Wait for replication to complete across all suppliers for each suffix
5. Check that all expected data is present on all suppliers
:expectedresults:
1. Success
2. Success
3. Success
4. Success
5. Success (the data is replicated everywhere)
"""
# Define additional suffixes
SUFFIX_2 = "dc=test2"
SUFFIX_3 = f"dc=test3,{DEFAULT_SUFFIX}"
all_suffixes = [DEFAULT_SUFFIX, SUFFIX_2, SUFFIX_3]

# Track test users for cleanup or later checks
test_users_by_suffix = {suffix: [] for suffix in all_suffixes}

# Get all suppliers for easier reference
suppliers = [
topo_m4.ms["supplier1"],
topo_m4.ms["supplier2"],
topo_m4.ms["supplier3"],
topo_m4.ms["supplier4"]
]

# Setup additional backends and replication for all suppliers
for suffix in [SUFFIX_2, SUFFIX_3]: # DEFAULT_SUFFIX already exists
repl = ReplicationManager(suffix)
for supplier in suppliers:
# Create a new backend for this suffix
props = {
'cn': f'userRoot_{suffix.split(",")[0][3:]}',
'nsslapd-suffix': suffix
}
be = Backend(supplier)
be.create(properties=props)
be.create_sample_entries('001004002')

# Enable replication
if supplier == suppliers[0]: # supplier1 is the "first" supplier
repl.create_first_supplier(supplier)
else:
repl.join_supplier(suppliers[0], supplier)

# Create full mesh topology for this suffix
for i, supplier_i in enumerate(suppliers):
for j, supplier_j in enumerate(suppliers):
if i != j: # Don't create an agreement with itself
repl.ensure_agreement(supplier_i, supplier_j)

# Generate test data for each suffix (add a set of users, then modify them)
for suffix in all_suffixes:
# Create some user entries in supplier1
for i in range(20):
user_dn = f'uid=test_user_{i},{suffix}'
test_user = UserAccount(suppliers[0], user_dn)
test_user.create(properties={
'uid': f'test_user_{i}',
'cn': f'Test User {i}',
'sn': f'User{i}',
'userPassword': 'password',
'uidNumber': str(1000 + i),
'gidNumber': '2000',
'homeDirectory': f'/home/test_user_{i}'
})
test_users_by_suffix[suffix].append(test_user)

# Perform modifications on these entries
for user in test_users_by_suffix[suffix]:
# Add some attributes
for j in range(3):
user.add('description', f'Description {j}')
# Replace an attribute
user.replace('cn', f'Modified User {user.get_attr_val("uid")}')
# Delete the attributes we added
for j in range(3):
try:
user.remove('description', f'Description {j}')
except Exception:
pass

# Wait for replication to complete across all suppliers, for each suffix
for suffix in all_suffixes:
repl = ReplicationManager(suffix)
for i, supplier_i in enumerate(suppliers):
for j, supplier_j in enumerate(suppliers):
if i != j:
repl.wait_for_replication(supplier_i, supplier_j)

# Verify that each user and modification replicated to all suppliers
for suffix in all_suffixes:
for i in range(20):
user_dn = f'uid=test_user_{i},{suffix}'
# Retrieve this user from all suppliers
all_user_objs = topo_m4.all_get_dsldapobject(user_dn, UserAccount)
# Ensure it exists in all 4 suppliers
assert len(all_user_objs) == 4, (
f"User {user_dn} not found on all suppliers. "
f"Found only on {len(all_user_objs)} suppliers."
)
# Check modifications: 'cn' should now be 'Modified User test_user_{i}'
for user_obj in all_user_objs:
expected_cn = f"Modified User test_user_{i}"
actual_cn = user_obj.get_attr_val_utf8("cn")
assert actual_cn == expected_cn, (
f"User {user_dn} has an unexpected 'cn' value: {actual_cn} "
f"(expected '{expected_cn}') on supplier {user_obj._instance.serverid}"
)
# And check that 'description' attributes were removed
desc_vals = user_obj.get_attr_vals_utf8('description')
for j in range(3):
assert f"Description {j}" not in desc_vals, (
f"User {user_dn} on supplier {user_obj._instance.serverid} "
f"still has 'Description {j}'"
)


if __name__ == '__main__':
# Run isolated
# -s for DEBUG mode
Expand Down
10 changes: 5 additions & 5 deletions src/lib389/lib389/replica.py
Original file line number Diff line number Diff line change
Expand Up @@ -2013,7 +2013,7 @@ def _create_service_group(self, from_instance):
return repl_group
else:
try:
repl_group = groups.get('replication_managers')
repl_group = groups.get(dn=f'cn=replication_managers,{self._suffix}')
return repl_group
except ldap.NO_SUCH_OBJECT:
self._log.warning("{} doesn't have cn=replication_managers,{} entry \
Expand All @@ -2037,7 +2037,7 @@ def _create_service_account(self, from_instance, to_instance):
services = ServiceAccounts(from_instance, self._suffix)
# Generate the password and save the credentials
# for putting them into agreements in the future
service_name = '{}:{}'.format(to_instance.host, port)
service_name = f'{self._suffix}:{to_instance.host}:{port}'
creds = password_generate()
repl_service = services.ensure_state(properties={
'cn': service_name,
Expand Down Expand Up @@ -2296,7 +2296,7 @@ def _get_replica_creds(self, from_instance, write_instance):
Internal Only.
"""

rdn = '{}:{}'.format(from_instance.host, from_instance.sslport)
rdn = f'{self._suffix}:{from_instance.host}:{from_instance.sslport}'
try:
creds = self._repl_creds[rdn]
except KeyError:
Expand Down Expand Up @@ -2537,8 +2537,8 @@ def wait_for_replication(self, from_instance, to_instance, timeout=60):
# Touch something then wait_for_replication.
from_groups = Groups(from_instance, basedn=self._suffix, rdn=None)
to_groups = Groups(to_instance, basedn=self._suffix, rdn=None)
from_group = from_groups.get('replication_managers')
to_group = to_groups.get('replication_managers')
from_group = from_groups.get(dn=f'cn=replication_managers,{self._suffix}')
to_group = to_groups.get(dn=f'cn=replication_managers,{self._suffix}')

change = str(uuid.uuid4())

Expand Down

0 comments on commit 5dc055b

Please sign in to comment.