1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix(jobs): a live clone that recovery cannot define ends uncertain

After a successful copy, recovery defined the clone; a define that
failed raised, and recovery retried it with the same result. One case
is certain: a worker killed after define_clone copied the UEFI
variables but before it defined the VM leaves <clone>_VARS.fd, and every
retry is refused because the volume exists. The job then waited for
recovery with its claims on the source VM and the clone's name. A
libvirt error now ends it uncertain, naming the error and the copied
volumes; an unreachable host still waits.
This commit is contained in:
catborise 2026-11-06 08:58:33 +03:00
parent 50c6e1f0fe
commit e3e32f6b90
6 changed files with 98 additions and 64 deletions

View file

@ -714,7 +714,14 @@ class CloneInstance(Kind):
if not success:
_delete_created(proxy, op)
return Outcome(ended, error=say(errmsg or _("The copy failed")))
proxy.define_clone(op.intent["clone"], op.intent["uuid"], {c["dev"]: c["path"] for c in created})
try:
proxy.define_clone(op.intent["clone"], op.intent["uuid"], {c["dev"]: c["path"] for c in created})
except util.ConnectionFailed:
raise # unreachable: recovery waits and retries
except libvirtError as err: # it would fail the same way at every retry
return Outcome(Operation.UNCERTAIN, error=say(_(
"The disks were copied, but the clone could not be defined: %(error)s; check these volumes, "
"then resolve this job: %(volumes)s"), error=err, volumes=volumes))
return Outcome(Operation.SUCCEEDED, result=_clone_made(op, _finish_clone(op, op.intent["clone"], [])))
def log_entry(self, op, state):

View file

@ -19,7 +19,7 @@ from instances.jobs import CloneInstance
from instances.models import Instance
from operations.utils import words
from operations.jobs import Cancelled, Context
from vrtManager.util import OperationError
from vrtManager.util import ConnectionFailed, OperationError
from operations.models import Claim, Operation, acquire
@ -601,6 +601,17 @@ class LiveCloneJobTestCase(TransactionTestCase):
Instance.objects.filter(uuid=op.intent["uuid"]).delete()
op.delete()
def test_recovery_that_cannot_define_the_clone_ends_uncertain(self):
# the worker stopped after define_clone copied the UEFI variables: a retry finds the copy there
self.track("define_clone", libvirt_error(libvirt.VIR_ERR_STORAGE_VOL_EXIST))
outcome, _op = self.recovered(copy_result=[True, ""])
self.assertEqual(outcome.state, Operation.UNCERTAIN)
self.assertIn("/real/copy-vda.qcow2", words(outcome.error))
self.assertEqual(self.deleted(), [])
self.track("define_clone", ConnectionFailed("host down"))
with self.assertRaises(ConnectionFailed):
self.recovered(copy_result=[True, ""])
def test_recovery_gives_the_clone_the_size_asked_and_starts_it(self):
clone = {"name": "copy", "disk-vda": "copy-vda.qcow2", "disk_owner_uid": 107, "disk_owner_gid": 107,
"vcpu": 2, "memory": None, "start": True}

Binary file not shown.

View file

@ -2857,7 +2857,7 @@ msgstr ""
msgid "Clone VM"
msgstr "Sanal makineyi klonla"
#: instances/jobs.py:559 instances/jobs.py:566 instances/jobs.py:834
#: instances/jobs.py:559 instances/jobs.py:566 instances/jobs.py:841
msgid "Copying disks"
msgstr "Diskler kopyalanıyor"
@ -2922,140 +2922,149 @@ msgstr ""
"libvirt'te kopyanın sonucu yok; şu birimleri kontrol edin, sonra bu işi "
"çözün: %(volumes)s"
#: instances/jobs.py:739 instances/templates/create_instance_w1.html:60
#: instances/jobs.py:723
#, python-format
msgid ""
"The disks were copied, but the clone could not be defined: %(error)s; check "
"these volumes, then resolve this job: %(volumes)s"
msgstr ""
"Diskler kopyalandı, ancak klon tanımlanamadı: %(error)s; şu birimleri kontrol"
" edin, sonra bu işi çözün: %(volumes)s"
#: instances/jobs.py:746 instances/templates/create_instance_w1.html:60
#: instances/templates/create_instance_w2.html:356
msgid "Create VM"
msgstr "Sanal makine oluştur"
#: instances/jobs.py:763 instances/jobs.py:808 instances/views.py:2504
#: instances/jobs.py:770 instances/jobs.py:815 instances/views.py:2504
msgid "A virtual machine with this name already exists"
msgstr "Bu isimde bir sanal makine mevcut"
#: instances/jobs.py:765 instances/jobs.py:805
#: instances/jobs.py:772 instances/jobs.py:812
msgid "There is an instance with same name. Remove it and try again!"
msgstr "Aynı isimde başka bir sanal makine var. Onu silip yeniden deneyin!"
#: instances/jobs.py:768
#: instances/jobs.py:775
msgid "First you need to create or select an image"
msgstr "Öncelikle bir imaj oluşturun ya da mevcut bir imajı seçin"
#: instances/jobs.py:770
#: instances/jobs.py:777
msgid "Choose one disk source: a new disk, existing volumes or a template"
msgstr ""
"Tek bir disk kaynağı seçin: yeni bir disk, mevcut birimler ya da bir şablon"
#: instances/jobs.py:772
#: instances/jobs.py:779
msgid "Image has already exist. Please check volumes or change instance name"
msgstr ""
"İmaj zaten mevcut. Lütfen ya makine adını değişin ya da disk alanlarını "
"kontrol edin"
#: instances/jobs.py:774
#: instances/jobs.py:781
msgid "Invalid cache mode"
msgstr "Geçersiz önbellek modu"
#: instances/jobs.py:776
#: instances/jobs.py:783
#, python-format
msgid "This host offers no CPU mode or model %(mode)s"
msgstr "Bu sunucu %(mode)s CPU modunu veya modelini sunmuyor"
#: instances/jobs.py:778
#: instances/jobs.py:785
#, python-format
msgid "A CD-ROM cannot use the %(bus)s bus"
msgstr "CD-ROM %(bus)s veri yolunu kullanamaz"
#: instances/jobs.py:781
#: instances/jobs.py:788
msgid "The cloud-init seed is read from a CD-ROM: choose its bus"
msgstr "cloud-init tohumu bir CD-ROM'dan okunur: veri yolunu seçin"
#: instances/jobs.py:784
#: instances/jobs.py:791
msgid "The cloud-init seed needs a directory storage pool for the disk"
msgstr "cloud-init tohumu, disk için bir dizin depolama havuzu gerektirir"
#: instances/jobs.py:790
#: instances/jobs.py:797
#, python-format
msgid "This host offers no firmware %(firmware)s"
msgstr "Bu sunucu %(firmware)s firmware'ini sunmuyor"
#: instances/jobs.py:792 vrtManager/instance.py:3023
#: instances/jobs.py:799 vrtManager/instance.py:3023
msgid "This host offers no TPM 2.0: install swtpm on it"
msgstr "Bu sunucu TPM 2.0 sunmuyor: üzerine swtpm kurun"
#: instances/jobs.py:853
#: instances/jobs.py:860
msgid "Writing the cloud-init seed"
msgstr "cloud-init tohumu yazılıyor"
#: instances/jobs.py:860
#: instances/jobs.py:867
msgid "Defining the VM"
msgstr "Sanal makine tanımlanıyor"
#: instances/jobs.py:893
#: instances/jobs.py:900
#, python-format
msgid "%(error)s. Remove these volumes by hand: %(volumes)s"
msgstr "%(error)s. Şu birimleri elle silin: %(volumes)s"
#: instances/jobs.py:918
#: instances/jobs.py:925
#, python-format
msgid "The instance is already on %(compute)s"
msgstr "Sanal makine zaten %(compute)s üzerinde"
#: instances/jobs.py:920
#: instances/jobs.py:927
#, python-format
msgid "%(compute)s is not reachable"
msgstr "%(compute)s sunucusuna ulaşılamıyor"
#: instances/jobs.py:922
#: instances/jobs.py:929
#, python-format
msgid "%(compute)s is the VM's own host under another name"
msgstr "%(compute)s, sanal makinenin kendi sunucusunun başka bir adı"
#: instances/jobs.py:940
#: instances/jobs.py:947
msgid "Migrate VM"
msgstr "Sanal makineyi taşı"
#: instances/jobs.py:984
#: instances/jobs.py:991
msgid "Migrating the VM"
msgstr "Sanal makine taşınıyor"
#: instances/jobs.py:1031
#: instances/jobs.py:1038
msgid "libvirt is still migrating the VM"
msgstr "libvirt sanal makineyi hâlâ taşıyor"
#: instances/jobs.py:1038
#: instances/jobs.py:1045
msgid "The VM stayed on the source host"
msgstr "Sanal makine kaynak sunucuda kaldı"
#: instances/jobs.py:1040
#: instances/jobs.py:1047
msgid ""
"The VM is on both computes or on neither; check them, then resolve this job."
msgstr ""
"Sanal makine ya iki sunucuda da var ya da hiçbirinde yok; kontrol edin, sonra"
" bu işi çözün."
#: instances/jobs.py:1088
#: instances/jobs.py:1095
msgid "Move disk"
msgstr "Diski taşı"
#: instances/jobs.py:1095
#: instances/jobs.py:1102
msgid "Choose a disk of the VM"
msgstr "Sanal makinenin bir diskini seçin"
#: instances/jobs.py:1119
#: instances/jobs.py:1126
msgid "Copying the disk"
msgstr "Disk kopyalanıyor"
#: instances/jobs.py:1162
#: instances/jobs.py:1169
#, python-format
msgid "Another block job runs on %(dev)s; check the VM"
msgstr ""
"%(dev)s üzerinde başka bir blok işi çalışıyor; sanal makineyi kontrol edin"
#: instances/jobs.py:1168
#: instances/jobs.py:1175
#, python-format
msgid "The disk stayed in %(path)s; remove %(dest)s if it is there"
msgstr "Disk %(path)s içinde kaldı; varsa %(dest)s dosyasını silin"
#: instances/jobs.py:1170
#: instances/jobs.py:1177
#, python-format
msgid "The definitions of %(dev)s name %(paths)s; check the VM"
msgstr "%(dev)s tanımları %(paths)s gösteriyor; sanal makineyi kontrol edin"

