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

fix(storages): a volume a running VM uses is not cloned

The copy was read while the guest wrote: a raw disk was copied with
data of different instants, and a qcow2 one failed on qemu-img's image
lock with a message that did not say why. Such a clone is refused, with
the running VMs named, when it is queued and again before it runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Kk4h2TcUiaWmU5MUszYSea
This commit is contained in:
catborise 2026-11-07 23:40:28 +03:00
parent 50ed08f3ca
commit 018ca69a3b
8 changed files with 266 additions and 213 deletions

View file

@ -132,3 +132,24 @@ class DeleteVolumeTestCase(unittest.TestCase):
conn, vols = storage([domain("u1", ["/pool/other.qcow2"])], [volume("/pool/disk.qcow2")])
conn.del_volume("disk.qcow2")
vols["disk.qcow2"].delete.assert_called_once_with(0)
class RunningCopyTestCase(unittest.TestCase):
"""A running guest's disk is not copied: the copy would mix instants."""
def test_each_way_a_disk_names_the_volume_refuses_its_copy(self):
for source in ("file='/pool/disk.qcow2'", "dev='/pool/disk.qcow2'", "protocol='rbd' name='/pool/disk.qcow2'",
"pool='pool' volume='disk.qcow2'"):
with self.subTest(source=source):
dom = MagicMock(**{"name.return_value": "vm"})
dom.XMLDesc.return_value = f"<domain><devices><disk device='disk'><source {source}/></disk></devices></domain>"
conn, _ = storage([dom], [volume("/pool/disk.qcow2")])
conn.pool.name.return_value = "pool"
with self.assertRaisesRegex(util.OperationError, "The running VMs vm use volume disk.qcow2"):
conn.refuse_running_copy("disk.qcow2")
conn.wvm.listAllDomains.assert_called_once_with(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE)
def test_another_disk_does_not(self):
conn, _ = storage([domain("u1", ["/pool/other.qcow2"])], [volume("/pool/disk.qcow2")])
conn.pool.name.return_value = "pool"
conn.refuse_running_copy("disk.qcow2")