def __init__(self, sock): self.devs = [] self.sock = sock self.ctl = loop.LoopControl() self.event_loop = asyncio.new_event_loop() self.event_loop.add_reader(self.sock, self._dispatch) self.thread = threading.Thread(target=self._run_event_loop)
def test_on_close(tempdir): path = os.path.join(tempdir, "test.img") ctl = loop.LoopControl() assert ctl lo, f = None, None invoked = False def on_close(l): nonlocal invoked invoked = True # check that this is a no-op l.close() try: f = open(path, "wb+") f.truncate(1024) f.flush() lo = ctl.loop_for_fd(f.fileno(), autoclear=True, lock=True) assert lo lo.on_close = on_close lo.close() assert invoked finally: if lo: lo.close() ctl.close()
def test_qemu(self): loctl = loop.LoopControl() with self.osbuild as osb: for fmt in ["raw", "raw.xz", "qcow2", "vmdk", "vdi"]: for fs_type in ["ext4", "xfs", "btrfs"]: with self.subTest(fmt=fmt, fs_type=fs_type): print(f" {fmt} {fs_type}", flush=True) options = { "format": fmt, "filename": f"image.{fmt}", "ptuuid": "b2c09a39-db93-44c5-846a-81e06b1dc162", "root_fs_uuid": "aff010e9-df95-4f81-be6b-e22317251033", "size": 1024 * MEBIBYTE, "root_fs_type": fs_type, } with self.run_assembler(osb, "org.osbuild.qemu", options, f"image.{fmt}") as (tree, image): if fmt == "raw.xz": subprocess.run( ["unxz", "--keep", "--force", image], check=True) image = image[:-3] fmt = "raw" self.assertImageFile(image, fmt, options["size"]) with open_image(loctl, image, fmt) as (target, device): ptable = self.read_partition_table(device) self.assertPartitionTable(ptable, "dos", options["ptuuid"], 1, boot_partition=1) if fs_type == "btrfs": l2hash = "daa74a424a41e2a13c6c4f6bada0e80d84a9865b12d3369470fc5e74004ed329" elif fs_type == "xfs": l2hash = "58ebc5a9b594607f49c290572e027c353a6359da83099020e6f3b9b1f22a897a" else: l2hash = "9b31c8fbc59602a38582988bf91c3948ae9c6f2a231ab505ea63a7005e302147" self.assertGRUB2( device, "26e3327c6b5ac9b5e21d8b86f19ff7cb4d12fb2d0406713f936997d9d89de3ee", l2hash, 1024 * 1024) p1 = ptable["partitions"][0] ssize = ptable.get("sectorsize", 512) start, size = p1["start"] * ssize, p1[ "size"] * ssize with loop_open(loctl, target, offset=start, size=size) as dev: self.assertFilesystem(dev, options["root_fs_uuid"], fs_type, tree)
def test_qemu(self): loctl = loop.LoopControl() with self.osbuild as osb: for fmt in ["raw", "raw.xz", "qcow2", "vmdk", "vdi"]: for fs_type in ["ext4", "xfs", "btrfs"]: with self.subTest(fmt=fmt, fs_type=fs_type): print(f" {fmt} {fs_type}", flush=True) options = { "format": fmt, "filename": f"image.{fmt}", "ptuuid": "b2c09a39-db93-44c5-846a-81e06b1dc162", "root_fs_uuid": "aff010e9-df95-4f81-be6b-e22317251033", "size": 1024 * MEBIBYTE, "root_fs_type": fs_type, } with self.run_assembler(osb, "org.osbuild.qemu", options, f"image.{fmt}") as (tree, image): if fmt == "raw.xz": subprocess.run( ["unxz", "--keep", "--force", image], check=True) image = image[:-3] fmt = "raw" self.assertImageFile(image, fmt, options["size"]) with open_image(loctl, image, fmt) as (target, device): ptable = self.read_partition_table(device) self.assertPartitionTable(ptable, "dos", options["ptuuid"], 1, boot_partition=1) if fs_type == "btrfs": l2hash = "919aad44d37aa9fdbb8cb1bbd8ce2a44e64aee76f4dceb805eaab041b7f62348" elif fs_type == "xfs": l2hash = "1729f531281e4c3cbcde2a39b587c9dd5334ea1335bb860905556d5b73603de6" else: l2hash = "24c3ad6be9a5687d5140e0bf66d25953c4f0c7eeb6aaced4cc64685f5b3cfa9e" self.assertGRUB2( device, "26e3327c6b5ac9b5e21d8b86f19ff7cb4d12fb2d0406713f936997d9d89de3ee", l2hash, 1024 * 1024) p1 = ptable["partitions"][0] ssize = ptable.get("sectorsize", 512) start, size = p1["start"] * ssize, p1[ "size"] * ssize with loop_open(loctl, target, offset=start, size=size) as dev: self.assertFilesystem(dev, options["root_fs_uuid"], fs_type, tree)
def test_qemu(self): loctl = loop.LoopControl() for fmt in ["raw", "raw.xz", "qcow2", "vmdk", "vdi"]: with self.subTest(fmt=fmt): print(f" {fmt}", flush=True) options = { "format": fmt, "filename": f"image.{fmt}", "ptuuid": "b2c09a39-db93-44c5-846a-81e06b1dc162", "root_fs_uuid": "aff010e9-df95-4f81-be6b-e22317251033", "size": 2 * 1024 * 1024 * 1024 } with self.run_assembler("org.osbuild.qemu", options, f"image.{fmt}") as (tree, image): if fmt == "raw.xz": subprocess.run(["unxz", "--keep", "--force", image], check=True) image = image[:-3] fmt = "raw" self.assertImageFile(image, fmt, options["size"]) with open_image(loctl, image, fmt) as (target, device): ptable = self.read_partition_table(device) self.assertPartitionTable(ptable, "dos", options["ptuuid"], 1, boot_partition=1) self.assertGRUB2( device, "26e3327c6b5ac9b5e21d8b86f19ff7cb4d12fb2d0406713f936997d9d89de3ee", "9b31c8fbc59602a38582988bf91c3948ae9c6f2a231ab505ea63a7005e302147", 1024 * 1024) p1 = ptable["partitions"][0] ssize = ptable.get("sectorsize", 512) start, size = p1["start"] * ssize, p1["size"] * ssize with loop_open(loctl, target, offset=start, size=size) as dev: self.assertFilesystem(dev, options["root_fs_uuid"], "ext4", tree)
def test_lock(tempdir): path = os.path.join(tempdir, "test.img") ctl = loop.LoopControl() assert ctl lo, lo2, f = None, None, None try: f = open(path, "wb+") f.truncate(1024) f.flush() lo = ctl.loop_for_fd(f.fileno(), autoclear=True, lock=True) assert lo lo2 = loop.Loop(lo.minor) assert lo2 with pytest.raises(BlockingIOError): lo2.flock(fcntl.LOCK_EX | fcntl.LOCK_NB) lo.close() lo = None # after lo is closed, the lock should be release and # we should be able to obtain the lock lo2.flock(fcntl.LOCK_EX | fcntl.LOCK_NB) lo2.clear_fd() finally: if lo2: lo2.close() if lo: lo.close() if f: f.close() ctl.close()
def test_basic(tempdir): test_data = b"osbuild" path = os.path.join(tempdir, "test.img") ctl = loop.LoopControl() assert ctl with pytest.raises(ValueError): ctl.loop_for_fd(-1) lo, f = None, None try: f = open(path, "wb+") f.truncate(1024) f.flush() lo = ctl.loop_for_fd(f.fileno(), autoclear=True) assert lo.is_bound_to(f.fileno()) sb = os.fstat(f.fileno()) assert lo assert lo.devname info = lo.get_status() assert info.lo_inode == sb.st_ino assert info.lo_number == lo.minor # check for `LoopInfo.is_bound_to` helper assert info.is_bound_to(sb) with TemporaryFile(dir=tempdir) as t: t.write(b"") t.flush() st = os.fstat(t.fileno()) assert not info.is_bound_to(st) # check for autoclear flags setting and helpers assert info.autoclear lo.set_status(autoclear=False) info = lo.get_status() assert not info.autoclear with open(os.path.join("/dev", lo.devname), "wb") as f: f.write(test_data) # the `flush_buf` seems to be necessary when calling # `LoopInfo.clear_fd`, otherwise the data integrity # check later will fail lo.flush_buf() lo.clear_fd() finally: if lo: with contextlib.suppress(OSError): lo.clear_fd() lo.close() if f: f.close() ctl.close() # check for data integrity, i.e. that what we wrote via the # loop device was actually written to the underlying file with open(path, "rb") as f: assert f.read(len(test_data)) == test_data # closing must be a no-op on a closed LoopControl ctl.close() # check we raise exceptions on methods that require # an open LoopControl for fn in (ctl.add, ctl.remove, ctl.get_unbound): with pytest.raises(RuntimeError): fn() with pytest.raises(RuntimeError): ctl.loop_for_fd(0)
def test_clear_fd_wait(tempdir): path = os.path.join(tempdir, "test.img") ctl = loop.LoopControl() assert ctl delay_time = 0.25 def close_loop(lo, barrier): barrier.wait() time.sleep(delay_time) print("closing loop") lo.close() lo, lo2, f = None, None, None try: f = open(path, "wb+") f.truncate(1024) f.flush() lo = ctl.loop_for_fd(f.fileno(), autoclear=False) assert lo # Increase reference count of the loop to > 1 thus # preventing the kernel from immediately closing the # device. Instead the kernel will set the autoclear # attribute and return lo2 = loop.Loop(lo.minor) assert lo2 # as long as the second loop is alive, the kernel can # not clear the fd and thus we will get a timeout with pytest.raises(TimeoutError): lo.clear_fd_wait(f.fileno(), 0.1, 0.01) # start a thread and sync with a barrier, then close # the loop device in the background thread while the # main thread is waiting in `clear_fd_wait`. We wait # four times the delay time of the thread to ensure # we don't get a timeout. barrier = threading.Barrier(2) thread = threading.Thread(target=close_loop, args=(lo2, barrier)) barrier.reset() thread.start() barrier.wait() lo.clear_fd_wait(f.fileno(), 4 * delay_time, delay_time / 10) # no timeout exception has occurred and thus the device # must not be be bound to the original file anymore assert not lo.is_bound_to(f.fileno()) finally: if lo2: lo2.close() if lo: with contextlib.suppress(OSError): lo.clear_fd() lo.close() if f: f.close() ctl.close()
def test_rename_vg_group(tempdir): path = os.path.join(tempdir, "lvm.img") ctl = loop.LoopControl() f = None lo = None try: f = open(path, "wb+") f.truncate(100 * 1024 * 1024) f.flush() lo = make_loop(ctl, f.fileno(), 0, None) devname = os.path.join("/dev", lo.devname) vg_name = str(uuid.uuid4()) pvcreate(devname) vgcreate(devname, vg_name) lvcreate(vg_name, "lv1", r"100%FREE") vgs = list_vgs() vg = find_vg(vgs, vg_name) assert vg finally: if lo: lo.close() if f: f.close() new_name = str(uuid.uuid4()) with lvm2.Disk.open(path) as disk: assert disk.metadata assert disk.metadata.vg_name == vg_name disk.rename_vg(new_name) disk.creation_host = "osbuild" disk.description = "created via lvm2 and osbuild" disk.flush_metadata() f = None lo = None try: f = open(path, "rb") lo = make_loop(ctl, f.fileno(), 0, None) devname = os.path.join("/dev", lo.devname) vg = None for i in range(3): vgs = list_vgs() vg = find_vg(vgs, new_name) if vg: break time.sleep(0.250 * (i+1)) if not vg: raise RuntimeError(f"Could not find vg {new_name}") finally: if lo: lo.close() if f: f.close()