diff options
| author | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-19 22:39:14 +0000 |
|---|---|---|
| committer | Bruce Ashfield <bruce.ashfield@gmail.com> | 2026-02-26 01:05:01 +0000 |
| commit | 6d94266a86246ac23abdbc28ac3b5c6e522fb748 (patch) | |
| tree | f3c14a73c232c1393d491b7f5d1cec318e12e285 /tests | |
| parent | 5c891aab54f1432f2afe75bf0dee0027fbb2e4bd (diff) | |
| download | meta-virtualization-6d94266a86246ac23abdbc28ac3b5c6e522fb748.tar.gz | |
xen: add configuration tests and update documentation
Add TestXenImageMinimalX86Config test class verifying:
- QB_CPU_KVM host passthrough for Xen CPUID filtering
- QB_MEM_VALUE override (not QB_MEM which can't override bbclass)
- dom0_mem in both QB_XEN_CMDLINE_EXTRA and WKS syslinux config
- vgabios SAVANNAH_GNU_MIRROR usage
Update Alpine recipe tests for per-arch checksums (name=${ALPINE_ARCH})
and S variable. Add qemux86-64 build and boot section to README-xen.md.
Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test_xen_guest_bundle.py | 129 |
1 files changed, 121 insertions, 8 deletions
diff --git a/tests/test_xen_guest_bundle.py b/tests/test_xen_guest_bundle.py index 2035a5f9..dd56dcc9 100644 --- a/tests/test_xen_guest_bundle.py +++ b/tests/test_xen_guest_bundle.py | |||
| @@ -281,14 +281,32 @@ class TestAlpineRecipe: | |||
| 281 | assert "alpine-minirootfs" in alpine_recipe_content | 281 | assert "alpine-minirootfs" in alpine_recipe_content |
| 282 | assert "subdir=alpine-rootfs" in alpine_recipe_content | 282 | assert "subdir=alpine-rootfs" in alpine_recipe_content |
| 283 | 283 | ||
| 284 | def test_sha256sum(self, alpine_recipe_content): | 284 | def test_per_arch_checksums(self, alpine_recipe_content): |
| 285 | """Test sha256sum is set (not placeholder).""" | 285 | """Test per-architecture sha256sums are set.""" |
| 286 | match = re.search(r'SRC_URI\[sha256sum\]\s*=\s*"([^"]*)"', | 286 | aarch64_match = re.search( |
| 287 | alpine_recipe_content) | 287 | r'SRC_URI\[aarch64\.sha256sum\]\s*=\s*"([^"]*)"', |
| 288 | assert match, "sha256sum not found" | 288 | alpine_recipe_content) |
| 289 | sha = match.group(1) | 289 | x86_64_match = re.search( |
| 290 | assert len(sha) == 64, f"sha256sum wrong length: {len(sha)}" | 290 | r'SRC_URI\[x86_64\.sha256sum\]\s*=\s*"([^"]*)"', |
| 291 | assert sha != "x" * 64, "sha256sum is still placeholder" | 291 | alpine_recipe_content) |
| 292 | assert aarch64_match, "aarch64 sha256sum not found" | ||
| 293 | assert x86_64_match, "x86_64 sha256sum not found" | ||
| 294 | for name, match in [("aarch64", aarch64_match), ("x86_64", x86_64_match)]: | ||
| 295 | sha = match.group(1) | ||
| 296 | assert len(sha) == 64, f"{name} sha256sum wrong length: {len(sha)}" | ||
| 297 | assert sha != "x" * 64, f"{name} sha256sum is still placeholder" | ||
| 298 | # Checksums must differ (different arch tarballs) | ||
| 299 | assert aarch64_match.group(1) != x86_64_match.group(1), \ | ||
| 300 | "aarch64 and x86_64 checksums should differ" | ||
| 301 | |||
| 302 | def test_src_uri_per_arch_name(self, alpine_recipe_content): | ||
| 303 | """Test SRC_URI uses name= for per-arch checksum matching.""" | ||
| 304 | assert "name=${ALPINE_ARCH}" in alpine_recipe_content, \ | ||
| 305 | "SRC_URI should use name=${ALPINE_ARCH} for per-arch checksums" | ||
| 306 | |||
| 307 | def test_s_variable(self, alpine_recipe_content): | ||
| 308 | """Test S variable is set to avoid UNPACKDIR warning.""" | ||
| 309 | assert 'S = "${UNPACKDIR}"' in alpine_recipe_content | ||
| 292 | 310 | ||
| 293 | def test_guest_bundles(self, alpine_recipe_content): | 311 | def test_guest_bundles(self, alpine_recipe_content): |
| 294 | """Test XEN_GUEST_BUNDLES is set.""" | 312 | """Test XEN_GUEST_BUNDLES is set.""" |
| @@ -359,3 +377,98 @@ class TestReadme: | |||
| 359 | """Test custom handler instructions.""" | 377 | """Test custom handler instructions.""" |
| 360 | assert "xen_guest_import_" in readme_content | 378 | assert "xen_guest_import_" in readme_content |
| 361 | assert "XEN_GUEST_IMPORT_DEPENDS_" in readme_content | 379 | assert "XEN_GUEST_IMPORT_DEPENDS_" in readme_content |
| 380 | |||
| 381 | |||
| 382 | # ============================================================================ | ||
| 383 | # x86-64 configuration tests | ||
| 384 | # ============================================================================ | ||
| 385 | |||
| 386 | class TestXenImageMinimalX86Config: | ||
| 387 | """Test xen-image-minimal.bb x86-64 configuration. | ||
| 388 | |||
| 389 | These verify the fixes needed for Xen on qemux86-64: | ||
| 390 | - CPU passthrough to avoid AVX stripping by Xen CPUID filtering | ||
| 391 | - Memory configuration using QB_MEM_VALUE (not QB_MEM) | ||
| 392 | - dom0_mem in QB_XEN_CMDLINE_EXTRA for runqemu | ||
| 393 | - dom0_mem in static WKS syslinux config for guest autostart | ||
| 394 | - vgabios using reliable download mirror | ||
| 395 | """ | ||
| 396 | |||
| 397 | @pytest.fixture(scope="class") | ||
| 398 | def image_recipe_content(self, meta_virt_dir): | ||
| 399 | path = meta_virt_dir / "recipes-extended" / "images" / "xen-image-minimal.bb" | ||
| 400 | if not path.exists(): | ||
| 401 | pytest.skip("xen-image-minimal.bb not found") | ||
| 402 | return path.read_text() | ||
| 403 | |||
| 404 | @pytest.fixture(scope="class") | ||
| 405 | def wks_cfg_content(self, meta_virt_dir): | ||
| 406 | path = meta_virt_dir / "wic" / "qemuboot-xen-x86-64.cfg" | ||
| 407 | if not path.exists(): | ||
| 408 | pytest.skip("qemuboot-xen-x86-64.cfg not found") | ||
| 409 | return path.read_text() | ||
| 410 | |||
| 411 | @pytest.fixture(scope="class") | ||
| 412 | def vgabios_recipe_content(self, meta_virt_dir): | ||
| 413 | recipes = list((meta_virt_dir / "recipes-extended" / "vgabios").glob( | ||
| 414 | "vgabios_*.bb")) | ||
| 415 | if not recipes: | ||
| 416 | pytest.skip("vgabios recipe not found") | ||
| 417 | return recipes[0].read_text() | ||
| 418 | |||
| 419 | def test_cpu_kvm_host_passthrough(self, image_recipe_content): | ||
| 420 | """Test QB_CPU_KVM uses -cpu host for qemux86-64. | ||
| 421 | |||
| 422 | Xen's CPUID filtering strips AVX/AVX2 when using fixed CPU models | ||
| 423 | (e.g. Skylake-Client), causing illegal instruction crashes with | ||
| 424 | x86-64-v3 tune. -cpu host passes real features through KVM. | ||
| 425 | """ | ||
| 426 | assert 'QB_CPU_KVM:qemux86-64 = "-cpu host' in image_recipe_content | ||
| 427 | |||
| 428 | def test_qb_mem_value_not_qb_mem(self, image_recipe_content): | ||
| 429 | """Test memory uses QB_MEM_VALUE, not QB_MEM. | ||
| 430 | |||
| 431 | qemuboot-xen-defaults.bbclass uses a hard assign: | ||
| 432 | QB_MEM = "-m ${QB_MEM_VALUE}" | ||
| 433 | A recipe-level QB_MEM ?= cannot override it. QB_MEM_VALUE ??= | ||
| 434 | in the class is the intended override point. | ||
| 435 | """ | ||
| 436 | assert 'QB_MEM_VALUE' in image_recipe_content | ||
| 437 | # Should NOT have QB_MEM ?= (common mistake) | ||
| 438 | assert 'QB_MEM ?=' not in image_recipe_content | ||
| 439 | |||
| 440 | def test_dom0_mem_in_xen_cmdline(self, image_recipe_content): | ||
| 441 | """Test dom0_mem is in QB_XEN_CMDLINE_EXTRA for runqemu.""" | ||
| 442 | match = re.search( | ||
| 443 | r'QB_XEN_CMDLINE_EXTRA\s*=\s*"([^"]*)"', image_recipe_content) | ||
| 444 | assert match, "QB_XEN_CMDLINE_EXTRA not found" | ||
| 445 | assert "dom0_mem=" in match.group(1), \ | ||
| 446 | "dom0_mem= must be in QB_XEN_CMDLINE_EXTRA" | ||
| 447 | |||
| 448 | def test_dom0_mem_in_wks_syslinux(self, wks_cfg_content): | ||
| 449 | """Test dom0_mem is in static WKS syslinux config. | ||
| 450 | |||
| 451 | Without dom0_mem in the syslinux config, Xen gives ALL memory | ||
| 452 | to Dom0 and guest autostart fails with 'failed to free memory'. | ||
| 453 | QB_XEN_CMDLINE_EXTRA only affects runqemu's dynamic command line. | ||
| 454 | """ | ||
| 455 | assert "dom0_mem=" in wks_cfg_content, \ | ||
| 456 | "dom0_mem= must be in qemuboot-xen-x86-64.cfg for guest autostart" | ||
| 457 | |||
| 458 | def test_wks_xen_kernel_cmdline(self, wks_cfg_content): | ||
| 459 | """Test WKS config has correct Xen and kernel boot params.""" | ||
| 460 | assert "mboot.c32" in wks_cfg_content | ||
| 461 | assert "/xen.gz" in wks_cfg_content | ||
| 462 | assert "console=hvc0" in wks_cfg_content | ||
| 463 | |||
| 464 | def test_vgabios_savannah_mirror(self, vgabios_recipe_content): | ||
| 465 | """Test vgabios uses ${SAVANNAH_GNU_MIRROR} for reliable downloads. | ||
| 466 | |||
| 467 | The old http://savannah.gnu.org/download/ URL can redirect to | ||
| 468 | broken mirrors that return HTML instead of the tarball. | ||
| 469 | """ | ||
| 470 | assert "${SAVANNAH_GNU_MIRROR}" in vgabios_recipe_content, \ | ||
| 471 | "vgabios should use ${SAVANNAH_GNU_MIRROR} variable" | ||
| 472 | # Should NOT use raw savannah.gnu.org URL | ||
| 473 | assert "http://savannah.gnu.org/download" not in vgabios_recipe_content, \ | ||
| 474 | "Should not use raw savannah.gnu.org URL (broken redirects)" | ||
