From ff0987b4e17f472dfc04dab52ce7020cafef207e Mon Sep 17 00:00:00 2001 From: Rushikesh Jadhav Date: Mon, 7 Jul 2025 16:09:23 +0530 Subject: [PATCH 1/5] tests/storage/ext: Updated ext to handle both vhd and qcow2 vdi image format Signed-off-by: Rushikesh Jadhav --- tests/storage/ext/conftest.py | 9 +++++++-- tests/storage/ext/test_ext_sr.py | 21 ++++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/storage/ext/conftest.py b/tests/storage/ext/conftest.py index d0fb452a9..64af17d69 100644 --- a/tests/storage/ext/conftest.py +++ b/tests/storage/ext/conftest.py @@ -11,10 +11,15 @@ from lib.sr import SR @pytest.fixture(scope='package') -def ext_sr(host: Host, unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> Generator[SR]: +def ext_sr(host: Host, + unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str + ) -> Generator[SR]: """ An EXT SR on first host. """ sr_disk = unused_512B_disks[host][0]["name"] - sr = host.sr_create('ext', "EXT-local-SR-test", {'device': '/dev/' + sr_disk}) + sr = host.sr_create('ext', "EXT-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}) yield sr # teardown sr.destroy() diff --git a/tests/storage/ext/test_ext_sr.py b/tests/storage/ext/test_ext_sr.py index 572102b66..319cf6a94 100644 --- a/tests/storage/ext/test_ext_sr.py +++ b/tests/storage/ext/test_ext_sr.py @@ -28,17 +28,32 @@ class TestEXTSRCreateDestroy: def test_create_sr_with_missing_device(self, host): try_to_create_sr_with_missing_device('ext', 'EXT-local-SR-test', host) - def test_create_and_destroy_sr(self, host: Host, unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> None: + def test_create_and_destroy_sr(self, host: Host, + unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str + ) -> None: # Create and destroy tested in the same test to leave the host as unchanged as possible sr_disk = unused_512B_disks[host][0]["name"] - sr = host.sr_create('ext', "EXT-local-SR-test", {'device': '/dev/' + sr_disk}, verify=True) + sr = host.sr_create('ext', "EXT-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}, verify=True) # import a VM in order to detect vm import issues here rather than in the vm_on_xfs_fixture used in # the next tests, because errors in fixtures break teardown vm = host.import_vm(vm_image('mini-linux-x86_64-bios'), sr_uuid=sr.uuid) vm.destroy(verify=True) sr.destroy(verify=True) -@pytest.mark.usefixtures("ext_sr") +# We want to skip class tests for qcow2 if the SM does not support qcow2 +@pytest.fixture(scope="class") +def for_qcow2_vdi_image_format(vdi_on_ext_sr: VDI, image_format: str): + # only check qcow2-specific behavior + if image_format != "qcow2": + return + # feature-detect: if the SM doesn't report image-format, skip this check + if not vdi_on_ext_sr.get_image_format(): + pytest.skip("SM does not report sm-config:image-format; skipping qcow2 format check") + +@pytest.mark.usefixtures("for_qcow2_vdi_image_format", "ext_sr") class TestEXTSR: @pytest.mark.quicktest def test_quicktest(self, ext_sr): From 9e85115306fc63d9c2623390312d55a5873ad3bb Mon Sep 17 00:00:00 2001 From: Rushikesh Jadhav Date: Mon, 7 Jul 2025 18:23:00 +0530 Subject: [PATCH 2/5] tests/storage/lvm: Updated lvm to handle both vhd and qcow2 vdi image format Signed-off-by: Rushikesh Jadhav --- tests/storage/lvm/conftest.py | 9 +++++++-- tests/storage/lvm/test_lvm_sr.py | 22 +++++++++++++++++++--- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/storage/lvm/conftest.py b/tests/storage/lvm/conftest.py index fb6845f88..d26a6f213 100644 --- a/tests/storage/lvm/conftest.py +++ b/tests/storage/lvm/conftest.py @@ -11,10 +11,15 @@ from lib.sr import SR @pytest.fixture(scope='package') -def lvm_sr(host: Host, unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> Generator[SR]: +def lvm_sr(host: Host, + unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str + ) -> Generator[SR]: """ An LVM SR on first host. """ sr_disk = unused_512B_disks[host][0]["name"] - sr = host.sr_create('lvm', "LVM-local-SR-test", {'device': '/dev/' + sr_disk}) + sr = host.sr_create('lvm', "LVM-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}) yield sr # teardown sr.destroy() diff --git a/tests/storage/lvm/test_lvm_sr.py b/tests/storage/lvm/test_lvm_sr.py index 63aa79c88..37a52ac79 100644 --- a/tests/storage/lvm/test_lvm_sr.py +++ b/tests/storage/lvm/test_lvm_sr.py @@ -28,17 +28,33 @@ class TestLVMSRCreateDestroy: def test_create_sr_with_missing_device(self, host): try_to_create_sr_with_missing_device('lvm', 'LVM-local-SR-test', host) - def test_create_and_destroy_sr(self, host: Host, unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> None: + def test_create_and_destroy_sr(self, host: Host, + unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str + ) -> None: sr_disk = unused_512B_disks[host][0]["name"] # Create and destroy tested in the same test to leave the host as unchanged as possible - sr = host.sr_create('lvm', "LVM-local-SR-test", {'device': '/dev/' + sr_disk}, verify=True) + sr = host.sr_create('lvm', "LVM-local-SR-test", { + 'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format + }, verify=True) # import a VM in order to detect vm import issues here rather than in the vm_on_xfs_fixture used in # the next tests, because errors in fixtures break teardown vm = host.import_vm(vm_image('mini-linux-x86_64-bios'), sr_uuid=sr.uuid) vm.destroy(verify=True) sr.destroy(verify=True) -@pytest.mark.usefixtures("lvm_sr") +# We want to skip class tests for qcow2 if the SM does not support qcow2 +@pytest.fixture(scope="class") +def for_qcow2_vdi_image_format(vdi_on_lvm_sr: VDI, image_format: str): + # only check qcow2-specific behavior + if image_format != "qcow2": + return + # feature-detect: if the SM doesn't report image-format, skip this check + if not vdi_on_lvm_sr.get_image_format(): + pytest.skip("SM does not report sm-config:image-format; skipping qcow2 format check") + +@pytest.mark.usefixtures("for_qcow2_vdi_image_format", "lvm_sr") class TestLVMSR: @pytest.mark.quicktest def test_quicktest(self, lvm_sr): From 3840f13b71e7f08b7af0dbb83cc1a9245971cd30 Mon Sep 17 00:00:00 2001 From: Rushikesh Jadhav Date: Mon, 7 Jul 2025 21:11:07 +0530 Subject: [PATCH 3/5] tests/storage/xfs: Updated xfs to handle both vhd and qcow2 vdi image format Signed-off-by: Rushikesh Jadhav --- tests/storage/xfs/conftest.py | 10 +++++++++- tests/storage/xfs/test_xfs_sr.py | 21 ++++++++++++++++++--- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tests/storage/xfs/conftest.py b/tests/storage/xfs/conftest.py index 205447926..6b6e57206 100644 --- a/tests/storage/xfs/conftest.py +++ b/tests/storage/xfs/conftest.py @@ -17,6 +17,11 @@ class XfsConfig: uninstall_xfs: bool = True +# NOTE: @pytest.mark.usefixtures does not parametrize this fixture. +# To recreate host_with_xfsprogs for each image_format value, accept +# image_format in the fixture arguments. +# ref https://docs.pytest.org/en/7.1.x/how-to/fixtures.html#use-fixtures-in-classes-and-modules-with-usefixtures + @pytest.fixture(scope='package') def _xfs_config() -> XfsConfig: return XfsConfig() @@ -37,10 +42,13 @@ def xfs_sr( unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], host_with_xfsprogs: Host, _xfs_config: XfsConfig, + image_format: str ) -> Generator[SR]: """ A XFS SR on first host. """ sr_disk = unused_512B_disks[host_with_xfsprogs][0]["name"] - sr = host_with_xfsprogs.sr_create('xfs', "XFS-local-SR-test", {'device': '/dev/' + sr_disk}) + sr = host_with_xfsprogs.sr_create('xfs', "XFS-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}) yield sr # teardown try: diff --git a/tests/storage/xfs/test_xfs_sr.py b/tests/storage/xfs/test_xfs_sr.py index 2d567bee7..da3a7c7a3 100644 --- a/tests/storage/xfs/test_xfs_sr.py +++ b/tests/storage/xfs/test_xfs_sr.py @@ -13,6 +13,7 @@ if TYPE_CHECKING: from lib.host import Host + from lib.vdi import VDI # Requirements: # - one XCP-ng host >= 8.2 with an additional unused disk for the SR @@ -27,7 +28,8 @@ class TestXFSSRCreateDestroy: def test_create_xfs_sr_without_xfsprogs(self, host: Host, - unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]] + unused_512B_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str ) -> None: # This test must be the first in the series in this module assert not host.file_exists('/usr/sbin/mkfs.xfs'), \ @@ -35,7 +37,10 @@ def test_create_xfs_sr_without_xfsprogs(self, sr_disk = unused_512B_disks[host][0]["name"] sr = None try: - sr = host.sr_create('xfs', "XFS-local-SR-test", {'device': '/dev/' + sr_disk}) + sr = host.sr_create('xfs', "XFS-local-SR-test", { + 'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format + }) except Exception: logging.info("SR creation failed, as expected.") if sr is not None: @@ -56,7 +61,17 @@ def test_create_and_destroy_sr(self, vm.destroy(verify=True) sr.destroy(verify=True) -@pytest.mark.usefixtures("xfs_sr") +# We want to skip class tests for qcow2 if the SM does not support qcow2 +@pytest.fixture(scope="class") +def for_qcow2_vdi_image_format(vdi_on_xfs_sr: VDI, image_format: str): + # only check qcow2-specific behavior + if image_format != "qcow2": + return + # feature-detect: if the SM doesn't report image-format, skip this check + if not vdi_on_xfs_sr.get_image_format(): + pytest.skip("SM does not report sm-config:image-format; skipping qcow2 format check") + +@pytest.mark.usefixtures("for_qcow2_vdi_image_format", "xfs_sr") class TestXFSSR: @pytest.mark.quicktest def test_quicktest(self, xfs_sr): From e2718de479a05081493783ef53473a68dc882de0 Mon Sep 17 00:00:00 2001 From: Rushikesh Jadhav Date: Tue, 8 Jul 2025 01:35:15 +0530 Subject: [PATCH 4/5] tests/storage/zfs: Updated zfs to handle both vhd and qcow2 vdi image format Signed-off-by: Rushikesh Jadhav --- tests/storage/zfs/conftest.py | 24 +++++++++++++++++++--- tests/storage/zfs/test_zfs_sr.py | 34 +++++++++++++++++++++++++++----- 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/tests/storage/zfs/conftest.py b/tests/storage/zfs/conftest.py index 2cd61925b..62a9334e0 100644 --- a/tests/storage/zfs/conftest.py +++ b/tests/storage/zfs/conftest.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest import logging @@ -5,6 +7,12 @@ # Explicitly import package-scoped fixtures (see explanation in pkgfixtures.py) from pkgfixtures import host_with_saved_yum_state, sr_disk_wiped +from typing import TYPE_CHECKING, Generator + +if TYPE_CHECKING: + from lib.host import Host + from lib.sr import SR + POOL_NAME = 'pool0' POOL_PATH = '/' + POOL_NAME @@ -13,8 +21,15 @@ def host_without_zfs(host): assert not host.file_exists('/usr/sbin/zpool'), \ "zfs must not be installed on the host at the beginning of the tests" +# NOTE: @pytest.mark.usefixtures does not parametrize this fixture. +# To recreate host_with_zfs for each image_format value, accept +# image_format in the fixture arguments. +# ref https://docs.pytest.org/en/7.1.x/how-to/fixtures.html#use-fixtures-in-classes-and-modules-with-usefixtures @pytest.fixture(scope='package') -def host_with_zfs(host_without_zfs, host_with_saved_yum_state): +def host_with_zfs(host_without_zfs: Host, + host_with_saved_yum_state: Host, + image_format: str + ) -> Generator[Host]: host = host_with_saved_yum_state host.yum_install(['zfs']) host.ssh(['modprobe', 'zfs']) @@ -28,9 +43,12 @@ def zpool_vol0(sr_disk_wiped, host_with_zfs): host_with_zfs.ssh(['zpool', 'destroy', POOL_NAME]) @pytest.fixture(scope='package') -def zfs_sr(host, zpool_vol0): +def zfs_sr(host: Host, image_format: str, zpool_vol0: None) -> Generator[SR]: """ A ZFS SR on first host. """ - sr = host.sr_create('zfs', "ZFS-local-SR-test", {'location': POOL_PATH}) + sr = host.sr_create('zfs', "ZFS-local-SR-test", { + 'location': POOL_PATH, + 'preferred-image-formats': image_format + }, verify=True) yield sr # teardown sr.destroy() diff --git a/tests/storage/zfs/test_zfs_sr.py b/tests/storage/zfs/test_zfs_sr.py index 64ac862d6..5f2ada583 100755 --- a/tests/storage/zfs/test_zfs_sr.py +++ b/tests/storage/zfs/test_zfs_sr.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import pytest import logging @@ -7,6 +9,12 @@ from lib.common import vm_image, wait_for from tests.storage import vdi_is_open +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lib.host import Host + from lib.vdi import VDI + from .conftest import POOL_NAME, POOL_PATH # Requirements: @@ -21,13 +29,16 @@ class TestZFSSRCreateDestroy: and VM import. """ - def test_create_zfs_sr_without_zfs(self, host): + def test_create_zfs_sr_without_zfs(self, host: Host, image_format: str) -> None: # This test must be the first in the series in this module assert not host.file_exists('/usr/sbin/zpool'), \ "zfs must not be installed on the host at the beginning of the tests" sr = None try: - sr = host.sr_create('zfs', "ZFS-local-SR-test", {'location': POOL_PATH}) + sr = host.sr_create('zfs', "ZFS-local-SR-test", { + 'location': POOL_PATH, + 'preferred-image-formats': image_format + }, verify=True) except Exception: logging.info("SR creation failed, as expected.") if sr is not None: @@ -35,16 +46,29 @@ def test_create_zfs_sr_without_zfs(self, host): assert False, "SR creation should not have succeeded!" @pytest.mark.usefixtures("zpool_vol0") - def test_create_and_destroy_sr(self, host): + def test_create_and_destroy_sr(self, host: Host, image_format: str) -> None: # Create and destroy tested in the same test to leave the host as unchanged as possible - sr = host.sr_create('zfs', "ZFS-local-SR-test", {'location': POOL_PATH}, verify=True) + sr = host.sr_create('zfs', "ZFS-local-SR-test", { + 'location': POOL_PATH, + 'preferred-image-formats': image_format + }, verify=True) # import a VM in order to detect vm import issues here rather than in the vm_on_xfs_fixture used in # the next tests, because errors in fixtures break teardown vm = host.import_vm(vm_image('mini-linux-x86_64-bios'), sr_uuid=sr.uuid) vm.destroy(verify=True) sr.destroy(verify=True) -@pytest.mark.usefixtures("zpool_vol0") +# We want to skip class tests for qcow2 if the SM does not support qcow2 +@pytest.fixture(scope="class") +def for_qcow2_vdi_image_format(vdi_on_zfs_sr: VDI, image_format: str): + # only check qcow2-specific behavior + if image_format != "qcow2": + return + # feature-detect: if the SM doesn't report image-format, skip this check + if not vdi_on_zfs_sr.get_image_format(): + pytest.skip("SM does not report sm-config:image-format; skipping qcow2 format check") + +@pytest.mark.usefixtures("for_qcow2_vdi_image_format", "zpool_vol0") class TestZFSSR: @pytest.mark.quicktest def test_quicktest(self, zfs_sr): From f0875ffe1ee5353c298f5ec042881819d3612d48 Mon Sep 17 00:00:00 2001 From: Rushikesh Jadhav Date: Wed, 9 Jul 2025 16:36:52 +0530 Subject: [PATCH 5/5] tests/storage/largeblock: Updated largeblock to handle both vhd and qcow2 vdi image format Signed-off-by: Rushikesh Jadhav --- tests/storage/largeblock/conftest.py | 8 +++++-- .../storage/largeblock/test_largeblock_sr.py | 21 ++++++++++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/tests/storage/largeblock/conftest.py b/tests/storage/largeblock/conftest.py index 58702a757..854ed0665 100644 --- a/tests/storage/largeblock/conftest.py +++ b/tests/storage/largeblock/conftest.py @@ -11,10 +11,14 @@ from lib.sr import SR @pytest.fixture(scope='package') -def largeblock_sr(host: Host, unused_4k_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> Generator[SR]: +def largeblock_sr(host: Host, + unused_4k_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str) -> Generator[SR]: """ A LARGEBLOCK SR on first host. """ sr_disk = unused_4k_disks[host][0]["name"] - sr = host.sr_create('largeblock', "LARGEBLOCK-local-SR-test", {'device': '/dev/' + sr_disk}) + sr = host.sr_create('largeblock', "LARGEBLOCK-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}) yield sr # teardown sr.destroy() diff --git a/tests/storage/largeblock/test_largeblock_sr.py b/tests/storage/largeblock/test_largeblock_sr.py index c2683283e..81dbd64b1 100644 --- a/tests/storage/largeblock/test_largeblock_sr.py +++ b/tests/storage/largeblock/test_largeblock_sr.py @@ -9,6 +9,7 @@ if TYPE_CHECKING: from lib.host import Host + from lib.vdi import VDI # Requirements: # - one XCP-ng host with an additional unused 4KiB disk for the SR @@ -23,17 +24,31 @@ class TestLARGEBLOCKSRCreateDestroy: def test_create_sr_with_missing_device(self, host): try_to_create_sr_with_missing_device('largeblock', 'LARGEBLOCK-local-SR-test', host) - def test_create_and_destroy_sr(self, host: Host, unused_4k_disks: dict[Host, list[Host.BlockDeviceInfo]]) -> None: + def test_create_and_destroy_sr(self, host: Host, + unused_4k_disks: dict[Host, list[Host.BlockDeviceInfo]], + image_format: str) -> None: # Create and destroy tested in the same test to leave the host as unchanged as possible sr_disk = unused_4k_disks[host][0]["name"] - sr = host.sr_create('largeblock', "LARGEBLOCK-local-SR-test", {'device': '/dev/' + sr_disk}, verify=True) + sr = host.sr_create('largeblock', "LARGEBLOCK-local-SR-test", + {'device': '/dev/' + sr_disk, + 'preferred-image-formats': image_format}, verify=True) # import a VM in order to detect vm import issues here rather than in the vm_on_xfs_fixture used in # the next tests, because errors in fixtures break teardown vm = host.import_vm(vm_image('mini-linux-x86_64-bios'), sr_uuid=sr.uuid) vm.destroy(verify=True) sr.destroy(verify=True) -@pytest.mark.usefixtures("largeblock_sr") +# We want to skip class tests for qcow2 if the SM does not support qcow2 +@pytest.fixture(scope="class") +def for_qcow2_vdi_image_format(vdi_on_largeblock_sr: VDI, image_format: str): + # only check qcow2-specific behavior + if image_format != "qcow2": + return + # feature-detect: if the SM doesn't report image-format, skip this check + if not vdi_on_largeblock_sr.get_image_format(): + pytest.skip("SM does not report sm-config:image-format; skipping qcow2 format check") + +@pytest.mark.usefixtures("for_qcow2_vdi_image_format", "largeblock_sr") class TestLARGEBLOCKSR: @pytest.mark.quicktest def test_quicktest(self, largeblock_sr):