summaryrefslogtreecommitdiff
path: root/source4/torture/drs
diff options
context:
space:
mode:
authorTim Beale <timbeale@catalyst.net.nz>2017-09-26 13:11:47 +1300
committerAndrew Bartlett <abartlet@samba.org>2017-09-26 05:33:17 +0200
commitd278f5ea9959b6ec6892f4b0c13a7dd3933ff364 (patch)
tree6509fd8898a9ae3976746e9cde8bd521d1d96707 /source4/torture/drs
parent1b395f488a94f2e2811622b61a8c750374d640ed (diff)
downloadsamba-d278f5ea9959b6ec6892f4b0c13a7dd3933ff364.tar.gz
selftest: Windows resolves object conflicts differently to Samba
While testing link conflicts I noticed that Windows resolves conflicts differently to Samba. Samba considers the version number first when resolving the conflict, whereas Windows always takes the latest change. The existing object conflict test cases didn't detect this problem because they were both modifying the object the same number of times (so they had the same version number). I've added new tests that highlight the problem. They are basically the same as the existing rename tests, except that only one DC does the rename. Samba will always pick the renamed object as the winner, whereas Windows picks the most recent change. I've marked this test as a known fail for now. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13039 Signed-off-by: Tim Beale <timbeale@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Diffstat (limited to 'source4/torture/drs')
-rw-r--r--source4/torture/drs/python/replica_sync.py80
1 files changed, 80 insertions, 0 deletions
diff --git a/source4/torture/drs/python/replica_sync.py b/source4/torture/drs/python/replica_sync.py
index 0886637c71d..c8944b0f3ca 100644
--- a/source4/torture/drs/python/replica_sync.py
+++ b/source4/torture/drs/python/replica_sync.py
@@ -298,6 +298,86 @@ objectClass: organizationalUnit
self._check_deleted(self.ldb_dc2, ou1_child)
self._check_deleted(self.ldb_dc2, ou2_child)
+ def test_ReplConflictsRenamedVsNewRemoteWin(self):
+ """Tests resolving a DN conflict between a renamed object and a new object"""
+ self._disable_inbound_repl(self.dnsname_dc1)
+ self._disable_inbound_repl(self.dnsname_dc2)
+
+ # Create an OU and rename it on DC1
+ self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Remote Rename Conflict orig")
+ self.ldb_dc1.rename("<GUID=%s>" % self.ou1, "OU=Test Remote Rename Conflict,%s" % self.domain_dn)
+
+ # We have to sleep to ensure that the two objects have different timestamps
+ time.sleep(1)
+
+ # create a conflicting object with the same DN on DC2
+ self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Remote Rename Conflict")
+
+ self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False)
+
+ # Check that DC2 got the DC1 object, and SELF.OU1 was made into conflict
+ res1 = self.ldb_dc1.search(base="<GUID=%s>" % self.ou1,
+ scope=SCOPE_BASE, attrs=["name"])
+ res2 = self.ldb_dc1.search(base="<GUID=%s>" % self.ou2,
+ scope=SCOPE_BASE, attrs=["name"])
+ print res1[0]["name"][0]
+ print res2[0]["name"][0]
+ self.assertTrue('CNF:%s' % self.ou1 in str(res1[0]["name"][0]))
+ self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn))
+ self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn))
+ self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value())
+ self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value())
+
+ # Delete both objects by GUID on DC1
+ self.ldb_dc1.delete('<GUID=%s>' % self.ou1)
+ self.ldb_dc1.delete('<GUID=%s>' % self.ou2)
+
+ self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False)
+
+ self._check_deleted(self.ldb_dc1, self.ou1)
+ self._check_deleted(self.ldb_dc1, self.ou2)
+ # Check deleted on DC2
+ self._check_deleted(self.ldb_dc2, self.ou1)
+ self._check_deleted(self.ldb_dc2, self.ou2)
+
+ def test_ReplConflictsRenamedVsNewLocalWin(self):
+ """Tests resolving a DN conflict between a renamed object and a new object"""
+ self._disable_inbound_repl(self.dnsname_dc1)
+ self._disable_inbound_repl(self.dnsname_dc2)
+
+ # Create conflicting objects on DC1 and DC2, where the DC2 object has been renamed
+ self.ou2 = self._create_ou(self.ldb_dc2, "OU=Test Rename Local Conflict orig")
+ self.ldb_dc2.rename("<GUID=%s>" % self.ou2, "OU=Test Rename Local Conflict,%s" % self.domain_dn)
+ # We have to sleep to ensure that the two objects have different timestamps
+ time.sleep(1)
+ self.ou1 = self._create_ou(self.ldb_dc1, "OU=Test Rename Local Conflict")
+
+ self._net_drs_replicate(DC=self.dnsname_dc1, fromDC=self.dnsname_dc2, forced=True, full_sync=False)
+
+ # Check that DC2 got the DC1 object, and OU2 was made into conflict
+ res1 = self.ldb_dc1.search(base="<GUID=%s>" % self.ou1,
+ scope=SCOPE_BASE, attrs=["name"])
+ res2 = self.ldb_dc1.search(base="<GUID=%s>" % self.ou2,
+ scope=SCOPE_BASE, attrs=["name"])
+ print res1[0]["name"][0]
+ print res2[0]["name"][0]
+ self.assertTrue('CNF:%s' % self.ou2 in str(res2[0]["name"][0]))
+ self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res1[0].dn))
+ self.assertTrue(self._lost_and_found_dn(self.ldb_dc1, self.domain_dn) not in str(res2[0].dn))
+ self.assertEqual(str(res1[0]["name"][0]), res1[0].dn.get_rdn_value())
+ self.assertEqual(str(res2[0]["name"][0]), res2[0].dn.get_rdn_value())
+
+ # Delete both objects by GUID on DC1
+ self.ldb_dc1.delete('<GUID=%s>' % self.ou1)
+ self.ldb_dc1.delete('<GUID=%s>' % self.ou2)
+
+ self._net_drs_replicate(DC=self.dnsname_dc2, fromDC=self.dnsname_dc1, forced=True, full_sync=False)
+
+ self._check_deleted(self.ldb_dc1, self.ou1)
+ self._check_deleted(self.ldb_dc1, self.ou2)
+ # Check deleted on DC2
+ self._check_deleted(self.ldb_dc2, self.ou1)
+ self._check_deleted(self.ldb_dc2, self.ou2)
def test_ReplConflictsRenameRemoteWin(self):
"""Tests that objects created in conflict become conflict DNs"""