mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-10 12:16:49 +00:00
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.
This commit is contained in:
parent
e14a4cc10d
commit
975b7f0baf
6 changed files with 345 additions and 2 deletions
|
|
@ -1,8 +1,10 @@
|
|||
import contextlib
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
import uuid
|
||||
from urllib.parse import quote
|
||||
|
||||
import libvirt
|
||||
|
|
@ -87,6 +89,10 @@ def _after_fork_in_child():
|
|||
os.register_at_fork(after_in_child=_after_fork_in_child)
|
||||
|
||||
|
||||
# libvirt's per-host directory of the VMs' UEFI variables (NVRAM)
|
||||
NVRAM_DIR = "/var/lib/libvirt/qemu/nvram"
|
||||
|
||||
|
||||
class wvmConnection(object):
|
||||
"""
|
||||
class representing a single connection stored in the Connection Manager
|
||||
|
|
@ -832,6 +838,66 @@ class wvmConnect(object):
|
|||
def get_volume_by_path(self, path):
|
||||
return self.wvm.storageVolLookupByPath(path)
|
||||
|
||||
def remove_nvram(self, path, destination):
|
||||
"""Deletes the NVRAM file of a VM that migrated from this host to
|
||||
destination (a wvmConnect). Only a file in libvirt's per-host NVRAM
|
||||
directory, and only when destination does not see that directory too
|
||||
(shared storage would hold the migrated VM's own file). Returns
|
||||
whether a file was deleted."""
|
||||
if os.path.dirname(path) != NVRAM_DIR:
|
||||
return False
|
||||
marker = (
|
||||
f"<volume><name>webvirtcloud-check-{uuid.uuid4().hex}</name><capacity>0</capacity>"
|
||||
"<target><format type='raw'/></target></volume>"
|
||||
)
|
||||
with self._nvram_pool() as pool:
|
||||
mine = pool.createXML(marker, 0)
|
||||
try:
|
||||
# Creating the same file on destination fails when both see one
|
||||
# directory: the create is exclusive on the file server, unlike
|
||||
# a directory listing, which an NFS client may cache.
|
||||
with destination._nvram_pool() as other:
|
||||
try:
|
||||
other.createXML(marker, 0).delete(0)
|
||||
except libvirtError:
|
||||
return False # shared, or not known to be local
|
||||
finally:
|
||||
mine.delete(0)
|
||||
vol = self._find_volume(pool, os.path.basename(path))
|
||||
if vol is None:
|
||||
return False
|
||||
vol.delete(0)
|
||||
return True
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _nvram_pool(self):
|
||||
"""An active, refreshed pool on NVRAM_DIR: libvirt reads and deletes
|
||||
files only as volumes of a pool. A transient one when none exists."""
|
||||
for pool in self.wvm.listAllStoragePools(libvirt.VIR_CONNECT_LIST_STORAGE_POOLS_ACTIVE):
|
||||
# a pool without a target path (rbd, gluster, ...) holds no files
|
||||
if (util.get_xml_path(pool.XMLDesc(0), "/pool/target/path") or "").rstrip("/") == NVRAM_DIR:
|
||||
pool.refresh(0)
|
||||
yield pool
|
||||
return
|
||||
pool = self.wvm.storagePoolCreateXML(
|
||||
f"<pool type='dir'><name>webvirtcloud-nvram-{uuid.uuid4().hex[:8]}</name>"
|
||||
f"<target><path>{NVRAM_DIR}</path></target></pool>",
|
||||
0,
|
||||
)
|
||||
try:
|
||||
yield pool
|
||||
finally:
|
||||
pool.destroy()
|
||||
|
||||
@staticmethod
|
||||
def _find_volume(pool, name):
|
||||
try:
|
||||
return pool.storageVolLookupByName(name)
|
||||
except libvirtError as err:
|
||||
if err.get_error_code() == libvirt.VIR_ERR_NO_STORAGE_VOL:
|
||||
return None
|
||||
raise
|
||||
|
||||
def get_network(self, net):
|
||||
return self.wvm.networkLookupByName(net)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue