def test_acquire(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) fs.add_lockspace("lockspace", 1, "path") fd = fs.register() fs.acquire("lockspace", "resource", [("path", 1048576)], slkfd=fd) res = fs.read_resource("path", 1048576) self.assertTrue(res["acquired"], "resource is not acquired")
def fake_sanlock(monkeypatch): fs = FakeSanlock() monkeypatch.setattr(clusterlock, "sanlock", fs) # FakeSanlock does not implement the depracated init_resource, so we will # create the resource using write_resource, so we can test acquire and # release. fs.write_resource(LS_NAME, LEASE.name, [(LEASE.path, LEASE.offset)]) return fs
def test_write_read_resource(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) info = fs.read_resource("path", 1048576) expected = {"resource": "resource", "lockspace": "lockspace", "version": 0} self.assertEqual(info, expected)
def test_add_lockspace_sync(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") ls = fs.spaces["lockspace"] self.assertEqual(ls["host_id"], 1) self.assertEqual(ls["path"], "path") self.assertEqual(ls["offset"], 0) self.assertEqual(ls["iotimeout"], 0) self.assertTrue(ls["ready"].is_set(), "lockspace is not ready")
def test_write_read_resource(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) info = fs.read_resource("path", 1048576) expected = { "resource": "resource", "lockspace": "lockspace", "version": 0 } self.assertEqual(info, expected)
def test_inq_lockspace_acquiring_wait(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path", async=True) t = concurrent.thread(fs.complete_async, args=("lockspace", )) t.start() try: acquired = fs.inq_lockspace("lockspace", 1, "path", wait=True) finally: t.join() self.assertTrue(acquired, "lockspace not acquired")
def test_release_not_acquired(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) fs.add_lockspace("lockspace", 1, "path") fd = fs.register() with self.assertRaises(fs.SanlockException) as e: fs.release("lockspace", "resource", [("path", 1048576)], slkfd=fd) self.assertEqual(e.exception.errno, errno.EPERM)
def test_write_resource_failure(self): fs = FakeSanlock() fs.errors["write_resource"] = ExpectedError with self.assertRaises(ExpectedError): fs.write_resource("lockspace", "resource", [("path", 1048576)]) with self.assertRaises(fs.SanlockException) as e: fs.read_resource("path", 1048576) self.assertEqual(e.exception.errno, fs.SANLK_LEADER_MAGIC)
def test_acquire_lockspace_adding(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) fs.add_lockspace("lockspace", 1, "path", async=True) fd = fs.register() with self.assertRaises(fs.SanlockException) as e: fs.acquire("lockspace", "resource", [("path", 1048576)], slkfd=fd) self.assertEqual(e.exception.errno, errno.ENOSPC)
class TestIndex(VdsmTestCase): def test_format(self): with make_volume() as vol: self.assertEqual(vol.leases(), {}) def test_create_read_failure(self): with make_leases() as path: file = FailingReader(path) with utils.closing(file): with self.assertRaises(ReadError): xlease.LeasesVolume("lockspace", file) def test_lookup_missing(self): with make_volume() as vol: with self.assertRaises(xlease.NoSuchLease): vol.lookup(make_uuid()) def test_lookup_stale(self): record = xlease.Record(make_uuid(), xlease.RECORD_STALE) with make_volume((42, record)) as vol: leases = vol.leases() self.assertEqual(leases[record.resource]["state"], "STALE") with self.assertRaises(xlease.StaleLease): vol.lookup(record.resource) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add(self): with make_volume() as vol: lease_id = make_uuid() start_time = int(time.time()) lease = vol.add(lease_id) self.assertEqual(lease.lockspace, vol.lockspace) self.assertEqual(lease.resource, lease_id) self.assertEqual(lease.path, vol.path) self.assertTrue(start_time <= lease.modified <= start_time + 1) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) self.assertEqual(res["lockspace"], lease.lockspace) self.assertEqual(res["resource"], lease.resource) def test_add_write_failure(self): with make_volume() as base: file = FailingWriter(base.path) with utils.closing(file): vol = xlease.LeasesVolume(base.lockspace, file) with utils.closing(vol): lease_id = make_uuid() with self.assertRaises(WriteError): vol.add(lease_id) # Must succeed becuase writng to storage failed self.assertNotIn(lease_id, vol.leases()) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_sanlock_failure(self): with make_volume() as vol: lease_id = make_uuid() sanlock = xlease.sanlock # Make sanlock fail to write a resource sanlock.errors["write_resource"] = sanlock.SanlockException with self.assertRaises(sanlock.SanlockException): vol.add(lease_id) # We should have a stale lease record lease = vol.leases()[lease_id] self.assertEqual(lease["state"], "STALE") # There should be no lease on storage with self.assertRaises(sanlock.SanlockException) as e: sanlock.read_resource(vol.path, lease["offset"]) self.assertEqual(e.exception.errno, sanlock.SANLK_LEADER_MAGIC) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_leases(self): with make_volume() as vol: uuid = make_uuid() lease_info = vol.add(uuid) leases = vol.leases() self.assertEqual(len(leases), 1) self.assertEqual(leases[uuid]["offset"], xlease.LEASE_BASE) self.assertEqual(leases[uuid]["state"], "USED") self.assertEqual(leases[uuid]["modified"], lease_info.modified) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_exists(self): with make_volume() as vol: lease_id = make_uuid() lease = vol.add(lease_id) with self.assertRaises(xlease.LeaseExists): vol.add(lease_id) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) self.assertEqual(res["lockspace"], lease.lockspace) self.assertEqual(res["resource"], lease.resource) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_lookup_exists(self): with make_volume() as vol: lease_id = make_uuid() add_info = vol.add(lease_id) lookup_info = vol.lookup(lease_id) self.assertEqual(add_info, lookup_info) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_remove_exists(self): with make_volume() as vol: leases = [make_uuid() for i in range(3)] for lease in leases: vol.add(lease) lease = vol.lookup(leases[1]) vol.remove(lease.resource) self.assertNotIn(lease.resource, vol.leases()) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) # There is no sanlock api for removing a resource, so we mark a # removed resource with empty (invalid) lockspace and lease id. self.assertEqual(res["lockspace"], "") self.assertEqual(res["resource"], "") def test_remove_missing(self): with make_volume() as vol: lease_id = make_uuid() with self.assertRaises(xlease.NoSuchLease): vol.remove(lease_id) def test_remove_write_failure(self): record = xlease.Record(make_uuid(), xlease.RECORD_STALE) with make_volume((42, record)) as base: file = FailingWriter(base.path) with utils.closing(file): vol = xlease.LeasesVolume(base.lockspace, file) with utils.closing(vol): with self.assertRaises(WriteError): vol.remove(record.resource) # Must succeed becuase writng to storage failed self.assertIn(record.resource, vol.leases()) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_remove_sanlock_failure(self): with make_volume() as vol: lease_id = make_uuid() vol.add(lease_id) sanlock = xlease.sanlock # Make sanlock fail to remove a resource (currnently removing a # resouce by writing invalid lockspace and resoruce name). sanlock.errors["write_resource"] = sanlock.SanlockException with self.assertRaises(sanlock.SanlockException): vol.remove(lease_id) # We should have a stale lease record lease = vol.leases()[lease_id] self.assertEqual(lease["state"], "STALE") # There lease should still be on storage res = sanlock.read_resource(vol.path, lease["offset"]) self.assertEqual(res["lockspace"], vol.lockspace) self.assertEqual(res["resource"], lease_id) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_first_free_slot(self): with make_volume() as vol: uuids = [make_uuid() for i in range(4)] for uuid in uuids[:3]: vol.add(uuid) vol.remove(uuids[1]) vol.add(uuids[3]) leases = vol.leases() # The first lease in the first slot self.assertEqual(leases[uuids[0]]["offset"], xlease.LEASE_BASE) # The forth lease was added in the second slot after the second # lease was removed. self.assertEqual(leases[uuids[3]]["offset"], xlease.LEASE_BASE + xlease.LEASE_SIZE) # The third lease in the third slot self.assertEqual(leases[uuids[2]]["offset"], xlease.LEASE_BASE + xlease.LEASE_SIZE * 2) @slowtest def test_time_lookup(self): setup = """ import os from testlib import make_uuid from vdsm import utils from vdsm.storage import xlease path = "%s" lockspace = os.path.basename(os.path.dirname(path)) lease_id = make_uuid() def bench(): file = xlease.DirectFile(path) with utils.closing(file): vol = xlease.LeasesVolume(lockspace, file) with utils.closing(vol, log="test"): try: vol.lookup(lease_id) except xlease.NoSuchLease: pass """ with make_volume() as vol: count = 1000 elapsed = timeit.timeit("bench()", setup=setup % vol.path, number=count) print("%d lookups in %.6f seconds (%.6f seconds per lookup)" % (count, elapsed, elapsed / count)) @slowtest @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_time_add(self): setup = """ import os from testlib import make_uuid from vdsm import utils from vdsm.storage import xlease path = "%s" lockspace = os.path.basename(os.path.dirname(path)) def bench(): lease_id = make_uuid() file = xlease.DirectFile(path) with utils.closing(file): vol = xlease.LeasesVolume(lockspace, file) with utils.closing(vol, log="test"): vol.add(lease_id) """ with make_volume() as vol: count = 100 elapsed = timeit.timeit("bench()", setup=setup % vol.path, number=count) # Note: this does not include the time to create the real sanlock # resource. print("%d adds in %.6f seconds (%.6f seconds per add)" % (count, elapsed, elapsed / count))
def test_inq_lockspace_released(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") fs.rem_lockspace("lockspace", 1, "path") acquired = fs.inq_lockspace("lockspace", 1, "path") self.assertFalse(acquired, "lockspace not released")
def test_register(self): fs = FakeSanlock() self.assertEqual(fs.register(), 42)
def test_add_lockspace_async(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path", async=True) ls = fs.spaces["lockspace"] self.assertEqual(ls["iotimeout"], 0) self.assertFalse(ls["ready"].is_set(), "lockspace is ready")
def test_add_lockspace_options(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path", offset=42, iotimeout=10) ls = fs.spaces["lockspace"] self.assertEqual(ls["offset"], 42) self.assertEqual(ls["iotimeout"], 10)
def test_acquire_an_acquired_resource(self): fs = FakeSanlock() fs.write_resource("lockspace", "resource", [("path", 1048576)]) fs.add_lockspace("lockspace", 1, "path") fd = fs.register() fs.acquire("lockspace", "resource", [("path", 1048576)], slkfd=fd) with self.assertRaises(fs.SanlockException) as e: fs.acquire("lockspace", "resource", [("path", 1048576)], slkfd=fd) self.assertEqual(e.exception.errno, errno.EEXIST) res = fs.read_resource("path", 1048576) self.assertTrue(res["acquired"], "resource is not acquired")
def test_non_existing_resource(self): fs = FakeSanlock() with self.assertRaises(fs.SanlockException) as e: fs.read_resource("path", 1048576) self.assertEqual(e.exception.errno, fs.SANLK_LEADER_MAGIC)
def test_rem_lockspace_sync(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") fs.rem_lockspace("lockspace", 1, "path") self.assertNotIn("lockspace", fs.spaces)
def test_read_resource_failure(self): fs = FakeSanlock() fs.errors["read_resource"] = ExpectedError fs.write_resource("lockspace", "resource", [("path", 1048576)]) with self.assertRaises(ExpectedError): fs.read_resource("path", 1048576)
def test_rem_lockspace_async(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") fs.rem_lockspace("lockspace", 1, "path", async=True) ls = fs.spaces["lockspace"] self.assertFalse(ls["ready"].is_set(), "lockspace is ready")
def test_inq_lockspace_acquired(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") acquired = fs.inq_lockspace("lockspace", 1, "path") self.assertTrue(acquired, "lockspace not acquired")
def test_inq_lockspace_acquring_no_wait(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path", async=True) acquired = fs.inq_lockspace("lockspace", 1, "path") self.assertIsNone(acquired, "lockspace is ready")
class TestIndex(VdsmTestCase): @MonkeyPatch(time, 'time', lambda: 123456789) def test_metadata(self): with make_volume() as vol: lockspace = os.path.basename(os.path.dirname(vol.path)) self.assertEqual(vol.version, 1) self.assertEqual(vol.lockspace, lockspace) self.assertEqual(vol.mtime, 123456789) def test_magic_big_endian(self): with make_volume() as vol: with io.open(vol.path, "rb") as f: f.seek(xlease.INDEX_BASE) self.assertEqual(f.read(4), b"\x12\x15\x20\x16") def test_bad_magic(self): with make_leases() as path: self.check_invalid_index(path) def test_bad_version(self): with make_volume() as vol: with io.open(vol.path, "r+b") as f: f.seek(xlease.INDEX_BASE + 5) f.write(b"blah") self.check_invalid_index(vol.path) def test_unsupported_version(self): with make_volume() as vol: md = xlease.IndexMetadata(2, "lockspace") with io.open(vol.path, "r+b") as f: f.seek(xlease.INDEX_BASE) f.write(md.bytes()) self.check_invalid_index(vol.path) def test_bad_lockspace(self): with make_volume() as vol: with io.open(vol.path, "r+b") as f: f.seek(xlease.INDEX_BASE + 10) f.write(b"\xf0") self.check_invalid_index(vol.path) def test_bad_mtime(self): with make_volume() as vol: with io.open(vol.path, "r+b") as f: f.seek(xlease.INDEX_BASE + 59) f.write(b"not a number") self.check_invalid_index(vol.path) def test_updating(self): with make_volume() as vol: md = xlease.IndexMetadata(xlease.INDEX_VERSION, "lockspace", updating=True) with io.open(vol.path, "r+b") as f: f.seek(xlease.INDEX_BASE) f.write(md.bytes()) self.check_invalid_index(vol.path) def test_truncated_index(self): with make_volume() as vol: # Truncate index, reading it should fail. with io.open(vol.path, "r+b") as f: f.truncate( xlease.INDEX_BASE + xlease.INDEX_SIZE - xlease.BLOCK_SIZE) self.check_invalid_index(vol.path) def check_invalid_index(self, path): file = xlease.DirectFile(path) with utils.closing(file): with self.assertRaises(xlease.InvalidIndex): vol = xlease.LeasesVolume(file) vol.close() def test_format(self): with make_volume() as vol: self.assertEqual(vol.leases(), {}) def test_create_read_failure(self): with make_leases() as path: file = FailingReader(path) with utils.closing(file): with self.assertRaises(ReadError): xlease.LeasesVolume(file) def test_lookup_missing(self): with make_volume() as vol: with self.assertRaises(xlease.NoSuchLease): vol.lookup(make_uuid()) def test_lookup_updating(self): record = xlease.Record(make_uuid(), 0, updating=True) with make_volume((42, record)) as vol: leases = vol.leases() self.assertTrue(leases[record.resource]["updating"]) with self.assertRaises(xlease.LeaseUpdating): vol.lookup(record.resource) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add(self): with make_volume() as vol: lease_id = make_uuid() lease = vol.add(lease_id) self.assertEqual(lease.lockspace, vol.lockspace) self.assertEqual(lease.resource, lease_id) self.assertEqual(lease.path, vol.path) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) self.assertEqual(res["lockspace"], lease.lockspace) self.assertEqual(res["resource"], lease.resource) def test_add_write_failure(self): with make_volume() as base: file = FailingWriter(base.path) with utils.closing(file): vol = xlease.LeasesVolume(file) with utils.closing(vol): lease_id = make_uuid() with self.assertRaises(WriteError): vol.add(lease_id) # Must succeed becuase writng to storage failed self.assertNotIn(lease_id, vol.leases()) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_sanlock_failure(self): with make_volume() as vol: lease_id = make_uuid() sanlock = xlease.sanlock # Make sanlock fail to write a resource sanlock.errors["write_resource"] = sanlock.SanlockException with self.assertRaises(sanlock.SanlockException): vol.add(lease_id) # We should have an updating lease record lease = vol.leases()[lease_id] self.assertTrue(lease["updating"]) # There should be no lease on storage with self.assertRaises(sanlock.SanlockException) as e: sanlock.read_resource(vol.path, lease["offset"]) self.assertEqual(e.exception.errno, sanlock.SANLK_LEADER_MAGIC) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_leases(self): with make_volume() as vol: uuid = make_uuid() lease_info = vol.add(uuid) leases = vol.leases() expected = { uuid: { "offset": lease_info.offset, "updating": False, } } self.assertEqual(leases, expected) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_exists(self): with make_volume() as vol: lease_id = make_uuid() lease = vol.add(lease_id) with self.assertRaises(xlease.LeaseExists): vol.add(lease_id) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) self.assertEqual(res["lockspace"], lease.lockspace) self.assertEqual(res["resource"], lease.resource) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_lookup_exists(self): with make_volume() as vol: lease_id = make_uuid() add_info = vol.add(lease_id) lookup_info = vol.lookup(lease_id) self.assertEqual(add_info, lookup_info) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_remove_exists(self): with make_volume() as vol: leases = [make_uuid() for i in range(3)] for lease in leases: vol.add(lease) lease = vol.lookup(leases[1]) vol.remove(lease.resource) self.assertNotIn(lease.resource, vol.leases()) sanlock = xlease.sanlock res = sanlock.read_resource(lease.path, lease.offset) # There is no sanlock api for removing a resource, so we mark a # removed resource with empty (invalid) lockspace and lease id. self.assertEqual(res["lockspace"], "") self.assertEqual(res["resource"], "") def test_remove_missing(self): with make_volume() as vol: lease_id = make_uuid() with self.assertRaises(xlease.NoSuchLease): vol.remove(lease_id) def test_remove_write_failure(self): record = xlease.Record(make_uuid(), 0, updating=True) with make_volume((42, record)) as base: file = FailingWriter(base.path) with utils.closing(file): vol = xlease.LeasesVolume(file) with utils.closing(vol): with self.assertRaises(WriteError): vol.remove(record.resource) # Must succeed becuase writng to storage failed self.assertIn(record.resource, vol.leases()) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_remove_sanlock_failure(self): with make_volume() as vol: lease_id = make_uuid() vol.add(lease_id) sanlock = xlease.sanlock # Make sanlock fail to remove a resource (currnently removing a # resouce by writing invalid lockspace and resoruce name). sanlock.errors["write_resource"] = sanlock.SanlockException with self.assertRaises(sanlock.SanlockException): vol.remove(lease_id) # We should have an updating lease record lease = vol.leases()[lease_id] self.assertTrue(lease["updating"]) # There lease should still be on storage res = sanlock.read_resource(vol.path, lease["offset"]) self.assertEqual(res["lockspace"], vol.lockspace) self.assertEqual(res["resource"], lease_id) @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_add_first_free_slot(self): with make_volume() as vol: uuids = [make_uuid() for i in range(4)] for uuid in uuids[:3]: vol.add(uuid) vol.remove(uuids[1]) vol.add(uuids[3]) leases = vol.leases() # The first lease in the first slot self.assertEqual(leases[uuids[0]]["offset"], xlease.USER_RESOURCE_BASE) # The forth lease was added in the second slot after the second # lease was removed. self.assertEqual(leases[uuids[3]]["offset"], xlease.USER_RESOURCE_BASE + xlease.SLOT_SIZE) # The third lease in the third slot self.assertEqual(leases[uuids[2]]["offset"], xlease.USER_RESOURCE_BASE + xlease.SLOT_SIZE * 2) @pytest.mark.slow def test_time_lookup(self): setup = """ import os from testlib import make_uuid from vdsm import utils from vdsm.storage import xlease path = "%s" lockspace = os.path.basename(os.path.dirname(path)) lease_id = make_uuid() def bench(): file = xlease.DirectFile(path) with utils.closing(file): vol = xlease.LeasesVolume(file) with utils.closing(vol, log="test"): try: vol.lookup(lease_id) except xlease.NoSuchLease: pass """ with make_volume() as vol: count = 100 elapsed = timeit.timeit("bench()", setup=setup % vol.path, number=count) print("%d lookups in %.6f seconds (%.6f seconds per lookup)" % (count, elapsed, elapsed / count)) @pytest.mark.slow @MonkeyPatch(xlease, "sanlock", FakeSanlock()) def test_time_add(self): setup = """ import os from testlib import make_uuid from vdsm import utils from vdsm.storage import xlease path = "%s" lockspace = os.path.basename(os.path.dirname(path)) def bench(): lease_id = make_uuid() file = xlease.DirectFile(path) with utils.closing(file): vol = xlease.LeasesVolume(file) with utils.closing(vol, log="test"): vol.add(lease_id) """ with make_volume() as vol: count = 100 elapsed = timeit.timeit("bench()", setup=setup % vol.path, number=count) # Note: this does not include the time to create the real sanlock # resource. print("%d adds in %.6f seconds (%.6f seconds per add)" % (count, elapsed, elapsed / count))
def test_inq_lockspace_releasing_no_wait(self): fs = FakeSanlock() fs.add_lockspace("lockspace", 1, "path") fs.rem_lockspace("lockspace", 1, "path", async=True) acquired = fs.inq_lockspace("lockspace", 1, "path") self.assertFalse(acquired, "lockspace not released")