1 # Unix SMB/CIFS implementation. Tests for DSDB locking
2 # Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """Tests for samba's dsdb modules"""
20 from samba
.tests
.samdb
import SamDBTestCase
21 from samba
.samdb
import SamDB
29 class DsdbLockTestCase(SamDBTestCase
):
30 def test_db_lock1(self
):
31 basedn
= self
.samdb
.get_default_basedn()
36 # In the child, close the main DB, re-open just one DB
39 self
.samdb
= SamDB(session_info
=self
.session
,
42 self
.samdb
.transaction_start()
44 dn
= "cn=test_db_lock_user,cn=users," + str(basedn
)
47 "objectclass": "user",
52 self
.samdb
.transaction_prepare_commit()
53 os
.write(w1
, b
"prepared")
57 self
.samdb
.transaction_cancel()
60 self
.assertEqual(os
.read(r1
, 8), b
"prepared")
64 # We need to hold this iterator open to hold the all-record lock.
65 res
= self
.samdb
.search_iterator()
67 # This should take at least 2 seconds because the transaction
68 # has a write lock on one backend db open
75 self
.assertGreater(end
- start
, 1.9)
77 (got_pid
, status
) = os
.waitpid(pid
, 0)
78 self
.assertEqual(got_pid
, pid
)
79 self
.assertTrue(os
.WIFEXITED(status
))
80 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
82 def test_db_lock2(self
):
83 basedn
= self
.samdb
.get_default_basedn()
89 # In the child, close the main DB, re-open
92 self
.samdb
= SamDB(session_info
=self
.session
,
95 # We need to hold this iterator open to hold the all-record lock.
96 res
= self
.samdb
.search_iterator()
98 os
.write(w2
, b
"start")
99 if (os
.read(r1
, 7) != b
"started"):
103 if (os
.read(r1
, 5) != b
"added"):
106 # Wait 2 seconds to block prepare_commit() in the child.
107 os
.write(w2
, b
"prepare")
114 if (os
.read(r1
, 8) != b
"prepared"):
119 # We can start the transaction during the search
120 # because both just grab the all-record read lock.
121 self
.assertEqual(os
.read(r2
, 5), b
"start")
122 self
.samdb
.transaction_start()
123 os
.write(w1
, b
"started")
125 self
.assertEqual(os
.read(r2
, 3), b
"add")
126 dn
= "cn=test_db_lock_user,cn=users," + str(basedn
)
129 "objectclass": "user",
131 self
.samdb
.delete(dn
)
132 os
.write(w1
, b
"added")
134 # Obtain a write lock, this will block until
135 # the parent releases the read lock.
136 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
138 self
.samdb
.transaction_prepare_commit()
141 self
.assertGreater(end
- start
, 1.9)
145 os
.write(w1
, b
"prepared")
147 # Drop the write lock
148 self
.samdb
.transaction_cancel()
150 (got_pid
, status
) = os
.waitpid(pid
, 0)
151 self
.assertEqual(got_pid
, pid
)
152 self
.assertTrue(os
.WIFEXITED(status
))
153 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
155 def test_db_lock3(self
):
156 basedn
= self
.samdb
.get_default_basedn()
162 # In the child, close the main DB, re-open
165 self
.samdb
= SamDB(session_info
=self
.session
,
168 # We need to hold this iterator open to hold the all-record lock.
169 res
= self
.samdb
.search_iterator()
171 os
.write(w2
, b
"start")
172 if (os
.read(r1
, 7) != b
"started"):
176 if (os
.read(r1
, 5) != b
"added"):
179 # Wait 2 seconds to block prepare_commit() in the child.
180 os
.write(w2
, b
"prepare")
187 if (os
.read(r1
, 8) != b
"prepared"):
192 # We can start the transaction during the search
193 # because both just grab the all-record read lock.
194 self
.assertEqual(os
.read(r2
, 5), b
"start")
195 self
.samdb
.transaction_start()
196 os
.write(w1
, b
"started")
198 self
.assertEqual(os
.read(r2
, 3), b
"add")
200 # This will end up in the top level db
201 dn
= "@DSDB_LOCK_TEST"
204 self
.samdb
.delete(dn
)
205 os
.write(w1
, b
"added")
207 # Obtain a write lock, this will block until
208 # the child releases the read lock.
209 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
211 self
.samdb
.transaction_prepare_commit()
213 self
.assertGreater(end
- start
, 1.9)
214 os
.write(w1
, b
"prepared")
216 # Drop the write lock
217 self
.samdb
.transaction_cancel()
219 (got_pid
, status
) = os
.waitpid(pid
, 0)
220 self
.assertTrue(os
.WIFEXITED(status
))
221 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
222 self
.assertEqual(got_pid
, pid
)
224 def _test_full_db_lock1(self
, backend_path
):
229 # In the child, close the main DB, re-open just one DB
233 backenddb
= ldb
.Ldb(backend_path
)
235 backenddb
.transaction_start()
237 backenddb
.add({"dn": "@DSDB_LOCK_TEST"})
238 backenddb
.delete("@DSDB_LOCK_TEST")
240 # Obtain a write lock
241 backenddb
.transaction_prepare_commit()
242 os
.write(w1
, b
"prepared")
245 # Drop the write lock
246 backenddb
.transaction_cancel()
249 self
.assertEqual(os
.read(r1
, 8), b
"prepared")
253 # We need to hold this iterator open to hold the all-record lock.
254 res
= self
.samdb
.search_iterator()
256 # This should take at least 2 seconds because the transaction
257 # has a write lock on one backend db open
260 self
.assertGreater(end
- start
, 1.9)
266 (got_pid
, status
) = os
.waitpid(pid
, 0)
267 self
.assertEqual(got_pid
, pid
)
268 self
.assertTrue(os
.WIFEXITED(status
))
269 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
271 def test_full_db_lock1(self
):
272 basedn
= self
.samdb
.get_default_basedn()
273 backend_filename
= "%s.ldb" % basedn
.get_casefold()
274 backend_subpath
= os
.path
.join("sam.ldb.d",
276 backend_path
= self
.lp
.private_path(backend_subpath
)
277 self
._test
_full
_db
_lock
1(backend_path
)
279 def test_full_db_lock1_config(self
):
280 basedn
= self
.samdb
.get_config_basedn()
281 backend_filename
= "%s.ldb" % basedn
.get_casefold()
282 backend_subpath
= os
.path
.join("sam.ldb.d",
284 backend_path
= self
.lp
.private_path(backend_subpath
)
285 self
._test
_full
_db
_lock
1(backend_path
)
287 def _test_full_db_lock2(self
, backend_path
):
294 # In the child, close the main DB, re-open
297 self
.samdb
= SamDB(session_info
=self
.session
,
300 # We need to hold this iterator open to hold the all-record lock.
301 res
= self
.samdb
.search_iterator()
303 os
.write(w2
, b
"start")
304 if (os
.read(r1
, 7) != b
"started"):
307 if (os
.read(r1
, 5) != b
"added"):
310 # Wait 2 seconds to block prepare_commit() in the child.
311 os
.write(w2
, b
"prepare")
318 if (os
.read(r1
, 8) != b
"prepared"):
323 # In the parent, close the main DB, re-open just one DB
326 backenddb
= ldb
.Ldb(backend_path
)
328 # We can start the transaction during the search
329 # because both just grab the all-record read lock.
330 self
.assertEqual(os
.read(r2
, 5), b
"start")
331 backenddb
.transaction_start()
332 os
.write(w1
, b
"started")
334 self
.assertEqual(os
.read(r2
, 3), b
"add")
335 backenddb
.add({"dn": "@DSDB_LOCK_TEST"})
336 backenddb
.delete("@DSDB_LOCK_TEST")
337 os
.write(w1
, b
"added")
339 # Obtain a write lock, this will block until
340 # the child releases the read lock.
341 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
343 backenddb
.transaction_prepare_commit()
347 self
.assertGreater(end
- start
, 1.9)
351 os
.write(w1
, b
"prepared")
353 # Drop the write lock
354 backenddb
.transaction_cancel()
356 (got_pid
, status
) = os
.waitpid(pid
, 0)
357 self
.assertEqual(got_pid
, pid
)
358 self
.assertTrue(os
.WIFEXITED(status
))
359 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
361 def test_full_db_lock2(self
):
362 basedn
= self
.samdb
.get_default_basedn()
363 backend_filename
= "%s.ldb" % basedn
.get_casefold()
364 backend_subpath
= os
.path
.join("sam.ldb.d",
366 backend_path
= self
.lp
.private_path(backend_subpath
)
367 self
._test
_full
_db
_lock
2(backend_path
)
369 def test_full_db_lock2_config(self
):
370 basedn
= self
.samdb
.get_config_basedn()
371 backend_filename
= "%s.ldb" % basedn
.get_casefold()
372 backend_subpath
= os
.path
.join("sam.ldb.d",
374 backend_path
= self
.lp
.private_path(backend_subpath
)
375 self
._test
_full
_db
_lock
2(backend_path
)