mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12: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:
parent
50ed08f3ca
commit
018ca69a3b
8 changed files with 266 additions and 213 deletions
|
|
@ -55,8 +55,10 @@ class CloneVolume(Kind):
|
|||
if op.user is None or not op.user.is_active or not op.user.is_superuser:
|
||||
raise PermissionDenied
|
||||
# a volume made outside the panel since the job was queued may have taken the name
|
||||
if op.intent["target"] in _storage(op.params)[1].get_volumes():
|
||||
stg = _storage(op.params)[1]
|
||||
if op.intent["target"] in stg.get_volumes():
|
||||
raise util.OperationError(gettext_noop("Name of volume already in use"))
|
||||
stg.refuse_running_copy(op.params["image"]) # the VM may have started since
|
||||
|
||||
def plan(self, user, params):
|
||||
compute, stg = _storage(params)
|
||||
|
|
@ -65,6 +67,7 @@ class CloneVolume(Kind):
|
|||
raise util.OperationError(gettext_noop("The pool has no volume named %(name)s"), name=params["image"])
|
||||
if is_iso(params["image"]):
|
||||
raise util.OperationError(gettext_noop("An ISO image is not cloned"))
|
||||
stg.refuse_running_copy(params["image"])
|
||||
target = stg.clone_name(params["name"], params["format"] or stg.get_volume_format_type(params["image"]))
|
||||
if target in volumes:
|
||||
raise util.OperationError(gettext_noop("Name of volume already in use"))
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from operations.jobs import Context
|
|||
from operations.models import Claim, Operation, acquire
|
||||
from libvirt import libvirtError
|
||||
from storages.jobs import CloneVolume
|
||||
from vrtManager import util
|
||||
|
||||
|
||||
class VolumeCloneJobTestCase(TransactionTestCase):
|
||||
|
|
@ -64,6 +65,12 @@ class VolumeCloneJobTestCase(TransactionTestCase):
|
|||
response = self.client.post(url, {"cln_volume": "1", "image": image, "name": "copy",
|
||||
"submission_key": key}, follow=True)
|
||||
self.assertContains(response, why)
|
||||
# a running guest's disk would be copied while it writes
|
||||
self.stg.get_volumes.return_value = ["base.qcow2"]
|
||||
self.stg.refuse_running_copy.side_effect = util.OperationError("The running VMs vm use volume base.qcow2")
|
||||
response = self.client.post(url, {"cln_volume": "1", "image": "base.qcow2", "name": "copy",
|
||||
"submission_key": "key-vol00004"}, follow=True)
|
||||
self.assertContains(response, "The running VMs vm use volume base.qcow2")
|
||||
self.assertFalse(Operation.objects.exists())
|
||||
|
||||
def test_recovery(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue