Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,34 @@
virsh_migrate_options = '--live --p2p --verbose'
- non_p2p:
virsh_migrate_options = '--live --verbose'
- p2p_tunneled:
virsh_migrate_options = '--live --p2p --tunnelled --verbose'
- postcopy:
virsh_migrate_options = '--live --postcopy --verbose'
- postcopy_p2p:
virsh_migrate_options = '--live --postcopy --p2p --verbose'
- auto_converge:
virsh_migrate_options = '--live --auto-converge --verbose'
- auto_converge_p2p:
virsh_migrate_options = '--live --auto-converge --p2p --verbose'
variants migration_option:
- with_xml:
- without_xml:
variants cpu_mode:
- host_model:
no s390x
numa_cell = "'numa_cell': [{'cpus': '0-1', 'memory': '2097152', 'unit': 'KiB'}]"
vm_attrs = {'cpu': {'mode': 'host-model', 'check': 'partial', ${numa_cell}}}
numa_cell = "'numa_cell': [{'cpus': '0-31', 'memory': '16777216', 'unit': 'KiB'}]"
vm_attrs = {'max_mem': 16777216, 'max_mem_unit': 'KiB', 'current_mem': 16777216, 'current_mem_unit': 'KiB', 'cpu': {'mode': 'host-model', 'check': 'partial', ${numa_cell}}}
- custom:
no aarch64
no aarch64,ppc64le,ppc64
cpu_mode = "custom"
variants kdump:
- kdump_migration:
# Trigger kdump on guest after migration and verify vmcore is created
# Crosses with all migration variants (p2p, p2p_tunneled, postcopy,
# auto_converge, etc.) - only runs with host_model cpu_mode (Power arch)
no custom
post_migration_kdump = yes
on_crash = restart
- no_kdump:
post_migration_kdump = no
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import os
import platform
import re

from virttest import data_dir
from virttest import utils_kdump
from virttest import virsh
from virttest.libvirt_xml import vm_xml

Expand All @@ -15,10 +18,22 @@ def run(test, params, env):
:param params: Dictionary with the test parameters
:param env: Dictionary with test environment.
"""
def get_host_cpu_model():
"""Get host CPU model in lowercase."""
try:
caps_xml = virsh.capabilities()
match = re.search(r'<model>(\w+)</model>', caps_xml)
if match:
model = match.group(1).lower()
test.log.info("Detected CPU model: %s", model)
return model
except Exception as e:
test.log.warning("Failed to detect CPU model: %s", e)
return None

def setup_test():
"""
Setup steps

"""
cpu_mode = params.get("cpu_mode")
migration_option = params.get("migration_option")
Expand All @@ -28,11 +43,26 @@ def setup_test():
vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name)
if cpu_mode == "host_model":
vm_attrs = eval(params.get('vm_attrs', '{}'))
arch = platform.machine()
if arch in ['ppc64', 'ppc64le'] and 'cpu' in vm_attrs:
host_model = get_host_cpu_model()
if host_model:
vm_attrs['cpu']['model'] = host_model
vm_attrs['cpu']['fallback'] = params.get('model_fallback', 'forbid')
test.log.info("Power arch - added CPU model: %s", host_model)
vmxml.setup_attrs(**vm_attrs)
vmxml.sync()
else:
base_steps.sync_cpu_for_mig(params)

# If kdump will be triggered after migration, ensure the VM restarts
# after a crash so we can verify the vmcore and wait for reboot.
if params.get("post_migration_kdump") == "yes":
vmxml = vm_xml.VMXML.new_from_dumpxml(vm_name)
vmxml.on_crash = "restart"
vmxml.sync()
test.log.info("Set on_crash=restart for kdump test.")

if not vm.is_alive():
vm.start()
vm.wait_for_login().close()
Expand All @@ -42,6 +72,70 @@ def setup_test():
virsh.dumpxml(vm_name, extra="--migratable", to_file=xmlfile, ignore_status=False)
params.update({"virsh_migrate_extra": f"--xml {xmlfile}"})

def _get_vmcores_via_serial(session):
"""List vmcore files using an already-open serial session."""
status, output = session.cmd_status_output(
"find /var/crash/ -type f -name vmcore 2>/dev/null | sort",
timeout=60,
)
if status:
return []
return [v for v in output.split() if v]

def verify_kdump_after_migration():
"""
Trigger kdump on the guest after successful migration and verify
a new vmcore is created.

All guest access uses wait_for_serial_login() which connects via the
virsh serial console (qemu+tcp://dest/system console <vm>). This
bypasses the source-side ARP / address-cache verification that
wait_for_login() performs and that always fails after live migration
because the VM MAC is no longer reachable on the source host virbr0.

Guest must have on_crash=restart in its XML (set in setup_test()) so
it reboots after kdump allowing vmcore presence to be confirmed.
"""
test.log.info("Triggering kdump on guest '%s' after migration.", vm_name)

# Pre-crash vmcore snapshot via serial console
pre_session = vm.wait_for_serial_login(timeout=240)
try:
pre_vmcores = _get_vmcores_via_serial(pre_session)
test.log.info("Pre-crash vmcores: %s", pre_vmcores)
finally:
pre_session.close()

# Trigger kernel panic via serial session
crash_session = vm.wait_for_serial_login(timeout=240)
utils_kdump.trigger_crash(
vm,
session=crash_session,
wait_time=300,
test=test,
)
test.log.info("Kernel panic triggered; waiting for guest to reboot.")

# Wait for reboot via serial (on_crash=restart ensures reboot)
vm.wait_for_serial_login(timeout=360).close()
test.log.info("Guest rebooted after kdump.")

# Post-crash vmcore list via serial
post_session = vm.wait_for_serial_login(timeout=240)
try:
post_vmcores = _get_vmcores_via_serial(post_session)
test.log.info("Post-crash vmcores: %s", post_vmcores)
finally:
post_session.close()

new_vmcores = [v for v in post_vmcores if v not in pre_vmcores]
if not new_vmcores:
test.fail(
"No new vmcore found in guest '%s' after kdump. "
"pre=%s post=%s" % (vm_name, pre_vmcores, post_vmcores)
)
test.log.info("kdump verified - new vmcore(s): %s", new_vmcores)

vm_name = params.get("migrate_main_vm")

vm = env.get_vm(vm_name)
Expand All @@ -51,5 +145,7 @@ def setup_test():
setup_test()
migration_obj.run_migration()
migration_obj.verify_default()
if params.get("post_migration_kdump") == "yes":
verify_kdump_after_migration()
finally:
migration_obj.cleanup_connection()