1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-09 03:36:48 +00:00

fix(vrtManager): refuse to migrate a VM with snapshots in every state

libvirt keeps snapshot metadata on the source host and a migration does
not carry it, even when both hosts share the disks. Only the shut-off
path refused such a VM itself; a running or paused one relied on
libvirt's own refusal. moveto now refuses it for every state, before
any check against the destination, with the same message.
This commit is contained in:
catborise 2026-10-13 07:43:59 +03:00
parent 097a91592c
commit 5388743053
2 changed files with 16 additions and 3 deletions

View file

@ -117,6 +117,10 @@ class wvmInstances(wvmConnect):
mode.append(label)
dom = conn.instance
# libvirt keeps snapshot metadata on the source host: shared disks do
# not carry it to the destination.
if dom.snapshotNum(0):
raise util.OperationError("A VM with snapshots cannot be migrated")
if state == 5:
self.refuse_local_state(dom.XMLDesc(0))
definitions = {dom.XMLDesc(0)}
@ -283,8 +287,6 @@ class wvmInstances(wvmConnect):
first (RHEL-145770) and tracks the fix as RHEL-156800; RHEL/Rocky 9.8
with libvirt 11.10.0-12.el9_8 is affected. When supported hosts all
have the fix, offline migration can go back to dom.migrate."""
if dom.snapshotNum(0):
raise util.OperationError("A VM with snapshots cannot be migrated")
if dom.hasManagedSaveImage(0):
raise util.OperationError("A VM with a saved state cannot be migrated")
for lookup, key in ((self.wvm.lookupByUUIDString, dom.UUIDString()), (self.wvm.lookupByName, dom.name())):

View file

@ -237,7 +237,6 @@ class ShutOffMigrationTestCase(unittest.TestCase):
def test_refused_before_anything_changes(self):
cases = {
"snapshots": (FakeHost(), {"snapshots": 2}),
"saved state": (FakeHost(), {"saved": True}),
"same uuid on the destination": (FakeHost(existing={"u-1"}), {}),
"same name on the destination": (FakeHost(existing={"vm"}), {}),
@ -300,6 +299,18 @@ class MigrationModeTestCase(unittest.TestCase):
destination().moveto(source, "vm", unsafe=False, **{"live": False, "offline": False, **request})
self.assertEqual(source.instance.calls, [])
def test_a_vm_with_snapshots_is_refused_in_every_state(self):
"""libvirt keeps snapshot metadata on the source host; a migration
does not carry it, even with shared disks."""
for state, request in ((1, {"live": True}), (1, {}), (3, {}), (5, {})):
with self.subTest(state=state, request=request):
host = FakeHost()
source = FakeSource(state, snapshots=1)
with self.assertRaisesRegex(util.OperationError, "A VM with snapshots cannot be migrated"):
destination(host).moveto(source, "vm", unsafe=False, **{"live": False, "offline": False, **request})
self.assertEqual(source.instance.calls, [])
self.assertEqual(host.defined, [])
def test_modes(self):
live, unsafe = libvirt.VIR_MIGRATE_LIVE, libvirt.VIR_MIGRATE_UNSAFE
converge, compressed = libvirt.VIR_MIGRATE_AUTO_CONVERGE, libvirt.VIR_MIGRATE_COMPRESSED