test/assemblers: remove the need to use nbd
Using the network block device (nbd) kernel module to test all the non-raw image formats often caused tests to fail due to nbd not being stable itself (see below). Instead convert non-raw images to the raw format via qemu-img convert and mount those with loop-back devices. All the testing code itself stays the same. Example nbd error messages: kernel: block nbd15: NBD_DISCONNECT kernel: block nbd15: Disconnected due to user request. kernel: print_req_error: 89 callbacks suppressed kernel: blk_update_request: I/O error, dev nbd15, sector 0 op 0x0:(READ) flags 0x0 phys_seg 1 prio class 0 kernel: buffer_io_error: 134 callbacks suppressed kernel: Buffer I/O error on dev nbd15, logical block 0, async page read kernel: blk_update_request: I/O error, dev nbd15, sector 1 op 0x0:(READ) flags 0x0 phys_seg 7 prio class 0
This commit is contained in:
parent
21e0475031
commit
cf03ca0715
1 changed files with 54 additions and 25 deletions
|
|
@ -3,15 +3,15 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import glob
|
import errno
|
||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from osbuild import loop
|
||||||
from .. import test
|
from .. import test
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -20,7 +20,6 @@ class TestAssemblers(test.TestBase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
subprocess.run(["modprobe", "nbd"], check=False)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.osbuild = test.OSBuild(self)
|
self.osbuild = test.OSBuild(self)
|
||||||
|
|
@ -102,6 +101,7 @@ class TestAssemblers(test.TestBase):
|
||||||
|
|
||||||
@unittest.skipUnless(test.TestBase.have_tree_diff(), "tree-diff missing")
|
@unittest.skipUnless(test.TestBase.have_tree_diff(), "tree-diff missing")
|
||||||
def test_qemu(self):
|
def test_qemu(self):
|
||||||
|
loctl = loop.LoopControl()
|
||||||
for fmt in ["raw", "raw.xz", "qcow2", "vmdk", "vdi"]:
|
for fmt in ["raw", "raw.xz", "qcow2", "vmdk", "vdi"]:
|
||||||
with self.subTest(fmt=fmt):
|
with self.subTest(fmt=fmt):
|
||||||
print(f" {fmt}", flush=True)
|
print(f" {fmt}", flush=True)
|
||||||
|
|
@ -120,7 +120,7 @@ class TestAssemblers(test.TestBase):
|
||||||
image = image[:-3]
|
image = image[:-3]
|
||||||
fmt = "raw"
|
fmt = "raw"
|
||||||
self.assertImageFile(image, fmt, options["size"])
|
self.assertImageFile(image, fmt, options["size"])
|
||||||
with nbd_connect(image) as device:
|
with open_image(loctl, image, fmt) as (target, device):
|
||||||
ptable = self.read_partition_table(device)
|
ptable = self.read_partition_table(device)
|
||||||
self.assertPartitionTable(ptable,
|
self.assertPartitionTable(ptable,
|
||||||
"dos",
|
"dos",
|
||||||
|
|
@ -131,7 +131,12 @@ class TestAssemblers(test.TestBase):
|
||||||
"26e3327c6b5ac9b5e21d8b86f19ff7cb4d12fb2d0406713f936997d9d89de3ee",
|
"26e3327c6b5ac9b5e21d8b86f19ff7cb4d12fb2d0406713f936997d9d89de3ee",
|
||||||
"9b31c8fbc59602a38582988bf91c3948ae9c6f2a231ab505ea63a7005e302147",
|
"9b31c8fbc59602a38582988bf91c3948ae9c6f2a231ab505ea63a7005e302147",
|
||||||
1024 * 1024)
|
1024 * 1024)
|
||||||
self.assertFilesystem(device + "p1", options["root_fs_uuid"], "ext4", tree)
|
|
||||||
|
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_tar(self):
|
def test_tar(self):
|
||||||
cases = [
|
cases = [
|
||||||
|
|
@ -150,6 +155,38 @@ class TestAssemblers(test.TestBase):
|
||||||
self.assertIn(mimetype, expected_mimetypes)
|
self.assertIn(mimetype, expected_mimetypes)
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def loop_create_device(ctl, fd, offset=None, sizelimit=None):
|
||||||
|
while True:
|
||||||
|
lo = loop.Loop(ctl.get_unbound())
|
||||||
|
try:
|
||||||
|
lo.set_fd(fd)
|
||||||
|
except OSError as e:
|
||||||
|
lo.close()
|
||||||
|
if e.errno == errno.EBUSY:
|
||||||
|
continue
|
||||||
|
raise e
|
||||||
|
try:
|
||||||
|
lo.set_status(offset=offset, sizelimit=sizelimit, autoclear=True)
|
||||||
|
except BlockingIOError:
|
||||||
|
lo.clear_fd()
|
||||||
|
lo.close()
|
||||||
|
continue
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
yield lo
|
||||||
|
finally:
|
||||||
|
lo.close()
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def loop_open(ctl, image, *, offset=None, size=None):
|
||||||
|
with open(image, "rb") as f:
|
||||||
|
fd = f.fileno()
|
||||||
|
with loop_create_device(ctl, fd, offset=offset, sizelimit=size) as lo:
|
||||||
|
yield os.path.join("/dev", lo.devname)
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def mount(device):
|
def mount(device):
|
||||||
with tempfile.TemporaryDirectory() as mountpoint:
|
with tempfile.TemporaryDirectory() as mountpoint:
|
||||||
|
|
@ -161,24 +198,16 @@ def mount(device):
|
||||||
|
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def nbd_connect(image):
|
def open_image(ctl, image, fmt):
|
||||||
for device in glob.glob("/dev/nbd*"):
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
r = subprocess.run(["qemu-nbd", "--connect", device, "--read-only", image], check=False).returncode
|
if fmt != "raw":
|
||||||
if r == 0:
|
target = os.path.join(tmp, "image.raw")
|
||||||
try:
|
subprocess.run(["qemu-img", "convert", "-O", "raw", image, target],
|
||||||
# qemu-nbd doesn't wait for the device to be ready
|
check=True)
|
||||||
for _ in range(100):
|
else:
|
||||||
if subprocess.run(["nbd-client", "--check", device],
|
target = image
|
||||||
check=False,
|
|
||||||
stdout=subprocess.DEVNULL).returncode == 0:
|
|
||||||
break
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
yield device
|
size = os.stat(target).st_size
|
||||||
finally:
|
|
||||||
# qemu-nbd doesn't wait until the device is released. nbd-client does
|
with loop_open(ctl, target, offset=0, size=size) as dev:
|
||||||
subprocess.run(["qemu-nbd", "--disconnect", device], check=True, stdout=subprocess.DEVNULL)
|
yield target, dev
|
||||||
subprocess.run(["nbd-client", "--disconnect", device], check=False, stdout=subprocess.DEVNULL)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise RuntimeError("no free network block device")
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue