|
|
5d2be4 |
From 9d25d8bc3262bfaeeda2992538f649bf1a1b33de Mon Sep 17 00:00:00 2001
|
|
|
5d2be4 |
From: progier389 <72748589+progier389@users.noreply.github.com>
|
|
|
5d2be4 |
Date: Thu, 12 Nov 2020 18:50:04 +0100
|
|
|
5d2be4 |
Subject: [PATCH 6/8] do not add referrals for masters with different data
|
|
|
5d2be4 |
generation #2054 (#4427)
|
|
|
5d2be4 |
|
|
|
5d2be4 |
Bug description:
|
|
|
5d2be4 |
The problem is that some operation mandatory in the usual cases are
|
|
|
5d2be4 |
also performed when replication cannot take place because the
|
|
|
5d2be4 |
database set are differents (i.e: RUV generation ids are different)
|
|
|
5d2be4 |
|
|
|
5d2be4 |
One of the issue is that the csn generator state is updated when
|
|
|
5d2be4 |
starting a replication session (it is a problem when trying to
|
|
|
5d2be4 |
reset the time skew, as freshly reinstalled replicas get infected
|
|
|
5d2be4 |
by the old ones)
|
|
|
5d2be4 |
|
|
|
5d2be4 |
A second issue is that the RUV got updated when ending a replication session
|
|
|
5d2be4 |
(which may add replica that does not share the same data set,
|
|
|
5d2be4 |
then update operations on consumer retun referrals towards wrong masters
|
|
|
5d2be4 |
|
|
|
5d2be4 |
Fix description:
|
|
|
5d2be4 |
The fix checks the RUVs generation id before updating the csn generator
|
|
|
5d2be4 |
and before updating the RUV.
|
|
|
5d2be4 |
|
|
|
5d2be4 |
Reviewed by: mreynolds
|
|
|
5d2be4 |
firstyear
|
|
|
5d2be4 |
vashirov
|
|
|
5d2be4 |
|
|
|
5d2be4 |
Platforms tested: F32
|
|
|
5d2be4 |
---
|
|
|
5d2be4 |
.../suites/replication/regression_test.py | 290 ++++++++++++++++++
|
|
|
5d2be4 |
ldap/servers/plugins/replication/repl5.h | 1 +
|
|
|
5d2be4 |
.../plugins/replication/repl5_inc_protocol.c | 20 +-
|
|
|
5d2be4 |
.../plugins/replication/repl5_replica.c | 39 ++-
|
|
|
5d2be4 |
src/lib389/lib389/dseldif.py | 37 +++
|
|
|
5d2be4 |
5 files changed, 368 insertions(+), 19 deletions(-)
|
|
|
5d2be4 |
|
|
|
5d2be4 |
diff --git a/dirsrvtests/tests/suites/replication/regression_test.py b/dirsrvtests/tests/suites/replication/regression_test.py
|
|
|
5d2be4 |
index 14b9d6a44..a72af6b30 100644
|
|
|
5d2be4 |
--- a/dirsrvtests/tests/suites/replication/regression_test.py
|
|
|
5d2be4 |
+++ b/dirsrvtests/tests/suites/replication/regression_test.py
|
|
|
5d2be4 |
@@ -13,6 +13,7 @@ from lib389.idm.user import TEST_USER_PROPERTIES, UserAccounts
|
|
|
5d2be4 |
from lib389.pwpolicy import PwPolicyManager
|
|
|
5d2be4 |
from lib389.utils import *
|
|
|
5d2be4 |
from lib389.topologies import topology_m2 as topo_m2, TopologyMain, topology_m3 as topo_m3, create_topology, _remove_ssca_db, topology_i2 as topo_i2
|
|
|
5d2be4 |
+from lib389.topologies import topology_m2c2 as topo_m2c2
|
|
|
5d2be4 |
from lib389._constants import *
|
|
|
5d2be4 |
from lib389.idm.organizationalunit import OrganizationalUnits
|
|
|
5d2be4 |
from lib389.idm.user import UserAccount
|
|
|
5d2be4 |
@@ -22,6 +23,7 @@ from lib389.idm.directorymanager import DirectoryManager
|
|
|
5d2be4 |
from lib389.replica import Replicas, ReplicationManager, Changelog5, BootstrapReplicationManager
|
|
|
5d2be4 |
from lib389.agreement import Agreements
|
|
|
5d2be4 |
from lib389 import pid_from_file
|
|
|
5d2be4 |
+from lib389.dseldif import *
|
|
|
5d2be4 |
|
|
|
5d2be4 |
|
|
|
5d2be4 |
pytestmark = pytest.mark.tier1
|
|
|
5d2be4 |
@@ -1027,6 +1029,294 @@ def test_online_init_should_create_keepalive_entries(topo_m2):
|
|
|
5d2be4 |
verify_keepalive_entries(topo_m2, True);
|
|
|
5d2be4 |
|
|
|
5d2be4 |
|
|
|
5d2be4 |
+def get_agreement(agmts, consumer):
|
|
|
5d2be4 |
+ # Get agreement towards consumer among the agremment list
|
|
|
5d2be4 |
+ for agmt in agmts.list():
|
|
|
5d2be4 |
+ if (agmt.get_attr_val_utf8('nsDS5ReplicaPort') == str(consumer.port) and
|
|
|
5d2be4 |
+ agmt.get_attr_val_utf8('nsDS5ReplicaHost') == consumer.host):
|
|
|
5d2be4 |
+ return agmt
|
|
|
5d2be4 |
+ return None;
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+def test_ruv_url_not_added_if_different_uuid(topo_m2c2):
|
|
|
5d2be4 |
+ """Check that RUV url is not updated if RUV generation uuid are different
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ :id: 7cc30a4e-0ffd-4758-8f00-e500279af344
|
|
|
5d2be4 |
+ :setup: Two masters + two consumers replication setup
|
|
|
5d2be4 |
+ :steps:
|
|
|
5d2be4 |
+ 1. Generate ldif without replication data
|
|
|
5d2be4 |
+ 2. Init both masters from that ldif
|
|
|
5d2be4 |
+ (to clear the ruvs and generates different generation uuid)
|
|
|
5d2be4 |
+ 3. Perform on line init from master1 to consumer1
|
|
|
5d2be4 |
+ and from master2 to consumer2
|
|
|
5d2be4 |
+ 4. Perform update on both masters
|
|
|
5d2be4 |
+ 5. Check that c1 RUV does not contains URL towards m2
|
|
|
5d2be4 |
+ 6. Check that c2 RUV does contains URL towards m2
|
|
|
5d2be4 |
+ 7. Perform on line init from master1 to master2
|
|
|
5d2be4 |
+ 8. Perform update on master2
|
|
|
5d2be4 |
+ 9. Check that c1 RUV does contains URL towards m2
|
|
|
5d2be4 |
+ :expectedresults:
|
|
|
5d2be4 |
+ 1. No error while generating ldif
|
|
|
5d2be4 |
+ 2. No error while importing the ldif file
|
|
|
5d2be4 |
+ 3. No error and Initialization done.
|
|
|
5d2be4 |
+ 4. No error
|
|
|
5d2be4 |
+ 5. master2 replicaid should not be in the consumer1 RUV
|
|
|
5d2be4 |
+ 6. master2 replicaid should be in the consumer2 RUV
|
|
|
5d2be4 |
+ 7. No error and Initialization done.
|
|
|
5d2be4 |
+ 8. No error
|
|
|
5d2be4 |
+ 9. master2 replicaid should be in the consumer1 RUV
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ """
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Variables initialization
|
|
|
5d2be4 |
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ m1 = topo_m2c2.ms["master1"]
|
|
|
5d2be4 |
+ m2 = topo_m2c2.ms["master2"]
|
|
|
5d2be4 |
+ c1 = topo_m2c2.cs["consumer1"]
|
|
|
5d2be4 |
+ c2 = topo_m2c2.cs["consumer2"]
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ replica_m1 = Replicas(m1).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_m2 = Replicas(m2).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_c1 = Replicas(c1).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_c2 = Replicas(c2).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ replicid_m2 = replica_m2.get_rid()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ agmts_m1 = Agreements(m1, replica_m1.dn)
|
|
|
5d2be4 |
+ agmts_m2 = Agreements(m2, replica_m2.dn)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ m1_m2 = get_agreement(agmts_m1, m2)
|
|
|
5d2be4 |
+ m1_c1 = get_agreement(agmts_m1, c1)
|
|
|
5d2be4 |
+ m1_c2 = get_agreement(agmts_m1, c2)
|
|
|
5d2be4 |
+ m2_m1 = get_agreement(agmts_m2, m1)
|
|
|
5d2be4 |
+ m2_c1 = get_agreement(agmts_m2, c1)
|
|
|
5d2be4 |
+ m2_c2 = get_agreement(agmts_m2, c2)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 1: Generate ldif without replication data
|
|
|
5d2be4 |
+ m1.stop()
|
|
|
5d2be4 |
+ m2.stop()
|
|
|
5d2be4 |
+ ldif_file = '%s/norepl.ldif' % m1.get_ldif_dir()
|
|
|
5d2be4 |
+ m1.db2ldif(bename=DEFAULT_BENAME, suffixes=[DEFAULT_SUFFIX],
|
|
|
5d2be4 |
+ excludeSuffixes=None, repl_data=False,
|
|
|
5d2be4 |
+ outputfile=ldif_file, encrypt=False)
|
|
|
5d2be4 |
+ # Remove replication metadata that are still in the ldif
|
|
|
5d2be4 |
+ # _remove_replication_data(ldif_file)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 2: Init both masters from that ldif
|
|
|
5d2be4 |
+ m1.ldif2db(DEFAULT_BENAME, None, None, None, ldif_file)
|
|
|
5d2be4 |
+ m2.ldif2db(DEFAULT_BENAME, None, None, None, ldif_file)
|
|
|
5d2be4 |
+ m1.start()
|
|
|
5d2be4 |
+ m2.start()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 3: Perform on line init from master1 to consumer1
|
|
|
5d2be4 |
+ # and from master2 to consumer2
|
|
|
5d2be4 |
+ m1_c1.begin_reinit()
|
|
|
5d2be4 |
+ m2_c2.begin_reinit()
|
|
|
5d2be4 |
+ (done, error) = m1_c1.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+ (done, error) = m2_c2.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 4: Perform update on both masters
|
|
|
5d2be4 |
+ repl.test_replication(m1, c1)
|
|
|
5d2be4 |
+ repl.test_replication(m2, c2)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 5: Check that c1 RUV does not contains URL towards m2
|
|
|
5d2be4 |
+ ruv = replica_c1.get_ruv()
|
|
|
5d2be4 |
+ log.debug(f"c1 RUV: {ruv}")
|
|
|
5d2be4 |
+ url=ruv._rid_url.get(replica_m2.get_rid())
|
|
|
5d2be4 |
+ if (url == None):
|
|
|
5d2be4 |
+ log.debug(f"No URL for RID {replica_m2.get_rid()} in RUV");
|
|
|
5d2be4 |
+ else:
|
|
|
5d2be4 |
+ log.debug(f"URL for RID {replica_m2.get_rid()} in RUV is {url}");
|
|
|
5d2be4 |
+ log.error(f"URL for RID {replica_m2.get_rid()} found in RUV")
|
|
|
5d2be4 |
+ #Note: this assertion fails if issue 2054 is not fixed.
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 6: Check that c2 RUV does contains URL towards m2
|
|
|
5d2be4 |
+ ruv = replica_c2.get_ruv()
|
|
|
5d2be4 |
+ log.debug(f"c1 RUV: {ruv} {ruv._rids} ")
|
|
|
5d2be4 |
+ url=ruv._rid_url.get(replica_m2.get_rid())
|
|
|
5d2be4 |
+ if (url == None):
|
|
|
5d2be4 |
+ log.error(f"No URL for RID {replica_m2.get_rid()} in RUV");
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+ else:
|
|
|
5d2be4 |
+ log.debug(f"URL for RID {replica_m2.get_rid()} in RUV is {url}");
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 7: Perform on line init from master1 to master2
|
|
|
5d2be4 |
+ m1_m2.begin_reinit()
|
|
|
5d2be4 |
+ (done, error) = m1_m2.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 8: Perform update on master2
|
|
|
5d2be4 |
+ repl.test_replication(m2, c1)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 9: Check that c1 RUV does contains URL towards m2
|
|
|
5d2be4 |
+ ruv = replica_c1.get_ruv()
|
|
|
5d2be4 |
+ log.debug(f"c1 RUV: {ruv} {ruv._rids} ")
|
|
|
5d2be4 |
+ url=ruv._rid_url.get(replica_m2.get_rid())
|
|
|
5d2be4 |
+ if (url == None):
|
|
|
5d2be4 |
+ log.error(f"No URL for RID {replica_m2.get_rid()} in RUV");
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+ else:
|
|
|
5d2be4 |
+ log.debug(f"URL for RID {replica_m2.get_rid()} in RUV is {url}");
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+def test_csngen_state_not_updated_if_different_uuid(topo_m2c2):
|
|
|
5d2be4 |
+ """Check that csngen remote offset is not updated if RUV generation uuid are different
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ :id: 77694b8e-22ae-11eb-89b2-482ae39447e5
|
|
|
5d2be4 |
+ :setup: Two masters + two consumers replication setup
|
|
|
5d2be4 |
+ :steps:
|
|
|
5d2be4 |
+ 1. Disable m1<->m2 agreement to avoid propagate timeSkew
|
|
|
5d2be4 |
+ 2. Generate ldif without replication data
|
|
|
5d2be4 |
+ 3. Increase time skew on master2
|
|
|
5d2be4 |
+ 4. Init both masters from that ldif
|
|
|
5d2be4 |
+ (to clear the ruvs and generates different generation uuid)
|
|
|
5d2be4 |
+ 5. Perform on line init from master1 to consumer1 and master2 to consumer2
|
|
|
5d2be4 |
+ 6. Perform update on both masters
|
|
|
5d2be4 |
+ 7: Check that c1 has no time skew
|
|
|
5d2be4 |
+ 8: Check that c2 has time skew
|
|
|
5d2be4 |
+ 9. Init master2 from master1
|
|
|
5d2be4 |
+ 10. Perform update on master2
|
|
|
5d2be4 |
+ 11. Check that c1 has time skew
|
|
|
5d2be4 |
+ :expectedresults:
|
|
|
5d2be4 |
+ 1. No error
|
|
|
5d2be4 |
+ 2. No error while generating ldif
|
|
|
5d2be4 |
+ 3. No error
|
|
|
5d2be4 |
+ 4. No error while importing the ldif file
|
|
|
5d2be4 |
+ 5. No error and Initialization done.
|
|
|
5d2be4 |
+ 6. No error
|
|
|
5d2be4 |
+ 7. c1 time skew should be lesser than threshold
|
|
|
5d2be4 |
+ 8. c2 time skew should be higher than threshold
|
|
|
5d2be4 |
+ 9. No error and Initialization done.
|
|
|
5d2be4 |
+ 10. No error
|
|
|
5d2be4 |
+ 11. c1 time skew should be higher than threshold
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ """
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Variables initialization
|
|
|
5d2be4 |
+ repl = ReplicationManager(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ m1 = topo_m2c2.ms["master1"]
|
|
|
5d2be4 |
+ m2 = topo_m2c2.ms["master2"]
|
|
|
5d2be4 |
+ c1 = topo_m2c2.cs["consumer1"]
|
|
|
5d2be4 |
+ c2 = topo_m2c2.cs["consumer2"]
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ replica_m1 = Replicas(m1).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_m2 = Replicas(m2).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_c1 = Replicas(c1).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+ replica_c2 = Replicas(c2).get(DEFAULT_SUFFIX)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ replicid_m2 = replica_m2.get_rid()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ agmts_m1 = Agreements(m1, replica_m1.dn)
|
|
|
5d2be4 |
+ agmts_m2 = Agreements(m2, replica_m2.dn)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ m1_m2 = get_agreement(agmts_m1, m2)
|
|
|
5d2be4 |
+ m1_c1 = get_agreement(agmts_m1, c1)
|
|
|
5d2be4 |
+ m1_c2 = get_agreement(agmts_m1, c2)
|
|
|
5d2be4 |
+ m2_m1 = get_agreement(agmts_m2, m1)
|
|
|
5d2be4 |
+ m2_c1 = get_agreement(agmts_m2, c1)
|
|
|
5d2be4 |
+ m2_c2 = get_agreement(agmts_m2, c2)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 1: Disable m1<->m2 agreement to avoid propagate timeSkew
|
|
|
5d2be4 |
+ m1_m2.pause()
|
|
|
5d2be4 |
+ m2_m1.pause()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 2: Generate ldif without replication data
|
|
|
5d2be4 |
+ m1.stop()
|
|
|
5d2be4 |
+ m2.stop()
|
|
|
5d2be4 |
+ ldif_file = '%s/norepl.ldif' % m1.get_ldif_dir()
|
|
|
5d2be4 |
+ m1.db2ldif(bename=DEFAULT_BENAME, suffixes=[DEFAULT_SUFFIX],
|
|
|
5d2be4 |
+ excludeSuffixes=None, repl_data=False,
|
|
|
5d2be4 |
+ outputfile=ldif_file, encrypt=False)
|
|
|
5d2be4 |
+ # Remove replication metadata that are still in the ldif
|
|
|
5d2be4 |
+ # _remove_replication_data(ldif_file)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 3: Increase time skew on master2
|
|
|
5d2be4 |
+ timeSkew=6*3600
|
|
|
5d2be4 |
+ # We can modify master2 time skew
|
|
|
5d2be4 |
+ # But the time skew on the consumer may be smaller
|
|
|
5d2be4 |
+ # depending on when the cnsgen generation time is updated
|
|
|
5d2be4 |
+ # and when first csn get replicated.
|
|
|
5d2be4 |
+ # Since we use timeSkew has threshold value to detect
|
|
|
5d2be4 |
+ # whether there are time skew or not,
|
|
|
5d2be4 |
+ # lets add a significative margin (longer than the test duration)
|
|
|
5d2be4 |
+ # to avoid any risk of erroneous failure
|
|
|
5d2be4 |
+ timeSkewMargin = 300
|
|
|
5d2be4 |
+ DSEldif(m2)._increaseTimeSkew(DEFAULT_SUFFIX, timeSkew+timeSkewMargin)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 4: Init both masters from that ldif
|
|
|
5d2be4 |
+ m1.ldif2db(DEFAULT_BENAME, None, None, None, ldif_file)
|
|
|
5d2be4 |
+ m2.ldif2db(DEFAULT_BENAME, None, None, None, ldif_file)
|
|
|
5d2be4 |
+ m1.start()
|
|
|
5d2be4 |
+ m2.start()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 5: Perform on line init from master1 to consumer1
|
|
|
5d2be4 |
+ # and from master2 to consumer2
|
|
|
5d2be4 |
+ m1_c1.begin_reinit()
|
|
|
5d2be4 |
+ m2_c2.begin_reinit()
|
|
|
5d2be4 |
+ (done, error) = m1_c1.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+ (done, error) = m2_c2.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 6: Perform update on both masters
|
|
|
5d2be4 |
+ repl.test_replication(m1, c1)
|
|
|
5d2be4 |
+ repl.test_replication(m2, c2)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 7: Check that c1 has no time skew
|
|
|
5d2be4 |
+ # Stop server to insure that dse.ldif is uptodate
|
|
|
5d2be4 |
+ c1.stop()
|
|
|
5d2be4 |
+ c1_nsState = DSEldif(c1).readNsState(DEFAULT_SUFFIX)[0]
|
|
|
5d2be4 |
+ c1_timeSkew = int(c1_nsState['time_skew'])
|
|
|
5d2be4 |
+ log.debug(f"c1 time skew: {c1_timeSkew}")
|
|
|
5d2be4 |
+ if (c1_timeSkew >= timeSkew):
|
|
|
5d2be4 |
+ log.error(f"c1 csngen state has unexpectedly been synchronized with m2: time skew {c1_timeSkew}")
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+ c1.start()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 8: Check that c2 has time skew
|
|
|
5d2be4 |
+ # Stop server to insure that dse.ldif is uptodate
|
|
|
5d2be4 |
+ c2.stop()
|
|
|
5d2be4 |
+ c2_nsState = DSEldif(c2).readNsState(DEFAULT_SUFFIX)[0]
|
|
|
5d2be4 |
+ c2_timeSkew = int(c2_nsState['time_skew'])
|
|
|
5d2be4 |
+ log.debug(f"c2 time skew: {c2_timeSkew}")
|
|
|
5d2be4 |
+ if (c2_timeSkew < timeSkew):
|
|
|
5d2be4 |
+ log.error(f"c2 csngen state has not been synchronized with m2: time skew {c2_timeSkew}")
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+ c2.start()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 9: Perform on line init from master1 to master2
|
|
|
5d2be4 |
+ m1_c1.pause()
|
|
|
5d2be4 |
+ m1_m2.resume()
|
|
|
5d2be4 |
+ m1_m2.begin_reinit()
|
|
|
5d2be4 |
+ (done, error) = m1_m2.wait_reinit()
|
|
|
5d2be4 |
+ assert done is True
|
|
|
5d2be4 |
+ assert error is False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 10: Perform update on master2
|
|
|
5d2be4 |
+ repl.test_replication(m2, c1)
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Step 11: Check that c1 has time skew
|
|
|
5d2be4 |
+ # Stop server to insure that dse.ldif is uptodate
|
|
|
5d2be4 |
+ c1.stop()
|
|
|
5d2be4 |
+ c1_nsState = DSEldif(c1).readNsState(DEFAULT_SUFFIX)[0]
|
|
|
5d2be4 |
+ c1_timeSkew = int(c1_nsState['time_skew'])
|
|
|
5d2be4 |
+ log.debug(f"c1 time skew: {c1_timeSkew}")
|
|
|
5d2be4 |
+ if (c1_timeSkew < timeSkew):
|
|
|
5d2be4 |
+ log.error(f"c1 csngen state has not been synchronized with m2: time skew {c1_timeSkew}")
|
|
|
5d2be4 |
+ assert False
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
if __name__ == '__main__':
|
|
|
5d2be4 |
# Run isolated
|
|
|
5d2be4 |
# -s for DEBUG mode
|
|
|
5d2be4 |
diff --git a/ldap/servers/plugins/replication/repl5.h b/ldap/servers/plugins/replication/repl5.h
|
|
|
5d2be4 |
index 638471744..b2605011a 100644
|
|
|
5d2be4 |
--- a/ldap/servers/plugins/replication/repl5.h
|
|
|
5d2be4 |
+++ b/ldap/servers/plugins/replication/repl5.h
|
|
|
5d2be4 |
@@ -698,6 +698,7 @@ void replica_dump(Replica *r);
|
|
|
5d2be4 |
void replica_set_enabled(Replica *r, PRBool enable);
|
|
|
5d2be4 |
Replica *replica_get_replica_from_dn(const Slapi_DN *dn);
|
|
|
5d2be4 |
Replica *replica_get_replica_from_root(const char *repl_root);
|
|
|
5d2be4 |
+int replica_check_generation(Replica *r, const RUV *remote_ruv);
|
|
|
5d2be4 |
int replica_update_ruv(Replica *replica, const CSN *csn, const char *replica_purl);
|
|
|
5d2be4 |
Replica *replica_get_replica_for_op(Slapi_PBlock *pb);
|
|
|
5d2be4 |
/* the functions below manipulate replica hash */
|
|
|
5d2be4 |
diff --git a/ldap/servers/plugins/replication/repl5_inc_protocol.c b/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
|
|
5d2be4 |
index 29b1fb073..af5e5897c 100644
|
|
|
5d2be4 |
--- a/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
|
|
5d2be4 |
+++ b/ldap/servers/plugins/replication/repl5_inc_protocol.c
|
|
|
5d2be4 |
@@ -2161,26 +2161,12 @@ examine_update_vector(Private_Repl_Protocol *prp, RUV *remote_ruv)
|
|
|
5d2be4 |
} else if (NULL == remote_ruv) {
|
|
|
5d2be4 |
return_value = EXAMINE_RUV_PRISTINE_REPLICA;
|
|
|
5d2be4 |
} else {
|
|
|
5d2be4 |
- char *local_gen = NULL;
|
|
|
5d2be4 |
- char *remote_gen = ruv_get_replica_generation(remote_ruv);
|
|
|
5d2be4 |
- Object *local_ruv_obj;
|
|
|
5d2be4 |
- RUV *local_ruv;
|
|
|
5d2be4 |
-
|
|
|
5d2be4 |
PR_ASSERT(NULL != prp->replica);
|
|
|
5d2be4 |
- local_ruv_obj = replica_get_ruv(prp->replica);
|
|
|
5d2be4 |
- if (NULL != local_ruv_obj) {
|
|
|
5d2be4 |
- local_ruv = (RUV *)object_get_data(local_ruv_obj);
|
|
|
5d2be4 |
- PR_ASSERT(local_ruv);
|
|
|
5d2be4 |
- local_gen = ruv_get_replica_generation(local_ruv);
|
|
|
5d2be4 |
- object_release(local_ruv_obj);
|
|
|
5d2be4 |
- }
|
|
|
5d2be4 |
- if (NULL == remote_gen || NULL == local_gen || strcmp(remote_gen, local_gen) != 0) {
|
|
|
5d2be4 |
- return_value = EXAMINE_RUV_GENERATION_MISMATCH;
|
|
|
5d2be4 |
- } else {
|
|
|
5d2be4 |
+ if (replica_check_generation(prp->replica, remote_ruv)) {
|
|
|
5d2be4 |
return_value = EXAMINE_RUV_OK;
|
|
|
5d2be4 |
+ } else {
|
|
|
5d2be4 |
+ return_value = EXAMINE_RUV_GENERATION_MISMATCH;
|
|
|
5d2be4 |
}
|
|
|
5d2be4 |
- slapi_ch_free((void **)&remote_gen);
|
|
|
5d2be4 |
- slapi_ch_free((void **)&local_gen);
|
|
|
5d2be4 |
}
|
|
|
5d2be4 |
return return_value;
|
|
|
5d2be4 |
}
|
|
|
5d2be4 |
diff --git a/ldap/servers/plugins/replication/repl5_replica.c b/ldap/servers/plugins/replication/repl5_replica.c
|
|
|
5d2be4 |
index f0ea0f8ef..7e56d6557 100644
|
|
|
5d2be4 |
--- a/ldap/servers/plugins/replication/repl5_replica.c
|
|
|
5d2be4 |
+++ b/ldap/servers/plugins/replication/repl5_replica.c
|
|
|
5d2be4 |
@@ -812,6 +812,36 @@ replica_set_ruv(Replica *r, RUV *ruv)
|
|
|
5d2be4 |
replica_unlock(r->repl_lock);
|
|
|
5d2be4 |
}
|
|
|
5d2be4 |
|
|
|
5d2be4 |
+/*
|
|
|
5d2be4 |
+ * Check if replica generation is the same than the remote ruv one
|
|
|
5d2be4 |
+ */
|
|
|
5d2be4 |
+int
|
|
|
5d2be4 |
+replica_check_generation(Replica *r, const RUV *remote_ruv)
|
|
|
5d2be4 |
+{
|
|
|
5d2be4 |
+ int return_value;
|
|
|
5d2be4 |
+ char *local_gen = NULL;
|
|
|
5d2be4 |
+ char *remote_gen = ruv_get_replica_generation(remote_ruv);
|
|
|
5d2be4 |
+ Object *local_ruv_obj;
|
|
|
5d2be4 |
+ RUV *local_ruv;
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ PR_ASSERT(NULL != r);
|
|
|
5d2be4 |
+ local_ruv_obj = replica_get_ruv(r);
|
|
|
5d2be4 |
+ if (NULL != local_ruv_obj) {
|
|
|
5d2be4 |
+ local_ruv = (RUV *)object_get_data(local_ruv_obj);
|
|
|
5d2be4 |
+ PR_ASSERT(local_ruv);
|
|
|
5d2be4 |
+ local_gen = ruv_get_replica_generation(local_ruv);
|
|
|
5d2be4 |
+ object_release(local_ruv_obj);
|
|
|
5d2be4 |
+ }
|
|
|
5d2be4 |
+ if (NULL == remote_gen || NULL == local_gen || strcmp(remote_gen, local_gen) != 0) {
|
|
|
5d2be4 |
+ return_value = PR_FALSE;
|
|
|
5d2be4 |
+ } else {
|
|
|
5d2be4 |
+ return_value = PR_TRUE;
|
|
|
5d2be4 |
+ }
|
|
|
5d2be4 |
+ slapi_ch_free_string(&remote_gen);
|
|
|
5d2be4 |
+ slapi_ch_free_string(&local_gen);
|
|
|
5d2be4 |
+ return return_value;
|
|
|
5d2be4 |
+}
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
/*
|
|
|
5d2be4 |
* Update one particular CSN in an RUV. This is meant to be called
|
|
|
5d2be4 |
* whenever (a) the server has processed a client operation and
|
|
|
5d2be4 |
@@ -1298,6 +1328,11 @@ replica_update_csngen_state_ext(Replica *r, const RUV *ruv, const CSN *extracsn)
|
|
|
5d2be4 |
|
|
|
5d2be4 |
PR_ASSERT(r && ruv);
|
|
|
5d2be4 |
|
|
|
5d2be4 |
+ if (!replica_check_generation(r, ruv)) /* ruv has wrong generation - we are done */
|
|
|
5d2be4 |
+ {
|
|
|
5d2be4 |
+ return 0;
|
|
|
5d2be4 |
+ }
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
rc = ruv_get_max_csn(ruv, &csn;;
|
|
|
5d2be4 |
if (rc != RUV_SUCCESS) {
|
|
|
5d2be4 |
return -1;
|
|
|
5d2be4 |
@@ -3713,8 +3748,8 @@ replica_update_ruv_consumer(Replica *r, RUV *supplier_ruv)
|
|
|
5d2be4 |
replica_lock(r->repl_lock);
|
|
|
5d2be4 |
|
|
|
5d2be4 |
local_ruv = (RUV *)object_get_data(r->repl_ruv);
|
|
|
5d2be4 |
-
|
|
|
5d2be4 |
- if (is_cleaned_rid(supplier_id) || local_ruv == NULL) {
|
|
|
5d2be4 |
+ if (is_cleaned_rid(supplier_id) || local_ruv == NULL ||
|
|
|
5d2be4 |
+ !replica_check_generation(r, supplier_ruv)) {
|
|
|
5d2be4 |
replica_unlock(r->repl_lock);
|
|
|
5d2be4 |
return;
|
|
|
5d2be4 |
}
|
|
|
5d2be4 |
diff --git a/src/lib389/lib389/dseldif.py b/src/lib389/lib389/dseldif.py
|
|
|
5d2be4 |
index f2725add9..6e6be7cd2 100644
|
|
|
5d2be4 |
--- a/src/lib389/lib389/dseldif.py
|
|
|
5d2be4 |
+++ b/src/lib389/lib389/dseldif.py
|
|
|
5d2be4 |
@@ -316,6 +316,43 @@ class DSEldif(DSLint):
|
|
|
5d2be4 |
|
|
|
5d2be4 |
return states
|
|
|
5d2be4 |
|
|
|
5d2be4 |
+ def _increaseTimeSkew(self, suffix, timeSkew):
|
|
|
5d2be4 |
+ # Increase csngen state local_offset by timeSkew
|
|
|
5d2be4 |
+ # Warning: instance must be stopped before calling this function
|
|
|
5d2be4 |
+ assert (timeSkew >= 0)
|
|
|
5d2be4 |
+ nsState = self.readNsState(suffix)[0]
|
|
|
5d2be4 |
+ self._instance.log.debug(f'_increaseTimeSkew nsState is {nsState}')
|
|
|
5d2be4 |
+ oldNsState = self.get(nsState['dn'], 'nsState', True)
|
|
|
5d2be4 |
+ self._instance.log.debug(f'oldNsState is {oldNsState}')
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ # Lets reencode the new nsState
|
|
|
5d2be4 |
+ from lib389.utils import print_nice_time
|
|
|
5d2be4 |
+ if pack('
|
|
|
5d2be4 |
+ end = '<'
|
|
|
5d2be4 |
+ elif pack('>h', 1) == pack('=h',1):
|
|
|
5d2be4 |
+ end = '>'
|
|
|
5d2be4 |
+ else:
|
|
|
5d2be4 |
+ raise ValueError("Unknown endian, unable to proceed")
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
+ thelen = len(oldNsState)
|
|
|
5d2be4 |
+ if thelen <= 20:
|
|
|
5d2be4 |
+ pad = 2 # padding for short H values
|
|
|
5d2be4 |
+ timefmt = 'I' # timevals are unsigned 32-bit int
|
|
|
5d2be4 |
+ else:
|
|
|
5d2be4 |
+ pad = 6 # padding for short H values
|
|
|
5d2be4 |
+ timefmt = 'Q' # timevals are unsigned 64-bit int
|
|
|
5d2be4 |
+ fmtstr = "%sH%dx3%sH%dx" % (end, pad, timefmt, pad)
|
|
|
5d2be4 |
+ newNsState = base64.b64encode(pack(fmtstr, int(nsState['rid']),
|
|
|
5d2be4 |
+ int(nsState['gen_time']), int(nsState['local_offset'])+timeSkew,
|
|
|
5d2be4 |
+ int(nsState['remote_offset']), int(nsState['seq_num'])))
|
|
|
5d2be4 |
+ newNsState = newNsState.decode('utf-8')
|
|
|
5d2be4 |
+ self._instance.log.debug(f'newNsState is {newNsState}')
|
|
|
5d2be4 |
+ # Lets replace the value.
|
|
|
5d2be4 |
+ (entry_dn_i, attr_data) = self._find_attr(nsState['dn'], 'nsState')
|
|
|
5d2be4 |
+ attr_i = next(iter(attr_data))
|
|
|
5d2be4 |
+ self._contents[entry_dn_i + attr_i] = f"nsState:: {newNsState}"
|
|
|
5d2be4 |
+ self._update()
|
|
|
5d2be4 |
+
|
|
|
5d2be4 |
|
|
|
5d2be4 |
class FSChecks(DSLint):
|
|
|
5d2be4 |
"""This is for the healthcheck feature, check commonly used system config files the
|
|
|
5d2be4 |
--
|
|
|
5d2be4 |
2.26.2
|
|
|
5d2be4 |
|