Binary file not shown.

View file

@ -2752,7 +2752,7 @@ msgstr "无法解冻客户机的文件系统(%(error)s);请用以下命令
msgid "Clone VM"
msgstr "克隆虚拟机"
#: instances/jobs.py:559 instances/jobs.py:566 instances/jobs.py:834
#: instances/jobs.py:559 instances/jobs.py:566 instances/jobs.py:841
msgid "Copying disks"
msgstr "正在复制磁盘"
@ -2807,134 +2807,141 @@ msgid ""
"job: %(volumes)s"
msgstr "libvirt 没有复制的结果;请检查这些卷,然后处理此任务:%(volumes)s"
#: instances/jobs.py:739 instances/templates/create_instance_w1.html:60
#: instances/jobs.py:723
#, python-format
msgid ""
"The disks were copied, but the clone could not be defined: %(error)s; check "
"these volumes, then resolve this job: %(volumes)s"
msgstr "磁盘已复制,但无法定义克隆:%(error)s;请检查这些卷,然后处理此任务:%(volumes)s"
#: instances/jobs.py:746 instances/templates/create_instance_w1.html:60
#: instances/templates/create_instance_w2.html:356
msgid "Create VM"
msgstr "创建虚拟机"
#: instances/jobs.py:763 instances/jobs.py:808 instances/views.py:2504
#: instances/jobs.py:770 instances/jobs.py:815 instances/views.py:2504
msgid "A virtual machine with this name already exists"
msgstr "同名虚拟机已存在"
#: instances/jobs.py:765 instances/jobs.py:805
#: instances/jobs.py:772 instances/jobs.py:812
msgid "There is an instance with same name. Remove it and try again!"
msgstr "已存在同名虚拟机。请删除后重试!"
#: instances/jobs.py:768
#: instances/jobs.py:775
msgid "First you need to create or select an image"
msgstr "请先创建或选择一个镜像"
#: instances/jobs.py:770
#: instances/jobs.py:777
msgid "Choose one disk source: a new disk, existing volumes or a template"
msgstr "请选择一种磁盘来源:新磁盘、现有卷或模板"
#: instances/jobs.py:772
#: instances/jobs.py:779
msgid "Image has already exist. Please check volumes or change instance name"
msgstr "镜像已存在。请检查卷或更改虚拟机名称"
#: instances/jobs.py:774
#: instances/jobs.py:781
msgid "Invalid cache mode"
msgstr "无效的缓存模式"
#: instances/jobs.py:776
#: instances/jobs.py:783
#, python-format
msgid "This host offers no CPU mode or model %(mode)s"
msgstr "此主机不提供 CPU 模式或型号 %(mode)s"
#: instances/jobs.py:778
#: instances/jobs.py:785
#, python-format
msgid "A CD-ROM cannot use the %(bus)s bus"
msgstr "CD-ROM 不能使用 %(bus)s 总线"
#: instances/jobs.py:781
#: instances/jobs.py:788
msgid "The cloud-init seed is read from a CD-ROM: choose its bus"
msgstr "cloud-init 种子从 CD-ROM 读取:请选择其总线"
#: instances/jobs.py:784
#: instances/jobs.py:791
msgid "The cloud-init seed needs a directory storage pool for the disk"
msgstr "cloud-init 种子需要为磁盘使用目录类型的存储池"
#: instances/jobs.py:790
#: instances/jobs.py:797
#, python-format
msgid "This host offers no firmware %(firmware)s"
msgstr "此主机不提供固件 %(firmware)s"
#: instances/jobs.py:792 vrtManager/instance.py:3023
#: instances/jobs.py:799 vrtManager/instance.py:3023
msgid "This host offers no TPM 2.0: install swtpm on it"
msgstr "此主机不提供 TPM 2.0:请在主机上安装 swtpm"
#: instances/jobs.py:853
#: instances/jobs.py:860
msgid "Writing the cloud-init seed"
msgstr "正在写入 cloud-init 种子"
#: instances/jobs.py:860
#: instances/jobs.py:867
msgid "Defining the VM"
msgstr "正在定义虚拟机"
#: instances/jobs.py:893
#: instances/jobs.py:900
#, python-format
msgid "%(error)s. Remove these volumes by hand: %(volumes)s"
msgstr "%(error)s。请手动删除这些卷:%(volumes)s"
#: instances/jobs.py:918
#: instances/jobs.py:925
#, python-format
msgid "The instance is already on %(compute)s"
msgstr "虚拟机已在 %(compute)s 上"
#: instances/jobs.py:920
#: instances/jobs.py:927
#, python-format
msgid "%(compute)s is not reachable"
msgstr "无法连接 %(compute)s"
#: instances/jobs.py:922
#: instances/jobs.py:929
#, python-format
msgid "%(compute)s is the VM's own host under another name"
msgstr "%(compute)s 是该虚拟机所在主机的另一个名称"
#: instances/jobs.py:940
#: instances/jobs.py:947
msgid "Migrate VM"
msgstr "迁移虚拟机"
#: instances/jobs.py:984
#: instances/jobs.py:991
msgid "Migrating the VM"
msgstr "正在迁移虚拟机"
#: instances/jobs.py:1031
#: instances/jobs.py:1038
msgid "libvirt is still migrating the VM"
msgstr "libvirt 仍在迁移虚拟机"
#: instances/jobs.py:1038
#: instances/jobs.py:1045
msgid "The VM stayed on the source host"
msgstr "虚拟机留在了源主机上"
#: instances/jobs.py:1040
#: instances/jobs.py:1047
msgid ""
"The VM is on both computes or on neither; check them, then resolve this job."
msgstr "虚拟机同时位于两个计算节点上,或都不在;请检查后处理此任务。"
#: instances/jobs.py:1088
#: instances/jobs.py:1095
msgid "Move disk"
msgstr "移动磁盘"
#: instances/jobs.py:1095
#: instances/jobs.py:1102
msgid "Choose a disk of the VM"
msgstr "请选择虚拟机的一个磁盘"
#: instances/jobs.py:1119
#: instances/jobs.py:1126
msgid "Copying the disk"
msgstr "正在复制磁盘"
#: instances/jobs.py:1162
#: instances/jobs.py:1169
#, python-format
msgid "Another block job runs on %(dev)s; check the VM"
msgstr "%(dev)s 上正在运行另一个块任务;请检查虚拟机"
#: instances/jobs.py:1168
#: instances/jobs.py:1175
#, python-format
msgid "The disk stayed in %(path)s; remove %(dest)s if it is there"
msgstr "磁盘仍在 %(path)s;如果 %(dest)s 存在,请将其删除"
#: instances/jobs.py:1170
#: instances/jobs.py:1177
#, python-format
msgid "The definitions of %(dev)s name %(paths)s; check the VM"
msgstr "%(dev)s 的定义指向 %(paths)s;请检查虚拟机"