1 # Unix SMB/CIFS implementation. Tests for SamDB
2 # Copyright (C) Jelmer Vernooij <jelmer@samba.org> 2008
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
28 class DsdbLockTestCase(SamDBTestCase
):
29 def test_db_lock1(self
):
30 basedn
= self
.samdb
.get_default_basedn()
35 # In the child, close the main DB, re-open just one DB
38 self
.samdb
= SamDB(session_info
=self
.session
,
41 self
.samdb
.transaction_start()
43 dn
= "cn=test_db_lock_user,cn=users," + str(basedn
)
46 "objectclass": "user",
51 self
.samdb
.transaction_prepare_commit()
52 os
.write(w1
, b
"prepared")
56 self
.samdb
.transaction_cancel()
59 self
.assertEqual(os
.read(r1
, 8), b
"prepared")
63 # We need to hold this iterator open to hold the all-record lock.
64 res
= self
.samdb
.search_iterator()
66 # This should take at least 2 seconds because the transaction
67 # has a write lock on one backend db open
74 self
.assertGreater(end
- start
, 1.9)
76 (got_pid
, status
) = os
.waitpid(pid
, 0)
77 self
.assertEqual(got_pid
, pid
)
78 self
.assertTrue(os
.WIFEXITED(status
))
79 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
81 def test_db_lock2(self
):
82 basedn
= self
.samdb
.get_default_basedn()
88 # In the child, close the main DB, re-open
91 self
.samdb
= SamDB(session_info
=self
.session
,
94 # We need to hold this iterator open to hold the all-record lock.
95 res
= self
.samdb
.search_iterator()
97 os
.write(w2
, b
"start")
98 if (os
.read(r1
, 7) != b
"started"):
102 if (os
.read(r1
, 5) != b
"added"):
105 # Wait 2 seconds to block prepare_commit() in the child.
106 os
.write(w2
, b
"prepare")
113 if (os
.read(r1
, 8) != b
"prepared"):
118 # We can start the transaction during the search
119 # because both just grab the all-record read lock.
120 self
.assertEqual(os
.read(r2
, 5), b
"start")
121 self
.samdb
.transaction_start()
122 os
.write(w1
, b
"started")
124 self
.assertEqual(os
.read(r2
, 3), b
"add")
125 dn
= "cn=test_db_lock_user,cn=users," + str(basedn
)
128 "objectclass": "user",
130 self
.samdb
.delete(dn
)
131 os
.write(w1
, b
"added")
133 # Obtain a write lock, this will block until
134 # the parent releases the read lock.
135 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
137 self
.samdb
.transaction_prepare_commit()
140 self
.assertGreater(end
- start
, 1.9)
144 os
.write(w1
, b
"prepared")
146 # Drop the write lock
147 self
.samdb
.transaction_cancel()
149 (got_pid
, status
) = os
.waitpid(pid
, 0)
150 self
.assertEqual(got_pid
, pid
)
151 self
.assertTrue(os
.WIFEXITED(status
))
152 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
154 def test_db_lock3(self
):
155 basedn
= self
.samdb
.get_default_basedn()
161 # In the child, close the main DB, re-open
164 self
.samdb
= SamDB(session_info
=self
.session
,
167 # We need to hold this iterator open to hold the all-record lock.
168 res
= self
.samdb
.search_iterator()
170 os
.write(w2
, b
"start")
171 if (os
.read(r1
, 7) != b
"started"):
175 if (os
.read(r1
, 5) != b
"added"):
178 # Wait 2 seconds to block prepare_commit() in the child.
179 os
.write(w2
, b
"prepare")
186 if (os
.read(r1
, 8) != b
"prepared"):
191 # We can start the transaction during the search
192 # because both just grab the all-record read lock.
193 self
.assertEqual(os
.read(r2
, 5), b
"start")
194 self
.samdb
.transaction_start()
195 os
.write(w1
, b
"started")
197 self
.assertEqual(os
.read(r2
, 3), b
"add")
199 # This will end up in the top level db
200 dn
= "@DSDB_LOCK_TEST"
203 self
.samdb
.delete(dn
)
204 os
.write(w1
, b
"added")
206 # Obtain a write lock, this will block until
207 # the child releases the read lock.
208 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
210 self
.samdb
.transaction_prepare_commit()
212 self
.assertGreater(end
- start
, 1.9)
213 os
.write(w1
, b
"prepared")
215 # Drop the write lock
216 self
.samdb
.transaction_cancel()
218 (got_pid
, status
) = os
.waitpid(pid
, 0)
219 self
.assertTrue(os
.WIFEXITED(status
))
220 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
221 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
)
236 backenddb
.transaction_start()
238 backenddb
.add({"dn":"@DSDB_LOCK_TEST"})
239 backenddb
.delete("@DSDB_LOCK_TEST")
241 # Obtain a write lock
242 backenddb
.transaction_prepare_commit()
243 os
.write(w1
, b
"prepared")
246 # Drop the write lock
247 backenddb
.transaction_cancel()
250 self
.assertEqual(os
.read(r1
, 8), b
"prepared")
254 # We need to hold this iterator open to hold the all-record lock.
255 res
= self
.samdb
.search_iterator()
257 # This should take at least 2 seconds because the transaction
258 # has a write lock on one backend db open
261 self
.assertGreater(end
- start
, 1.9)
267 (got_pid
, status
) = os
.waitpid(pid
, 0)
268 self
.assertEqual(got_pid
, pid
)
269 self
.assertTrue(os
.WIFEXITED(status
))
270 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
272 def test_full_db_lock1(self
):
273 basedn
= self
.samdb
.get_default_basedn()
274 backend_filename
= "%s.ldb" % basedn
.get_casefold()
275 backend_subpath
= os
.path
.join("sam.ldb.d",
277 backend_path
= self
.lp
.private_path(backend_subpath
)
278 self
._test
_full
_db
_lock
1(backend_path
)
281 def test_full_db_lock1_config(self
):
282 basedn
= self
.samdb
.get_config_basedn()
283 backend_filename
= "%s.ldb" % basedn
.get_casefold()
284 backend_subpath
= os
.path
.join("sam.ldb.d",
286 backend_path
= self
.lp
.private_path(backend_subpath
)
287 self
._test
_full
_db
_lock
1(backend_path
)
290 def _test_full_db_lock2(self
, backend_path
):
297 # In the child, close the main DB, re-open
300 self
.samdb
= SamDB(session_info
=self
.session
,
303 # We need to hold this iterator open to hold the all-record lock.
304 res
= self
.samdb
.search_iterator()
306 os
.write(w2
, b
"start")
307 if (os
.read(r1
, 7) != b
"started"):
310 if (os
.read(r1
, 5) != b
"added"):
313 # Wait 2 seconds to block prepare_commit() in the child.
314 os
.write(w2
, b
"prepare")
321 if (os
.read(r1
, 8) != b
"prepared"):
326 # In the parent, close the main DB, re-open just one DB
329 backenddb
= ldb
.Ldb(backend_path
)
331 # We can start the transaction during the search
332 # because both just grab the all-record read lock.
333 self
.assertEqual(os
.read(r2
, 5), b
"start")
334 backenddb
.transaction_start()
335 os
.write(w1
, b
"started")
337 self
.assertEqual(os
.read(r2
, 3), b
"add")
338 backenddb
.add({"dn":"@DSDB_LOCK_TEST"})
339 backenddb
.delete("@DSDB_LOCK_TEST")
340 os
.write(w1
, b
"added")
342 # Obtain a write lock, this will block until
343 # the child releases the read lock.
344 self
.assertEqual(os
.read(r2
, 7), b
"prepare")
346 backenddb
.transaction_prepare_commit()
350 self
.assertGreater(end
- start
, 1.9)
354 os
.write(w1
, b
"prepared")
356 # Drop the write lock
357 backenddb
.transaction_cancel()
359 (got_pid
, status
) = os
.waitpid(pid
, 0)
360 self
.assertEqual(got_pid
, pid
)
361 self
.assertTrue(os
.WIFEXITED(status
))
362 self
.assertEqual(os
.WEXITSTATUS(status
), 0)
364 def test_full_db_lock2(self
):
365 basedn
= self
.samdb
.get_default_basedn()
366 backend_filename
= "%s.ldb" % basedn
.get_casefold()
367 backend_subpath
= os
.path
.join("sam.ldb.d",
369 backend_path
= self
.lp
.private_path(backend_subpath
)
370 self
._test
_full
_db
_lock
2(backend_path
)
372 def test_full_db_lock2_config(self
):
373 basedn
= self
.samdb
.get_config_basedn()
374 backend_filename
= "%s.ldb" % basedn
.get_casefold()
375 backend_subpath
= os
.path
.join("sam.ldb.d",
377 backend_path
= self
.lp
.private_path(backend_subpath
)
378 self
._test
_full
_db
_lock
2(backend_path
)