1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 19:26:48 +00:00
webvirtcloud/vrtManager/test_live_nvram.py
catborise 975b7f0baf fix(migration): remove the NVRAM file a live migration leaves on the source
libvirt undefines the source domain after a migration but keeps its NVRAM
file. A VM created later on that host with the same name reuses the file
and starts with the old VM's UEFI variables (boot entries, enrolled keys):
checked on libvirt 11.10, where only VIR_DOMAIN_START_RESET_NVRAM
recreates it from the template.

After a successful migration the file is now deleted on the source, as a
volume of the pool on libvirt's NVRAM directory or of a transient one.
Only a file in /var/lib/libvirt/qemu/nvram, and only when the destination
cannot create a marker file the source just created there (an exclusive
create also fails on shared storage whose listing an NFS client caches).
A failed removal is logged with the migration, which stands and is
recorded.
2026-10-09 07:29:10 +03:00

88 lines
3.6 KiB
Python

"""Live check that the NVRAM file a migrated VM leaves behind is removed,
and kept when the destination sees the same directory.
Runs only with TEST_LIBVIRT_HOST set (see instances/livetest.py). The
deletion needs a second host: TEST_LIBVIRT_SSH_HOST (and _LOGIN).
"""
import os
import unittest
import libvirt
from django.test import SimpleTestCase
from lxml import etree
from instances import livetest
from vrtManager.connection import CONN_SSH, NVRAM_DIR, connection_manager, wvmConnect
NAME = livetest.PREFIX + "nvram"
PATH = f"{NVRAM_DIR}/{NAME}_VARS.fd"
class LiveRemoveNvramTestCase(SimpleTestCase):
@classmethod
def setUpClass(cls):
if not livetest.enabled():
raise unittest.SkipTest("Set TEST_LIBVIRT_HOST to run the live libvirt tests")
cls.args = (
os.environ["TEST_LIBVIRT_HOST"],
os.environ.get("TEST_LIBVIRT_LOGIN", ""),
os.environ.get("TEST_LIBVIRT_PASSWORD", ""),
int(os.environ.get("TEST_LIBVIRT_TYPE", 4)),
)
cls.conn = connection_manager.get_connection(*cls.args)
livetest.cleanup(cls.conn)
cls.inventory = livetest.inventory(cls.conn)
cls.pools = {p.name() for p in cls.conn.listAllStoragePools()}
super().setUpClass()
@classmethod
def tearDownClass(cls):
livetest.cleanup(cls.conn)
super().tearDownClass()
if livetest.inventory(cls.conn) != cls.inventory:
raise AssertionError("Live tests changed objects outside the test namespace")
if {p.name() for p in cls.conn.listAllStoragePools()} != cls.pools:
raise AssertionError("A storage pool was left behind")
def leave_nvram(self):
"""What a migration with VIR_MIGRATE_UNDEFINE_SOURCE leaves: the file
without a domain."""
dom = self.conn.defineXML(
f"<domain type='kvm'><name>{NAME}</name><memory unit='MiB'>128</memory><vcpu>1</vcpu>"
"<os firmware='efi'><type arch='x86_64' machine='q35'>hvm</type></os>"
"<features><acpi/></features><devices/></domain>"
)
dom.createWithFlags(libvirt.VIR_DOMAIN_START_PAUSED) # creates the file
self.assertEqual(etree.fromstring(dom.XMLDesc(0)).findtext("os/nvram"), PATH)
dom.destroy()
dom.undefineFlags(libvirt.VIR_DOMAIN_UNDEFINE_KEEP_NVRAM)
def tearDown(self):
livetest.cleanup(self.conn)
host = wvmConnect(*self.args)
with host._nvram_pool() as pool:
vol = host._find_volume(pool, os.path.basename(PATH))
if vol is not None:
vol.delete(0)
def exists(self):
host = wvmConnect(*self.args)
with host._nvram_pool() as pool:
return host._find_volume(pool, os.path.basename(PATH)) is not None
def test_a_directory_both_connections_see_is_kept(self):
self.leave_nvram()
self.assertFalse(wvmConnect(*self.args).remove_nvram(PATH, wvmConnect(*self.args)))
self.assertTrue(self.exists())
def test_the_file_is_deleted_when_the_destination_is_another_host(self):
other_host = os.environ.get("TEST_LIBVIRT_SSH_HOST")
if not other_host or other_host == self.args[0]:
self.skipTest("Set TEST_LIBVIRT_SSH_HOST to a second host")
other = wvmConnect(other_host, os.environ.get("TEST_LIBVIRT_SSH_LOGIN", "root"), "", CONN_SSH)
other_pools = {p.name() for p in other.wvm.listAllStoragePools()}
self.leave_nvram()
self.assertTrue(wvmConnect(*self.args).remove_nvram(PATH, other))
self.assertFalse(self.exists())
self.assertEqual({p.name() for p in other.wvm.listAllStoragePools()}, other_pools)