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

fix(instances): a shut-off clone into a volume name its pool has is refused before it begins

libvirt refused the copy only after the job had begun; recovery then
found a volume of the planned name and ended the job uncertain, holding
the VM. The names are checked when the job is queued and before it runs,
as a live clone did; that check now refreshes the pool first, so a live
clone's free space is read current too.

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:42:21 +03:00
parent 018ca69a3b
commit 4ee88d6767
4 changed files with 52 additions and 10 deletions

View file

@ -2582,13 +2582,7 @@ class wvmInstance(wvmConnect):
vol = self.get_volume_by_path(source.get("file"))
pool = vol.storagePoolLookupByVolume()
name = clone_data["disk-" + dev]
try:
pool.storageVolLookupByName(name)
except libvirtError as err:
if err.get_error_code() != VIR_ERR_NO_STORAGE_VOL:
raise
else:
raise util.OperationError(gettext_noop("Volume %(name)s already exists in storage pool %(pool)s"), name=name, pool=pool.name())
self._refuse_taken(pool, name)
capacity = vol.info()[1]
pools[pool.name()] = pool
copied[pool.name()] = copied.get(pool.name(), 0) + capacity
@ -2612,6 +2606,29 @@ class wvmInstance(wvmConnect):
pool=name, free=free / 1024**3, size=needed / 1024**3, percent=free_percent, minimum=free_min_gib)
return targets
@staticmethod
def _refuse_taken(pool, name):
"""A copy never takes a volume's name in its pool: libvirt would
refuse it only after the clone began. The pool is refreshed first,
so its free space is current too."""
pool.refresh(0)
try:
pool.storageVolLookupByName(name)
except libvirtError as err:
if err.get_error_code() != VIR_ERR_NO_STORAGE_VOL:
raise
else:
raise util.OperationError(gettext_noop("Volume %(name)s already exists in storage pool %(pool)s"),
name=name, pool=pool.name())
def check_clone_volumes(self, clone_data):
"""Refuses a shut-off clone whose copy of a disk would take a name its
pool has already, before anything is allocated (see clone_instance)."""
_tree, disks = self._clone_definition(clone_data, "")
for source, dev in disks:
vol = self.get_volume_by_path(source.get("file") or source.get("dev") or source.get("name"))
self._refuse_taken(vol.storagePoolLookupByVolume(), clone_data["disk-" + dev])
def create_clone_target(self, target, owner_uid, owner_gid):
"""An empty volume for a copied disk; libvirt refuses one that exists.
Returns its path."""