From dacabae2ab5b93801b39bc1cbcf7b3960d2b2fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Wed, 8 Oct 2025 14:08:56 +0200 Subject: [PATCH 1/4] create_vdi: make vdi name optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gaëtan Lehmann --- lib/common.py | 10 ++++++++++ lib/sr.py | 6 +++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/common.py b/lib/common.py index 05df77586..2dbc3d967 100644 --- a/lib/common.py +++ b/lib/common.py @@ -7,6 +7,8 @@ import itertools import logging import os +import random +import string import sys import time import traceback @@ -238,6 +240,14 @@ def url_download(url: str, filename: str) -> None: fd.write(chunk) os.rename(tempfilename, filename) +def randid(length=6): + """ + Generates a random string of a specified length. + The string consists of lowercase letters, uppercase letters, and digits. + """ + characters = string.ascii_lowercase + string.digits + return ''.join(random.choices(characters, k=length)) + @overload def _param_get(host: 'lib.host.Host', xe_prefix: str, uuid: str, param_name: str, key: Optional[str] = ..., accept_unknown_key: Literal[False] = ...) -> str: diff --git a/lib/sr.py b/lib/sr.py index 24c22a919..05800adf1 100644 --- a/lib/sr.py +++ b/lib/sr.py @@ -5,6 +5,7 @@ from lib.common import ( GiB, prefix_object_name, + randid, safe_split, strtobool, wait_for, @@ -167,7 +168,10 @@ def get_type(self) -> str: self._type = self.pool.master.xe("sr-param-get", {"uuid": self.uuid, "param-name": "type"}) return self._type - def create_vdi(self, name_label: str, virtual_size: int = 1 * GiB, image_format: Optional[str] = None) -> VDI: + def create_vdi( + self, name_label: str | None = None, virtual_size: int = 1 * GiB, image_format: Optional[str] = None + ) -> VDI: + name_label = name_label or f'test-vdi-{randid()}' logging.info("Create VDI %r on SR %s", name_label, self.uuid) args = { 'name-label': prefix_object_name(name_label), From 52ba840177c2cba18a393e77672d58ed4067fef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Tue, 14 Oct 2025 21:17:44 +0200 Subject: [PATCH 2/4] vm: add destroy_vdi_by_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vdi uuid changes during migration, so we use its name to identify it Signed-off-by: Gaëtan Lehmann --- lib/vm.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/vm.py b/lib/vm.py index eaf310912..f08e1abbc 100644 --- a/lib/vm.py +++ b/lib/vm.py @@ -1,5 +1,7 @@ from __future__ import annotations +import pytest + import logging import os import tempfile @@ -345,6 +347,14 @@ def destroy_vdi(self, vdi_uuid: str) -> None: super().destroy_vdi(vdi_uuid) break + def destroy_vdi_by_name(self, name: str) -> None: + for vdi in self.vdis: + if vdi.name() == name: + self.vdis.remove(vdi) + super().destroy_vdi(vdi.uuid) + return + raise pytest.fail(f"No VDI named '{name}' in vm {self.uuid}") + def create_vdis_list(self) -> None: """ Used to redo the VDIs list of the VM when reverting a snapshot. """ try: From addf191d1746613b61bfc597d385fd64062d9c96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Wed, 8 Oct 2025 14:06:32 +0200 Subject: [PATCH 3/4] Add integrity check in live migration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gaëtan Lehmann --- tests/storage/storage.py | 47 +++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/tests/storage/storage.py b/tests/storage/storage.py index 036a77887..6d518f538 100644 --- a/tests/storage/storage.py +++ b/tests/storage/storage.py @@ -1,16 +1,17 @@ +from __future__ import annotations + import logging from lib.commands import SSHCommandFailed -from lib.common import strtobool, wait_for, wait_for_not +from lib.common import GiB, strtobool, wait_for, wait_for_not +from lib.host import Host from lib.sr import SR from lib.vdi import VDI +from lib.vm import VM -from typing import TYPE_CHECKING, Literal - -if TYPE_CHECKING: - from lib.host import Host - from lib.vm import VM +from typing import Literal +RANDSTREAM_1GIB_CHECKSUM = '65280014' def try_to_create_sr_with_missing_device(sr_type, label, host): try: @@ -42,21 +43,51 @@ def cold_migration_then_come_back(vm, prov_host, dest_host, dest_sr): vm.wait_for_os_booted() vm.shutdown(verify=True) -def live_storage_migration_then_come_back(vm, prov_host, dest_host, dest_sr): + if vdi_name is not None: + vm.destroy_vdi_by_name(vdi_name) + +def live_storage_migration_then_come_back(vm: VM, prov_host: Host, dest_host: Host, dest_sr: SR): prov_sr = vm.get_sr() + vdi_name = None + + if not vm.is_windows: + vdi = prov_sr.create_vdi(virtual_size=1 * GiB) + vdi_name = vdi.name() + vm.connect_vdi(vdi, 'xvdb') + # start VM vm.start(on=prov_host.uuid) - vm.wait_for_os_booted() + if vm.is_windows: + vm.wait_for_os_booted() + else: + vm.wait_for_vm_running_and_ssh_up() + install_randstream(vm) + logging.info("Generate /dev/xvdb content") + vm.ssh("randstream generate -v /dev/xvdb") + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") + # Move the VM to another host of the pool vm.migrate(dest_host, dest_sr) wait_for(lambda: vm.all_vdis_on_sr(dest_sr), "Wait for all VDIs on destination SR") wait_for(lambda: vm.is_running_on_host(dest_host), "Wait for VM to be running on destination host") + if not vm.is_windows: + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") + # Migrate it back to the provenance SR vm.migrate(prov_host, prov_sr) wait_for(lambda: vm.all_vdis_on_sr(prov_sr), "Wait for all VDIs back on provenance SR") wait_for(lambda: vm.is_running_on_host(prov_host), "Wait for VM to be running on provenance host") + if not vm.is_windows: + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") + vm.shutdown(verify=True) + if vdi_name is not None: + vm.destroy_vdi_by_name(vdi_name) + def vdi_is_open(vdi): sr = vdi.sr From 6c7d966e011130e8bf9c0a35ff00330c6ffcec3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Wed, 8 Oct 2025 18:29:44 +0200 Subject: [PATCH 4/4] Add integrity check in cold migration tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gaëtan Lehmann --- tests/storage/storage.py | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/storage/storage.py b/tests/storage/storage.py index 6d518f538..71406d5e5 100644 --- a/tests/storage/storage.py +++ b/tests/storage/storage.py @@ -24,23 +24,53 @@ def try_to_create_sr_with_missing_device(sr_type, label, host): return assert False, 'SR creation should not have succeeded!' -def cold_migration_then_come_back(vm, prov_host, dest_host, dest_sr): +def cold_migration_then_come_back(vm: VM, prov_host: Host, dest_host: Host, dest_sr: SR): """ Storage migration of a shutdown VM, then migrate it back. """ prov_sr = vm.get_sr() + vdi_name = None + + if not vm.is_windows: + # the vdi will be destroyed with the vm + vdi = prov_sr.create_vdi(virtual_size=1 * GiB) + vdi_name = vdi.name() + vm.connect_vdi(vdi, 'xvdb') + vm.start() + vm.wait_for_vm_running_and_ssh_up() + install_randstream(vm) + logging.info("Generate /dev/xvdb content") + vm.ssh("randstream generate -v /dev/xvdb") + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") + vm.shutdown(verify=True) + assert vm.is_halted() + # Move the VM to another host of the pool vm.migrate(dest_host, dest_sr) wait_for(lambda: vm.all_vdis_on_sr(dest_sr), "Wait for all VDIs on destination SR") + # Start VM to make sure it works vm.start(on=dest_host.uuid) - vm.wait_for_os_booted() + if vm.is_windows: + vm.wait_for_os_booted() + else: + vm.wait_for_vm_running_and_ssh_up() + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") vm.shutdown(verify=True) + # Migrate it back to the provenance SR vm.migrate(prov_host, prov_sr) wait_for(lambda: vm.all_vdis_on_sr(prov_sr), "Wait for all VDIs back on provenance SR") + # Start VM to make sure it works vm.start(on=prov_host.uuid) - vm.wait_for_os_booted() + if vm.is_windows: + vm.wait_for_os_booted() + else: + vm.wait_for_vm_running_and_ssh_up() + logging.info("Validate /dev/xvdb") + vm.ssh(f"randstream validate -v --expected-checksum {RANDSTREAM_1GIB_CHECKSUM} /dev/xvdb") vm.shutdown(verify=True) if vdi_name is not None: