diff --git a/instances/jobs.py b/instances/jobs.py index c7da6194..fc40d29e 100644 --- a/instances/jobs.py +++ b/instances/jobs.py @@ -930,11 +930,15 @@ def migration_target(instance, target_pk): return target +def _connect(compute_pk): + c = Compute.objects.get(pk=compute_pk) + return wvmConnect(c.hostname, c.login, c.password, c.type) + + def _domain(compute_pk, uuid): """The VM on the compute, or None when it has none there.""" - c = Compute.objects.get(pk=compute_pk) try: - return wvmConnect(c.hostname, c.login, c.password, c.type).wvm.lookupByUUIDString(uuid) + return _connect(compute_pk).wvm.lookupByUUIDString(uuid) except libvirtError as err: if err.get_error_code() != VIR_ERR_NO_DOMAIN: raise # unreachable: recovery waits and retries @@ -947,6 +951,25 @@ def _shut_off_again(op, dom): dom.destroy() +def _moved(op, dest): + """What migrate_instance does once libvirt moved the VM, for a job that + stopped before it: autostart is per host, and the source keeps the + NVRAM file. The job's result, with a warning for a step that failed.""" + notes = [] + if op.intent.get("autostart"): + try: + dest.setAutostart(1) + except libvirtError as err: + notes += say(_("The VM migrated, but autostart could not be set on the destination: %(error)s"), + error=err) + if nvram := op.intent.get("nvram"): + try: + _connect(op.intent["compute"]).remove_nvram(nvram, _connect(op.params["target"])) + except libvirtError as err: + notes += say(_("The VM migrated, but its NVRAM file stays on the source: %(error)s"), error=err) + return {"warning": notes} if notes else {} + + @register class MigrateInstance(Kind): name = "instance.migrate" @@ -993,8 +1016,10 @@ class MigrateInstance(Kind): def run(self, op, ctx): p = op.params instance = _instance(p["instance"]) - # a shut-off VM may migrate started paused (migrate_running): recovery shuts it off again - ctx.record_intent(shut_off=instance.proxy.get_status() == 5) + # a shut-off VM may migrate started paused (migrate_running): recovery shuts it off again; + # it also does what migrate_instance does after the move (_moved) + proxy = instance.proxy + ctx.record_intent(shut_off=proxy.get_status() == 5, autostart=proxy.get_autostart(), nvram=proxy.get_nvram()) ctx.set_phase(gettext_noop("Migrating the VM")) try: utils.migrate_instance(Compute.objects.get(pk=p["target"]), instance, op.user, @@ -1048,7 +1073,7 @@ class MigrateInstance(Kind): if instance is not None: # the job stopped before it moved the record utils.record_migration(instance, Compute.objects.get(pk=op.params["target"])) _shut_off_again(op, dest) - return Outcome(Operation.SUCCEEDED) + return Outcome(Operation.SUCCEEDED, result=_moved(op, dest)) if source is not None and dest is None: _shut_off_again(op, source) return Outcome(Operation.FAILED, error=say(_("The VM stayed on the source host"))) diff --git a/instances/tests/test_migrate_job.py b/instances/tests/test_migrate_job.py index 0c98f81c..96e018dd 100644 --- a/instances/tests/test_migrate_job.py +++ b/instances/tests/test_migrate_job.py @@ -100,6 +100,27 @@ class MigrateJobTestCase(TransactionTestCase): MigrateInstance().reconcile(op) dom.destroy.assert_not_called() + def test_recovery_of_a_moved_vm_sets_its_autostart_and_removes_the_source_nvram(self): + self.vm.proxy.get_autostart.return_value = True + self.vm.proxy.get_nvram.return_value = "/var/lib/libvirt/qemu/nvram/mig-vm_VARS.fd" + op = self.queue() + ctx = self.start(op) + self.dest_conn.moveto.side_effect = OSError("the worker stops here") + with self.assertRaises(OSError): + get_kind(op.kind).run(op, ctx) + dom = MagicMock() + with patch("instances.jobs._domain", side_effect=[None, dom]), patch("instances.jobs._connect") as connect: + outcome = MigrateInstance().reconcile(op) + self.assertEqual((outcome.state, outcome.result), (Operation.SUCCEEDED, {})) + dom.setAutostart.assert_called_once_with(1) + connect.return_value.remove_nvram.assert_called_once_with( + "/var/lib/libvirt/qemu/nvram/mig-vm_VARS.fd", connect.return_value) + dom.setAutostart.side_effect = libvirtError("refused") + with patch("instances.jobs._domain", side_effect=[None, dom]), patch("instances.jobs._connect"): + outcome = MigrateInstance().reconcile(op) + self.assertEqual(outcome.state, Operation.SUCCEEDED) + self.assertIn("autostart could not be set", outcome.result["warning"][0]["text"]) + def test_a_copy_claims_its_volume_on_both_computes(self): Instance.proxy.get_disk_devices.return_value = [ {"dev": "vda", "storage": "default", "image": "mig-vm.qcow2", "path": "/i/mig-vm.qcow2"}, diff --git a/locale/tr/LC_MESSAGES/django.mo b/locale/tr/LC_MESSAGES/django.mo index c4f6e855..ef6aa6fd 100644 Binary files a/locale/tr/LC_MESSAGES/django.mo and b/locale/tr/LC_MESSAGES/django.mo differ diff --git a/locale/tr/LC_MESSAGES/django.po b/locale/tr/LC_MESSAGES/django.po index 12805bfe..b9b6285b 100644 --- a/locale/tr/LC_MESSAGES/django.po +++ b/locale/tr/LC_MESSAGES/django.po @@ -3018,53 +3018,65 @@ msgstr "%(compute)s sunucusuna ulaşılamıyor" 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:947 +#: instances/jobs.py:963 +#, python-format +msgid "" +"The VM migrated, but autostart could not be set on the destination: %(error)s" +msgstr "" +"Sanal makine taşındı, ancak hedefte otomatik başlatma ayarlanamadı: %(error)s" + +#: instances/jobs.py:969 +#, python-format +msgid "The VM migrated, but its NVRAM file stays on the source: %(error)s" +msgstr "Sanal makine taşındı, ancak NVRAM dosyası kaynakta kaldı: %(error)s" + +#: instances/jobs.py:976 msgid "Migrate VM" msgstr "Sanal makineyi taşı" -#: instances/jobs.py:991 +#: instances/jobs.py:1023 msgid "Migrating the VM" msgstr "Sanal makine taşınıyor" -#: instances/jobs.py:1038 +#: instances/jobs.py:1070 msgid "libvirt is still migrating the VM" msgstr "libvirt sanal makineyi hâlâ taşıyor" -#: instances/jobs.py:1045 +#: instances/jobs.py:1079 msgid "The VM stayed on the source host" msgstr "Sanal makine kaynak sunucuda kaldı" -#: instances/jobs.py:1047 +#: instances/jobs.py:1081 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:1095 +#: instances/jobs.py:1129 msgid "Move disk" msgstr "Diski taşı" -#: instances/jobs.py:1102 +#: instances/jobs.py:1136 msgid "Choose a disk of the VM" msgstr "Sanal makinenin bir diskini seçin" -#: instances/jobs.py:1126 +#: instances/jobs.py:1160 msgid "Copying the disk" msgstr "Disk kopyalanıyor" -#: instances/jobs.py:1169 +#: instances/jobs.py:1203 #, 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:1175 +#: instances/jobs.py:1209 #, 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:1177 +#: instances/jobs.py:1211 #, 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" diff --git a/locale/zh/LC_MESSAGES/django.mo b/locale/zh/LC_MESSAGES/django.mo index c684f630..e12ef6f0 100644 Binary files a/locale/zh/LC_MESSAGES/django.mo and b/locale/zh/LC_MESSAGES/django.mo differ diff --git a/locale/zh/LC_MESSAGES/django.po b/locale/zh/LC_MESSAGES/django.po index 61f1a205..923ce01b 100644 --- a/locale/zh/LC_MESSAGES/django.po +++ b/locale/zh/LC_MESSAGES/django.po @@ -2898,50 +2898,61 @@ msgstr "无法连接 %(compute)s" msgid "%(compute)s is the VM's own host under another name" msgstr "%(compute)s 是该虚拟机所在主机的另一个名称" -#: instances/jobs.py:947 +#: instances/jobs.py:963 +#, python-format +msgid "" +"The VM migrated, but autostart could not be set on the destination: %(error)s" +msgstr "虚拟机已迁移,但无法在目标主机上设置自动启动:%(error)s" + +#: instances/jobs.py:969 +#, python-format +msgid "The VM migrated, but its NVRAM file stays on the source: %(error)s" +msgstr "虚拟机已迁移,但其 NVRAM 文件仍留在源主机上:%(error)s" + +#: instances/jobs.py:976 msgid "Migrate VM" msgstr "迁移虚拟机" -#: instances/jobs.py:991 +#: instances/jobs.py:1023 msgid "Migrating the VM" msgstr "正在迁移虚拟机" -#: instances/jobs.py:1038 +#: instances/jobs.py:1070 msgid "libvirt is still migrating the VM" msgstr "libvirt 仍在迁移虚拟机" -#: instances/jobs.py:1045 +#: instances/jobs.py:1079 msgid "The VM stayed on the source host" msgstr "虚拟机留在了源主机上" -#: instances/jobs.py:1047 +#: instances/jobs.py:1081 msgid "" "The VM is on both computes or on neither; check them, then resolve this job." msgstr "虚拟机同时位于两个计算节点上,或都不在;请检查后处理此任务。" -#: instances/jobs.py:1095 +#: instances/jobs.py:1129 msgid "Move disk" msgstr "移动磁盘" -#: instances/jobs.py:1102 +#: instances/jobs.py:1136 msgid "Choose a disk of the VM" msgstr "请选择虚拟机的一个磁盘" -#: instances/jobs.py:1126 +#: instances/jobs.py:1160 msgid "Copying the disk" msgstr "正在复制磁盘" -#: instances/jobs.py:1169 +#: instances/jobs.py:1203 #, python-format msgid "Another block job runs on %(dev)s; check the VM" msgstr "%(dev)s 上正在运行另一个块任务;请检查虚拟机" -#: instances/jobs.py:1175 +#: instances/jobs.py:1209 #, python-format msgid "The disk stayed in %(path)s; remove %(dest)s if it is there" msgstr "磁盘仍在 %(path)s;如果 %(dest)s 存在,请将其删除" -#: instances/jobs.py:1177 +#: instances/jobs.py:1211 #, python-format msgid "The definitions of %(dev)s name %(paths)s; check the VM" msgstr "%(dev)s 的定义指向 %(paths)s;请检查虚拟机"