From 94f579b546505b68afb90680d8bf5f79f4b11772 Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Mon, 24 Aug 2026 14:54:56 +0000 Subject: [PATCH 1/2] feat: Add QEMU disk image parameter For running integration tests the binaries and their data have to be made available in the image. Mounting the data via an extra image is the easiest and fastest way. It has lesser system requirements compared to e.g. mounting and modifying a QCOW2 image. --- .bazelrc | 1 + MODULE.bazel | 10 ++- MODULE.bazel.lock | 89 +++++++++++++++++++++++--- README.md | 6 ++ score/itf/plugins/qemu/__init__.py | 19 +++++- score/itf/plugins/qemu/qemu.py | 32 +++++++-- score/itf/plugins/qemu/qemu_process.py | 5 ++ score/itf/plugins/qemu/qemu_target.py | 1 + test/integration/BUILD | 16 +++-- test/integration/test_qemu_disk.py | 48 ++++++++++++++ test/resources/BUILD | 19 ++++++ test/resources/qemu_disk_content.txt | 1 + test/unit/test_qemu.py | 41 +++++++++++- 13 files changed, 264 insertions(+), 24 deletions(-) create mode 100644 test/integration/test_qemu_disk.py create mode 100644 test/resources/qemu_disk_content.txt diff --git a/.bazelrc b/.bazelrc index 6376ac2a..57af29da 100644 --- a/.bazelrc +++ b/.bazelrc @@ -17,6 +17,7 @@ test:qemu-integration --config=x86_64-qnx test:qemu-integration --run_under=//scripts:run_under_qemu test:qemu-integration --build_tests_only +build:qemu-integration-linux --extra_toolchains=@score_qnx_x86_64_ext4_toolchain//:ext4-x86_64-linux test:qemu-integration-linux --run_under=//scripts:run_under_qemu test:qemu-integration-linux --build_tests_only diff --git a/MODULE.bazel b/MODULE.bazel index 67ff2d20..d4158f32 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -208,7 +208,7 @@ use_repo( "score_qcc_x86_64_toolchain_pkg", ) -bazel_dep(name = "score_rules_imagefs", version = "0.0.3", dev_dependency = True) +bazel_dep(name = "score_rules_imagefs", version = "0.1.0", dev_dependency = True) imagefs = use_extension("@score_rules_imagefs//extensions:imagefs.bzl", "imagefs", dev_dependency = True) imagefs.toolchain( @@ -219,7 +219,13 @@ imagefs.toolchain( target_os = "qnx", type = "ifs", ) -use_repo(imagefs, "score_qnx_x86_64_ifs_toolchain") +imagefs.toolchain( + name = "score_qnx_x86_64_ext4_toolchain", + target_cpu = "x86_64", + target_os = "linux", + type = "ext4", +) +use_repo(imagefs, "score_qnx_x86_64_ext4_toolchain", "score_qnx_x86_64_ifs_toolchain") ############################################################################### # diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 0240cd3d..8784e21e 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -73,7 +73,8 @@ "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "407729e232f611c3270005b016b437005daa7b1505826798ea584169a476e878", "https://bcr.bazel.build/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "d6e00979a98ac14ada5e31c8794708b41434d461e7e7ca39b59b765e6d233b18", "https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d", - "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751", + "https://bcr.bazel.build/modules/bazel_lib/3.7.1/MODULE.bazel": "b6fd9b2f8fab956420c11836f416efac4a70e20804ae384ebe62773a4ed70046", + "https://bcr.bazel.build/modules/bazel_lib/3.7.1/source.json": "635fdaa28b50c04febc5e60ef51bc913d3bc87bfbaac7045449273c2341648cb", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", @@ -354,6 +355,7 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0-rc.0/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.0.0/MODULE.bazel": "not found", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_lib/3.7.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.0.3/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.1.1/MODULE.bazel": "not found", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/bazel_skylib/1.2.0/MODULE.bazel": "not found", @@ -515,7 +517,8 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_cpp_toolchains/0.5.1/source.json": "e3bb1beaf9e8e08fe682a616647d17badb1d7f8eeed4d2582277a9d7a1186c62", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.1.1/MODULE.bazel": "236e5bdff6f2d6de6f96cc4f5f4b1bf2cd6137547ce279668a2dd4c54cd4236c", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.1.2/MODULE.bazel": "d1889bf36241521c5d5c401aaf1e98242a60f9cab9d223f8190e5d7087956e83", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/0.1.2/source.json": "549c18a968c1ab76135ad38fd15981fcc04d377a62f20e2e5e0b5faa9de2b01d", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/1.1.0/MODULE.bazel": "e5c656abcad121a89cbabad3550bb20167a6865de219ef6e1c68ca30567e1c32", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_bazel_platforms/1.1.0/source.json": "9797d56e9dd68e9d3e8b674402df2d6d5e2174f84184ca4cbee78fc07dc117d9", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_cr_checker/0.2.2/MODULE.bazel": "dc36d9c35543db918c3fb5b93a8e684431f56c7c784cf2a1b90f35802a373c98", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_cr_checker/0.3.1/MODULE.bazel": "f49e037d7fbc0b2a8b2734fc6b47334e8cc8589ca7a5aa0f3ccca85cc5f79fac", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_dash_license_checker/0.1.1/MODULE.bazel": "76681dbd2d45b5c540869a2337174086c56c54953aab1d02cd878b59d31d13a5", @@ -556,8 +559,8 @@ "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.2/MODULE.bazel": "f25490f64035a0e3a0d53ad9cb6164e8325ce6cf2a7ee68c6ae153840cb2497e", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.4/MODULE.bazel": "53bd16dfbb1fb8ecf6822fb26f9f4e8333bac7b14d12bb02bf84078063820a31", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_python_basics/0.3.4/source.json": "28ed0207a16f8498a84fae4983fa73060805003a4fbd68c6d749e2f1cc14601f", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rules_imagefs/0.0.3/MODULE.bazel": "d87fcd3485438284fa96d6a5a81923d6b6672468947423517cc43c4d7692ca6a", - "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rules_imagefs/0.0.3/source.json": "5f2de07264fbc8f3adecffde5aea326b9f8fccf9e74fd6a057fc9c3556b00cfa", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rules_imagefs/0.1.0/MODULE.bazel": "7964f78b6a92aebc38916dedbfb5f011d4fa4c385d86f7612c39c7a423b012cc", + "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rules_imagefs/0.1.0/source.json": "8aa1214aec6645a9cbbfec86b855402de2f0c42b9aa37224279a64788350a58e", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/MODULE.bazel": "ade2bad4a331b02d9b7e7d9842e8de8c6fded6186486e02c4f7db5cd4b71d34d", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_rust_policies/0.0.2/source.json": "fbcbc738e652b0c68d5d28dd1db09f2e643dc111f5739b2f6af7ec56c2e88043", "https://raw.githubusercontent.com/eclipse-score/bazel_registry/main/modules/score_starpls_lsp/0.1.0/MODULE.bazel": "b2f8c4c8d8e851706255ff9002b448bff6e040b8f0c6adedbde2a09375aa16cc", @@ -2577,20 +2580,80 @@ }, "@@score_rules_imagefs+//extensions:imagefs.bzl%imagefs": { "general": { - "bzlTransitiveDigest": "6ZLANe53LXwcnne1ZrZm9GkJ9H5VdsCLSgSNHyUrqA4=", - "usagesDigest": "v6LxuRqCAQvitgq4q5lQseaSUnV05WHiDU49y4X6Ohc=", + "bzlTransitiveDigest": "AhldzzceHQweCskTyocFKGDFKZfW2t5IdpfsFJV4S9g=", + "usagesDigest": "BCZ++xZkfqLELCnyXPxhCQyptuLgGhNRgOzGykcI8hs=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, "generatedRepoSpecs": { "score_qnx_x86_64_ifs_toolchain": { - "repoRuleId": "@@score_rules_imagefs+//rules/qnx:imagefs_toolchain.bzl%imagefs_toolchain", + "repoRuleId": "@@score_rules_imagefs+//rules:imagefs_toolchain.bzl%imagefs_toolchain", "attributes": { "tc_cpu": "x86_64", "tc_os": "qnx", "tc_pkg_repo": "'@@score_bazel_cpp_toolchains++gcc+score_qcc_x86_64_toolchain_pkg'", "sdp_version": "8.0.0", - "tc_type": "ifs" + "tc_type": "ifs", + "coreutils_pkg_repo": "" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_darwin_amd64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "darwin_amd64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_darwin_arm64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "darwin_arm64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_linux_amd64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "linux_amd64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_linux_arm64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "linux_arm64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_windows_amd64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "windows_amd64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_windows_arm64": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", + "attributes": { + "platform": "windows_arm64", + "version": "0.5.0" + } + }, + "score_qnx_x86_64_ext4_toolchain_coreutils_toolchains": { + "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_toolchains_repo", + "attributes": { + "user_repository_name": "score_qnx_x86_64_ext4_toolchain_coreutils" + } + }, + "score_qnx_x86_64_ext4_toolchain": { + "repoRuleId": "@@score_rules_imagefs+//rules:imagefs_toolchain.bzl%imagefs_toolchain", + "attributes": { + "tc_cpu": "x86_64", + "tc_os": "linux", + "tc_pkg_repo": "", + "sdp_version": "8.0.0", + "tc_type": "ext4", + "coreutils_pkg_repo": "@score_qnx_x86_64_ext4_toolchain_coreutils_linux_amd64" } } }, @@ -2600,6 +2663,16 @@ "score_qcc_x86_64_toolchain_pkg", "score_bazel_cpp_toolchains++gcc+score_qcc_x86_64_toolchain_pkg" ], + [ + "bazel_lib+", + "bazel_tools", + "bazel_tools" + ], + [ + "score_rules_imagefs+", + "bazel_lib", + "bazel_lib+" + ], [ "score_rules_imagefs+", "bazel_tools", diff --git a/README.md b/README.md index 12eb6760..e5fd7c9f 100644 --- a/README.md +++ b/README.md @@ -285,10 +285,13 @@ py_itf_test( args = [ "--qemu-rootfs=$(location @os_images//ubuntu_x86_64:image)", "--qemu-config=$(location @os_images//ubuntu_x86_64:qemu_config)", + # optional disk image with test data + "--qemu-disk=$(location @os_images//ubuntu_x86_64:qemu_disk_image)", ], data = [ "@os_images//ubuntu_x86_64:image", "@os_images//ubuntu_x86_64:qemu_config", + "@os_images//ubuntu_x86_64:qemu_disk_image", ], plugins = ["@score_itf//score/itf/plugins:qemu_plugin"], ) @@ -304,11 +307,14 @@ py_itf_test( "--qemu-rootfs=$(location @os_images//ebclfsa_aarch64:image)", "--qemu-kernel=$(location @os_images//ebclfsa_aarch64:kernel)", "--qemu-config=$(location @os_images//ebclfsa_aarch64:qemu_config)", + # optional disk image with test data + "--qemu-disk=$(location @os_images//ebclfsa_aarch64:qemu_disk_image)", ], data = [ "@os_images//ebclfsa_aarch64:image", "@os_images//ebclfsa_aarch64:kernel", "@os_images//ebclfsa_aarch64:qemu_config", + "@os_images//ebclfsa_aarch64:qemu_disk_image", ], plugins = ["@score_itf//score/itf/plugins:qemu_plugin"], ) diff --git a/score/itf/plugins/qemu/__init__.py b/score/itf/plugins/qemu/__init__.py index 071b2a14..47696f6a 100644 --- a/score/itf/plugins/qemu/__init__.py +++ b/score/itf/plugins/qemu/__init__.py @@ -80,6 +80,13 @@ def pytest_addoption(parser): help="Path to a QEMU disk image (qcow2, wic, or img). " "An ephemeral overlay is created so the original image is not modified.", ) + parser.addoption( + "--qemu-disk", + action="store", + default=None, + help="Path to an additional disk image to attach to the target as a second block device. " + "A qcow2 overlay is created so the original image is not modified.", + ) @pytest.fixture(scope="session") @@ -96,6 +103,7 @@ def config(request): qemu_kernel = request.config.getoption("qemu_kernel") qemu_image = request.config.getoption("qemu_image") rootfs = request.config.getoption("qemu_rootfs") + disk = request.config.getoption("qemu_disk") if qemu_image: logger.warning( @@ -108,6 +116,7 @@ def config(request): qemu_config=load_configuration(qemu_config), qemu_kernel=qemu_kernel, qemu_rootfs=rootfs, + qemu_disk=disk, ) @@ -115,14 +124,18 @@ def config(request): def target_init(config, request, dlt): logger.info(f"Starting tests on host: {socket.gethostname()}") overlay_path = None - if config.qemu_rootfs: - overlay_path = _create_overlay(os.path.abspath(config.qemu_rootfs)) + disk_overlay_path = None try: + if config.qemu_rootfs: + overlay_path = _create_overlay(os.path.abspath(config.qemu_rootfs)) + if config.qemu_disk: + disk_overlay_path = _create_overlay(os.path.abspath(config.qemu_disk)) with qemu_target( Bunch( qemu_config=config.qemu_config, qemu_kernel=config.qemu_kernel, qemu_rootfs=overlay_path, + qemu_disk=disk_overlay_path, ) ) as qemu: pre_tests_phase(qemu) @@ -130,3 +143,5 @@ def target_init(config, request, dlt): finally: if overlay_path and os.path.exists(overlay_path): os.unlink(overlay_path) + if disk_overlay_path and os.path.exists(disk_overlay_path): + os.unlink(disk_overlay_path) diff --git a/score/itf/plugins/qemu/qemu.py b/score/itf/plugins/qemu/qemu.py index 4f06905c..49cbc75b 100644 --- a/score/itf/plugins/qemu/qemu.py +++ b/score/itf/plugins/qemu/qemu.py @@ -25,6 +25,9 @@ "network_device": "virtio-net-pci", "machine": "pc", "block_device": "virtio-blk-pci", + # virtio-blk-pci is probed by the guest in the same order the devices are + # specified on the command line. + "block_device_order": "ascending", }, "virt-aarch64": { "architecture": "aarch64", @@ -32,6 +35,9 @@ "network_device": "virtio-net-device", "machine": "virt,virtualization=true,gic-version=3", "block_device": "virtio-blk-device", + # virtio-blk-device (virtio-mmio) is probed by the guest in the reverse order + # the devices are specified on the command line. + "block_device_order": "descending", }, } @@ -55,6 +61,7 @@ def __init__( port_forwarding, rootfs, kernel_cmdline, + disk, ): """Create a QEMU instance with the specified parameters. @@ -67,6 +74,7 @@ def __init__( :param list port_forwarding: List of port forwarding configurations. :param str rootfs: Optional path to a qcow2 disk image. :param str kernel_cmdline: Optional kernel command line string. + :param str disk: Optional path to an additional qcow2 disk image. """ if machine not in _SUPPORTED_MACHINES: raise ValueError("machine must be one of: " + ", ".join(sorted(_SUPPORTED_MACHINES))) @@ -78,6 +86,7 @@ def __init__( self.__port_forwarding = port_forwarding self.__rootfs = rootfs self.__kernel_cmdline = kernel_cmdline + self.__disk = disk self.__check_qemu_is_installed() @@ -147,10 +156,10 @@ def __build_qemu_command(self): + self.__network_devices_args() + self.__port_forwarding_args() + self.__kernel_args() - + self.__rootfs_args() + + self.__disks_args() ) - def __kernel_args(self): + def __kernel_args(self) -> list[str]: if not self.__path_to_kernel_image: return [] args = ["-kernel", self.__path_to_kernel_image] @@ -158,14 +167,25 @@ def __kernel_args(self): args.extend(["-append", self.__kernel_cmdline]) return args - def __rootfs_args(self): - if not self.__rootfs: + def __disks_args(self) -> list[str]: + # Order the disks so that, regardless of the guest's probing order, the rootfs + # always ends up as the first block device (/dev/vda) in the guest. + disks = [self.__rootfs, self.__disk] + if self.__arch_config["block_device_order"] == "descending": + disks = list(reversed(disks)) + args = [] + for id, disk in enumerate(disks): + args += self.__disk_args(disk, id) + return args + + def __disk_args(self, disk: str, id: int) -> list[str]: + if not disk: return [] return [ "-device", - f"{self.__arch_config['block_device']},drive=vd0", + f"{self.__arch_config['block_device']},drive=vd{id}", "-drive", - f"if=none,format=qcow2,file={self.__rootfs},id=vd0", + f"if=none,format=qcow2,file={disk},id=vd{id}", ] def __network_devices_args(self): diff --git a/score/itf/plugins/qemu/qemu_process.py b/score/itf/plugins/qemu/qemu_process.py index 7f1221a0..765e0379 100644 --- a/score/itf/plugins/qemu/qemu_process.py +++ b/score/itf/plugins/qemu/qemu_process.py @@ -30,6 +30,7 @@ def __init__( machine, rootfs, kernel_cmdline, + disk, ): self._path_to_qemu_kernel_image = path_to_qemu_kernel_image self._available_ram = available_ram @@ -39,6 +40,7 @@ def __init__( self._machine = machine self._rootfs = rootfs self._kernel_cmdline = kernel_cmdline + self._disk = disk self._qemu = Qemu( self._path_to_qemu_kernel_image, self._available_ram, @@ -48,6 +50,7 @@ def __init__( machine=self._machine, rootfs=self._rootfs, kernel_cmdline=self._kernel_cmdline, + disk=self._disk, ) self._console = None @@ -65,6 +68,8 @@ def start(self): logger.info(f"Using QEMU kernel command line: {self._kernel_cmdline}") if self._rootfs is not None: logger.info(f"Using QEMU root filesystem image: {self._rootfs}") + if self._disk is not None: + logger.info(f"Using QEMU additional disk image: {self._disk}") subprocess_params = { "stdin": subprocess.PIPE, "stdout": subprocess.PIPE, diff --git a/score/itf/plugins/qemu/qemu_target.py b/score/itf/plugins/qemu/qemu_target.py index f5feb55d..43d845d2 100644 --- a/score/itf/plugins/qemu/qemu_target.py +++ b/score/itf/plugins/qemu/qemu_target.py @@ -293,6 +293,7 @@ def qemu_target(test_config): machine=test_config.qemu_config.qemu_machine, rootfs=test_config.qemu_rootfs, kernel_cmdline=test_config.qemu_config.qemu_kernel_cmdline, + disk=test_config.qemu_disk, ) else: process_ctx = nullcontext() diff --git a/test/integration/BUILD b/test/integration/BUILD index 66a190ec..4c9aa2dd 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -211,16 +211,19 @@ py_itf_test( ) py_itf_test( - name = "test_ebclfsa_ping", + name = "test_ebclfsa", srcs = [ "test_ping.py", + "test_qemu_disk.py", ], args = [ "--qemu-config=$(location @os_images//ebclfsa_aarch64:qemu_config)", "--qemu-kernel=$(location @os_images//ebclfsa_aarch64:kernel)", "--qemu-rootfs=$(location @os_images//ebclfsa_aarch64:image)", + "--qemu-disk=$(location //test/resources:qemu_disk_image)", ], data = [ + "//test/resources:qemu_disk_image", "@os_images//ebclfsa_aarch64:image", "@os_images//ebclfsa_aarch64:kernel", "@os_images//ebclfsa_aarch64:qemu_config", @@ -231,18 +234,22 @@ py_itf_test( tags = [ "manual", ], + target_compatible_with = ["@platforms//os:linux"], ) py_itf_test( - name = "test_ubuntu_ping", + name = "test_ubuntu", srcs = [ "test_ping.py", + "test_qemu_disk.py", ], args = [ "--qemu-config=$(location @os_images//ubuntu_x86_64:qemu_config)", "--qemu-rootfs=$(location @os_images//ubuntu_x86_64:image)", + "--qemu-disk=$(location //test/resources:qemu_disk_image)", ], data = [ + "//test/resources:qemu_disk_image", "@os_images//ubuntu_x86_64:image", "@os_images//ubuntu_x86_64:qemu_config", ], @@ -252,6 +259,7 @@ py_itf_test( tags = [ "manual", ], + target_compatible_with = ["@platforms//os:linux"], ) test_suite( @@ -260,8 +268,8 @@ test_suite( "manual", ], tests = [ - ":test_ebclfsa_ping", - ":test_ubuntu_ping", + ":test_ebclfsa", + ":test_ubuntu", ], ) diff --git a/test/integration/test_qemu_disk.py b/test/integration/test_qemu_disk.py new file mode 100644 index 00000000..dc7ed989 --- /dev/null +++ b/test/integration/test_qemu_disk.py @@ -0,0 +1,48 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* +"""Verifies that a second disk, passed to the QEMU plugin via ``--qemu-disk``, +shows up in the guest, can be mounted and contains the expected content. +""" + +EXPECTED_DISK_CONTENT = "Hello from the QEMU disk image!\n" + +# The additional disk is attached as the second virtio block device. The +# rootfs occupies the first one, so the disk always shows up as /dev/vdb. +DISK_DEVICE = "/dev/vdb" +MOUNT_POINT = "/mnt/qemu_disk" + + +def test_disk_device_is_visible(target): + exit_code, _ = target.execute(f"test -b {DISK_DEVICE}") + assert exit_code == 0, f"Expected block device {DISK_DEVICE} to be present" + + +def test_disk_can_be_mounted_and_has_expected_content(target): + exit_code, _ = target.execute(f"mkdir -p {MOUNT_POINT} && mount {DISK_DEVICE} {MOUNT_POINT}") + assert exit_code == 0, "Mounting the additional disk failed" + try: + exit_code, output = target.execute(f"cat {MOUNT_POINT}/qemu_disk_content.txt") + assert exit_code == 0 + assert output.decode("utf-8") == EXPECTED_DISK_CONTENT + finally: + target.execute(f"umount {MOUNT_POINT}") + + +def test_disk_is_writable(target): + exit_code, _ = target.execute(f"mkdir -p {MOUNT_POINT} && mount {DISK_DEVICE} {MOUNT_POINT}") + assert exit_code == 0, "Mounting the additional disk failed" + try: + exit_code, _ = target.execute(f"touch {MOUNT_POINT}/should_be_writable") + assert exit_code == 0, "Writing to the disk should work" + finally: + target.execute(f"umount {MOUNT_POINT}") diff --git a/test/resources/BUILD b/test/resources/BUILD index f5700827..80942648 100644 --- a/test/resources/BUILD +++ b/test/resources/BUILD @@ -11,7 +11,9 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load") +load("@rules_pkg//pkg:mappings.bzl", "pkg_files") load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("@score_rules_imagefs//rules/linux:ext4.bzl", "ext4") filegroup( name = "dlt_config", @@ -99,3 +101,20 @@ oci_load( #"@score_itf_examples//:__subpackages__", ], ) + +pkg_files( + name = "qemu_disk_files", + srcs = ["qemu_disk_content.txt"], + prefix = "", +) + +ext4( + name = "qemu_disk_image", + srcs = [":qemu_disk_files"], + out = "qemu_disk.ext4", + tags = ["manual"], + target_compatible_with = ["@platforms//os:linux"], + visibility = [ + "//test:__subpackages__", + ], +) diff --git a/test/resources/qemu_disk_content.txt b/test/resources/qemu_disk_content.txt new file mode 100644 index 00000000..04606245 --- /dev/null +++ b/test/resources/qemu_disk_content.txt @@ -0,0 +1 @@ +Hello from the QEMU disk image! diff --git a/test/unit/test_qemu.py b/test/unit/test_qemu.py index 9610a832..9cc7747e 100644 --- a/test/unit/test_qemu.py +++ b/test/unit/test_qemu.py @@ -25,6 +25,7 @@ def _build_qemu( path_to_kernel_image=None, kernel_cmdline=None, rootfs=None, + disk=None, network_adapters=None, port_forwarding=None, ): @@ -44,6 +45,7 @@ def _build_qemu( port_forwarding=port_forwarding, rootfs=rootfs, kernel_cmdline=kernel_cmdline, + disk=disk, ) @@ -59,6 +61,7 @@ def test_invalid_machine_is_rejected(mocker): port_forwarding=[], rootfs=None, kernel_cmdline=None, + disk=None, ) @@ -99,7 +102,7 @@ def test_kernel_args_are_empty_without_kernel_image(mocker): def test_rootfs_args_include_arch_specific_block_device(mocker): qemu = _build_qemu(mocker, machine="virt-aarch64", rootfs="/tmp/rootfs.qcow2") - assert qemu._Qemu__rootfs_args() == [ + assert qemu._Qemu__disk_args("/tmp/rootfs.qcow2", 0) == [ "-device", "virtio-blk-device,drive=vd0", "-drive", @@ -110,7 +113,41 @@ def test_rootfs_args_include_arch_specific_block_device(mocker): def test_rootfs_args_are_empty_without_rootfs(mocker): qemu = _build_qemu(mocker) - assert qemu._Qemu__rootfs_args() == [] + assert qemu._Qemu__disk_args(None, 0) == [] + + +def test_disk_args_include_arch_specific_block_device(mocker): + qemu = _build_qemu(mocker, machine="virt-aarch64", disk="/tmp/disk.qcow2") + + assert qemu._Qemu__disk_args("/tmp/disk.qcow2", 1) == [ + "-device", + "virtio-blk-device,drive=vd1", + "-drive", + "if=none,format=qcow2,file=/tmp/disk.qcow2,id=vd1", + ] + + +def test_disk_args_are_empty_without_disk(mocker): + qemu = _build_qemu(mocker) + + assert qemu._Qemu__disk_args(None, 0) == [] + + +def test_rootfs_and_disk_can_be_combined(mocker): + qemu = _build_qemu(mocker, rootfs="/tmp/rootfs.qcow2", disk="/tmp/disk.qcow2") + + assert qemu._Qemu__disk_args("/tmp/rootfs.qcow2", 0) == [ + "-device", + "virtio-blk-pci,drive=vd0", + "-drive", + "if=none,format=qcow2,file=/tmp/rootfs.qcow2,id=vd0", + ] + assert qemu._Qemu__disk_args("/tmp/disk.qcow2", 1) == [ + "-device", + "virtio-blk-pci,drive=vd1", + "-drive", + "if=none,format=qcow2,file=/tmp/disk.qcow2,id=vd1", + ] def test_network_args_skip_loopback_and_use_machine_specific_device(mocker): From 62c33b6a4720031e63f90c0a1889d53f4dff2c7b Mon Sep 17 00:00:00 2001 From: Lutz Reinhardt Date: Fri, 4 Sep 2026 09:47:59 +0000 Subject: [PATCH 2/2] rename ext4 toolchain --- .bazelrc | 2 +- MODULE.bazel | 4 ++-- MODULE.bazel.lock | 22 +++++++++++----------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.bazelrc b/.bazelrc index 57af29da..8456ee2a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -17,7 +17,7 @@ test:qemu-integration --config=x86_64-qnx test:qemu-integration --run_under=//scripts:run_under_qemu test:qemu-integration --build_tests_only -build:qemu-integration-linux --extra_toolchains=@score_qnx_x86_64_ext4_toolchain//:ext4-x86_64-linux +build:qemu-integration-linux --extra_toolchains=@score_linux_x86_64_ext4_toolchain//:ext4-x86_64-linux test:qemu-integration-linux --run_under=//scripts:run_under_qemu test:qemu-integration-linux --build_tests_only diff --git a/MODULE.bazel b/MODULE.bazel index d4158f32..5991833c 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -220,12 +220,12 @@ imagefs.toolchain( type = "ifs", ) imagefs.toolchain( - name = "score_qnx_x86_64_ext4_toolchain", + name = "score_linux_x86_64_ext4_toolchain", target_cpu = "x86_64", target_os = "linux", type = "ext4", ) -use_repo(imagefs, "score_qnx_x86_64_ext4_toolchain", "score_qnx_x86_64_ifs_toolchain") +use_repo(imagefs, "score_linux_x86_64_ext4_toolchain", "score_qnx_x86_64_ifs_toolchain") ############################################################################### # diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 8784e21e..76100ee0 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -2581,7 +2581,7 @@ "@@score_rules_imagefs+//extensions:imagefs.bzl%imagefs": { "general": { "bzlTransitiveDigest": "AhldzzceHQweCskTyocFKGDFKZfW2t5IdpfsFJV4S9g=", - "usagesDigest": "BCZ++xZkfqLELCnyXPxhCQyptuLgGhNRgOzGykcI8hs=", + "usagesDigest": "rvePbK74MbNcQLgmiJ6PcIOcLxli5RcnYZlpoE7btnE=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -2597,55 +2597,55 @@ "coreutils_pkg_repo": "" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_darwin_amd64": { + "score_linux_x86_64_ext4_toolchain_coreutils_darwin_amd64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "darwin_amd64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_darwin_arm64": { + "score_linux_x86_64_ext4_toolchain_coreutils_darwin_arm64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "darwin_arm64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_linux_amd64": { + "score_linux_x86_64_ext4_toolchain_coreutils_linux_amd64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "linux_amd64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_linux_arm64": { + "score_linux_x86_64_ext4_toolchain_coreutils_linux_arm64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "linux_arm64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_windows_amd64": { + "score_linux_x86_64_ext4_toolchain_coreutils_windows_amd64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "windows_amd64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_windows_arm64": { + "score_linux_x86_64_ext4_toolchain_coreutils_windows_arm64": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_platform_repo", "attributes": { "platform": "windows_arm64", "version": "0.5.0" } }, - "score_qnx_x86_64_ext4_toolchain_coreutils_toolchains": { + "score_linux_x86_64_ext4_toolchain_coreutils_toolchains": { "repoRuleId": "@@bazel_lib+//lib/private:coreutils_toolchain.bzl%coreutils_toolchains_repo", "attributes": { - "user_repository_name": "score_qnx_x86_64_ext4_toolchain_coreutils" + "user_repository_name": "score_linux_x86_64_ext4_toolchain_coreutils" } }, - "score_qnx_x86_64_ext4_toolchain": { + "score_linux_x86_64_ext4_toolchain": { "repoRuleId": "@@score_rules_imagefs+//rules:imagefs_toolchain.bzl%imagefs_toolchain", "attributes": { "tc_cpu": "x86_64", @@ -2653,7 +2653,7 @@ "tc_pkg_repo": "", "sdp_version": "8.0.0", "tc_type": "ext4", - "coreutils_pkg_repo": "@score_qnx_x86_64_ext4_toolchain_coreutils_linux_amd64" + "coreutils_pkg_repo": "@score_linux_x86_64_ext4_toolchain_coreutils_linux_amd64" } } },