diff --git a/admin/tests/test_tombstones.py b/admin/tests/test_tombstones.py index 11e793ff..cce11668 100644 --- a/admin/tests/test_tombstones.py +++ b/admin/tests/test_tombstones.py @@ -8,6 +8,7 @@ from django.urls import reverse from django.utils import timezone from instances.models import InstanceTombstone +from logs.models import Logs UUID = "00000000-0000-4000-8000-000000000001" @@ -57,3 +58,6 @@ class TombstoneTests(TestCase): self.assertRedirects(response, reverse("admin:tombstone_list")) self.assertContains(response, "Removed VM ts-vm deleted") self.assertFalse(InstanceTombstone.objects.exists()) + # the VM's history (found by UUID) has it + self.assertEqual(list(Logs.objects.filter(message__startswith="Removed VM").values_list("host", "instance", "instance_uuid")), + [("ts-host", "ts-vm", UUID)]) diff --git a/admin/views.py b/admin/views.py index 998ee82e..cf32fe92 100644 --- a/admin/views.py +++ b/admin/views.py @@ -343,7 +343,8 @@ def tombstone_list(request): def tombstone_delete(request, pk): tombstone = get_object_or_404(InstanceTombstone, pk=pk) if request.method == "POST": - with audit(request, None, f"Removed VM {tombstone.name} ({tombstone.uuid})", "delete"): + with audit(request, tombstone.host or None, f"Removed VM {tombstone.name} ({tombstone.uuid})", "delete", + instance=tombstone.name, uuid=tombstone.uuid): tombstone.delete() messages.success(request, _("Removed VM %(name)s deleted") % {"name": tombstone.name}) return redirect("admin:tombstone_list") diff --git a/computes/tests/test_ownership.py b/computes/tests/test_ownership.py index 047fa02c..a074cdf3 100644 --- a/computes/tests/test_ownership.py +++ b/computes/tests/test_ownership.py @@ -109,7 +109,7 @@ class OwnershipTestCase(TestCase): self.assertEqual(self.ownership(self.host_a), (False, [])) self.assertFalse(InstanceTombstone.objects.exists()) - self.assertIn(("-", "-", f"VM vm ({UUID}): removed record expired"), self.rows()) + self.assertIn(("-", "host-a", f"VM vm ({UUID}): removed record expired"), self.rows()) @override_settings(INSTANCE_OWNERSHIP_RETENTION_DAYS=30) def test_a_removed_record_expires_while_nothing_changes(self): @@ -120,7 +120,9 @@ class OwnershipTestCase(TestCase): refresh_instance_database(self.host_b) # its VMs match the database self.assertFalse(InstanceTombstone.objects.exists()) - self.assertIn(("-", "-", f"VM vm ({UUID}): removed record expired"), self.rows()) + # the VM's history (found by UUID) has it + self.assertTrue(Logs.objects.filter(instance_uuid=UUID, instance="vm", message__endswith="expired").exists()) + self.assertIn(("-", "host-a", f"VM vm ({UUID}): removed record expired"), self.rows()) def test_deleted_users_are_skipped(self): self.owned_vm(self.host_a) diff --git a/computes/utils.py b/computes/utils.py index 2efd7393..c7605832 100644 --- a/computes/utils.py +++ b/computes/utils.py @@ -212,7 +212,7 @@ def refresh_instance_database(compute): # ownership is restored. expired = InstanceTombstone.objects.filter(removed__lt=cutoff) for t in expired: - write("-", "-", "", f"VM {t.name} ({t.uuid}): removed record expired", True) + write("-", t.host or "-", t.name, f"VM {t.name} ({t.uuid}): removed record expired", True, uuid=t.uuid) expired.delete() for uuid, name in host_domains.items(): if uuid not in db_uuids: diff --git a/instances/jobs.py b/instances/jobs.py index 1956a0bf..064c9747 100644 --- a/instances/jobs.py +++ b/instances/jobs.py @@ -225,6 +225,8 @@ def _finish_clone(op, clone, warning): proxy.start() except libvirtError as err: notes += say(_("The clone is made, but not as asked: %(error)s"), error=err) + write(op.username, c.name, op.intent["name"], f"VM {op.intent['name']}: resize or start the clone: " + f"{safe_text(err)}", False, uuid=op.intent["uuid"]) return {"warning": notes} if notes else {} @@ -966,17 +968,25 @@ def _moved(op, dest): 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 = [] + source, _sep, name = op.resource_label.rpartition("/") + target = Compute.objects.get(pk=op.params["target"]).name + + def failed(action, err): # as migrate_instance logs its later steps + write(op.username, source, name, f"VM {name}: {action}: {safe_text(err)}", False, uuid=op.intent["uuid"]) + 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) + failed(f"restore autostart on {target}", 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) + failed(f"remove NVRAM file {nvram} on the source", err) return {"warning": notes} if notes else {} @@ -1034,8 +1044,13 @@ class MigrateInstance(Kind): try: utils.migrate_instance(Compute.objects.get(pk=p["target"]), instance, op.user, disk_pools=p["disk_pools"], + on_moved=lambda: ctx.record_intent(outcome_logged=True), **{k: p[k] for k in MIGRATE_OPTIONS}) - except libvirtError as err: + except Exception as err: + # migrate_instance wrote the error's Logs row; recovery may still find the VM moved + ctx.record_intent(failure_logged=True) + if not isinstance(err, libvirtError): + raise op.refresh_from_db(fields=["cancel_requested"]) if op.cancel_requested: raise Cancelled from None @@ -1045,15 +1060,13 @@ class MigrateInstance(Kind): if isinstance(err, util.OperationError): raise raise util.OperationError(str(err)) from err - finally: - # migrate_instance wrote the outcome's Logs row (log_entry) - ctx.record_intent(outcome_logged=True) return {} def log_entry(self, op, state): """A row for an end migrate_instance did not log: refused before it - ran, cancelled while queued, or ended by recovery.""" - if op.intent.get("outcome_logged"): + ran, cancelled while queued, or ended by recovery other than the + failure migrate_instance logged.""" + if op.intent.get("outcome_logged") or (op.intent.get("failure_logged") and state == Operation.FAILED): return None target = Compute.objects.filter(pk=op.params["target"]).values_list("name", flat=True).first() return _log(op, state, f"migrate to {target}") diff --git a/instances/tests/test_clone_job.py b/instances/tests/test_clone_job.py index bbcd6eeb..dd9d486c 100644 --- a/instances/tests/test_clone_job.py +++ b/instances/tests/test_clone_job.py @@ -17,6 +17,7 @@ from appsettings.settings import get_settings from computes.models import Compute from instances.jobs import CloneInstance from instances.models import Instance +from logs.models import Logs from operations.utils import words from operations.jobs import Cancelled, Context from vrtManager.util import ConnectionFailed, OperationError @@ -148,6 +149,9 @@ class CloneJobTestCase(TransactionTestCase): op, result = self.clone_with(**{"clone-start": "true"}) self.assertTrue(Instance.objects.filter(uuid=op.intent["uuid"]).exists()) self.assertIn("The clone is made, but not as asked", words(result["warning"])) + # the job succeeded: Logs has the step that failed too + self.assertTrue(Logs.objects.filter(instance_uuid=op.intent["uuid"], success=False, + message="VM copy: resize or start the clone: no memory").exists()) def queued(self): self.client.post(reverse("instances:clone", args=[self.source.pk]), diff --git a/instances/tests/test_migrate_job.py b/instances/tests/test_migrate_job.py index 8c72d25e..f528fcd2 100644 --- a/instances/tests/test_migrate_job.py +++ b/instances/tests/test_migrate_job.py @@ -11,6 +11,7 @@ from appsettings.settings import get_settings from computes.models import Compute from instances.jobs import MigrateInstance from instances.models import Instance +from logs.models import Logs from libvirt import VIR_DOMAIN_XML_INACTIVE, VIR_ERR_SYSTEM_ERROR, VIR_FROM_RPC, libvirtError from operations.jobs import Cancelled, Context, get_kind from operations.models import Claim, Operation, acquire @@ -124,6 +125,8 @@ class MigrateJobTestCase(TransactionTestCase): outcome = MigrateInstance().reconcile(op) self.assertEqual(outcome.state, Operation.SUCCEEDED) self.assertIn("autostart could not be set", outcome.result["warning"][0]["text"]) + self.assertTrue(Logs.objects.filter(instance_uuid=UUID, success=False, + message="VM mig-vm: restore autostart on mig-dst: refused").exists()) def test_a_copy_claims_its_volume_on_both_computes(self): Instance.proxy.get_disk_devices.return_value = [ @@ -261,8 +264,6 @@ class MigrateJobTestCase(TransactionTestCase): proxy.return_value.instance.abortJob.assert_not_called() def test_each_end_of_a_migration_leaves_one_logs_row(self): - from logs.models import Logs - op = self.queue(live_migrate="true") ctx = self.start(op) get_kind(op.kind).run(op, ctx) # migrate_instance writes its row @@ -275,6 +276,29 @@ class MigrateJobTestCase(TransactionTestCase): self.assertEqual(MigrateInstance().log_entry(queued, Operation.CANCELLED), ("mig-src", "mig-vm", UUID, "VM mig-vm: migrate to mig-dst (cancelled)")) + def test_a_failed_migration_recovery_finds_moved_has_a_row_of_that_end(self): + op = self.queue(live_migrate="true") + ctx = self.start(op) + self.dest_conn.moveto.side_effect = libvirtError("connection reset while migrating") + with self.assertRaises(Exception): + get_kind(op.kind).run(op, ctx) # migrate_instance writes the failed row + op.refresh_from_db() + self.assertIsNone(MigrateInstance().log_entry(op, Operation.FAILED)) # that row is the end + for state, message in ((Operation.SUCCEEDED, "VM mig-vm: migrate to mig-dst"), + (Operation.UNCERTAIN, "VM mig-vm: migrate to mig-dst (uncertain)")): + with self.subTest(state): + self.assertEqual(MigrateInstance().log_entry(op, state)[3], message) + + def test_a_migration_moved_then_failed_to_record_has_its_row_once(self): + op = self.queue(live_migrate="true") + ctx = self.start(op) + with patch("instances.utils.record_migration", side_effect=RuntimeError("database locked")), \ + self.assertRaises(RuntimeError): + get_kind(op.kind).run(op, ctx) # the migration's row, then the failed record's + op.refresh_from_db() + self.assertIsNone(MigrateInstance().log_entry(op, Operation.SUCCEEDED)) # recovery finds it moved + self.assertEqual(Logs.objects.filter(instance_uuid=UUID, message__startswith="VM mig-vm: migrate (").count(), 1) + def test_recording_a_migration_holds_the_ownership_lock(self): # a sync of a third compute must not move the owners at the same time from instances import utils diff --git a/instances/utils.py b/instances/utils.py index 4c94d317..bf5c9d92 100644 --- a/instances/utils.py +++ b/instances/utils.py @@ -214,13 +214,14 @@ def migrate_instance( compress=False, delete_source=False, disk_pools=None, + on_moved=None, ): """Migrates instance to new_compute and logs the outcome (the mode used, or the error), then each later step that failed in a row of its - own. disk_pools: {target: pool} of the disks copied into pools of - new_compute (moveto's pools); their source files go with delete_source - unless another VM or volume uses them, and are named in the log - otherwise. Returns the mode.""" + own; on_moved is called once the outcome's row is written. disk_pools: + {target: pool} of the disks copied into pools of new_compute (moveto's + pools); their source files go with delete_source unless another VM or + volume uses them, and are named in the log otherwise. Returns the mode.""" source, target = instance.compute, migration_target(new_compute) def log(action, error=None): @@ -259,6 +260,8 @@ def migrate_instance( # The VM runs on new_compute now: what follows must not stop it # from being recorded there. log(f"migrate ({mode}) to {target}") + if on_moved is not None: # its row is written: a later step that raises does not undo it + on_moved() if autostart: conn_new = None try: diff --git a/locale/tr/LC_MESSAGES/django.mo b/locale/tr/LC_MESSAGES/django.mo index 9c88723c..f788e810 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 15e206a3..3482fd1e 100644 --- a/locale/tr/LC_MESSAGES/django.po +++ b/locale/tr/LC_MESSAGES/django.po @@ -11,7 +11,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-11-06 21:10+0000\n" +"POT-Creation-Date: 2026-11-07 11:22+0000\n" "PO-Revision-Date: 2020-11-04 12:00+0000\n" "Last-Translator: catborise , 2020\n" "Language-Team: Turkish (https://www.transifex.com/catborise/teams/110663/tr/)\n" @@ -21,7 +21,7 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: accounts/auth.py:22 +#: accounts/auth.py:22 accounts/middleware.py:28 msgid "Change your password before using the API." msgstr "API'yi kullanmadan önce parolanızı değiştirin." @@ -1157,12 +1157,12 @@ msgstr "" msgid "deleted user" msgstr "silinmiş kullanıcı" -#: admin/views.py:348 +#: admin/views.py:349 #, python-format msgid "Removed VM %(name)s deleted" msgstr "Kaldırılan sanal makine %(name)s silindi" -#: admin/views.py:355 +#: admin/views.py:356 msgid "Its owners no longer get the VM back if its UUID shows up again." msgstr "" "UUID'si yeniden görünse bile sahipleri sanal makineyi artık geri almaz." @@ -2750,7 +2750,7 @@ msgstr "Klonun adını boş adlar listesinden seçin" msgid "%(quota)s: cannot create '%(clone_name)s'." msgstr "%(quota)s: '%(clone_name)s' oluşturulamıyor." -#: instances/jobs.py:200 instances/jobs.py:552 +#: instances/jobs.py:200 instances/jobs.py:554 #, python-format msgid "Instance '%(clone_name)s' already exists!" msgstr "'%(clone_name)s' sanal makinesi zaten mevcut!" @@ -2775,11 +2775,11 @@ msgstr "Disk adı '%(disk_name)s' geçersiz karakterler içeriyor!" msgid "The clone is made, but not as asked: %(error)s" msgstr "Klon oluşturuldu, ama istendiği gibi değil: %(error)s" -#: instances/jobs.py:283 +#: instances/jobs.py:285 msgid "The VM was not defined and no volume of it was left." msgstr "Sanal makine tanımlanmadı ve ondan geriye birim kalmadı." -#: instances/jobs.py:285 +#: instances/jobs.py:287 #, python-format msgid "" "The VM was not defined; check these volumes, then resolve this job: " @@ -2788,28 +2788,28 @@ msgstr "" "Sanal makine tanımlanmadı; şu birimleri kontrol edin, sonra bu işi çözün: " "%(volumes)s" -#: instances/jobs.py:306 +#: instances/jobs.py:308 msgid "Delete snapshot" msgstr "Anlık görüntüyü sil" -#: instances/jobs.py:316 vrtManager/instance.py:2287 +#: instances/jobs.py:318 vrtManager/instance.py:2288 #, python-format msgid "The VM has no external snapshot named %(name)s" msgstr "Sanal makinede %(name)s adında harici anlık görüntü yok" -#: instances/jobs.py:329 +#: instances/jobs.py:331 msgid "Merging the snapshot" msgstr "Anlık görüntü birleştiriliyor" -#: instances/jobs.py:348 +#: instances/jobs.py:350 msgid "The VM is no longer on its compute" msgstr "Sanal makine artık kendi sunucusunda değil" -#: instances/jobs.py:351 +#: instances/jobs.py:353 msgid "libvirt is still merging the snapshot" msgstr "libvirt anlık görüntüyü hâlâ birleştiriyor" -#: instances/jobs.py:359 +#: instances/jobs.py:361 msgid "" "The snapshot is still listed and no merge runs. It cannot be told whether the" " disks changed; check the VM before deleting the snapshot again or reverting " @@ -2819,44 +2819,44 @@ msgstr "" "değişmediği bilinemiyor; anlık görüntüyü yeniden silmeden ya da ona dönmeden " "önce sanal makineyi kontrol edin." -#: instances/jobs.py:366 +#: instances/jobs.py:368 msgid "The VM is no longer on the compute the job ran on" msgstr "Sanal makine artık işin çalıştığı sunucuda değil" -#: instances/jobs.py:372 +#: instances/jobs.py:374 msgid "Take snapshot" msgstr "Anlık görüntü al" -#: instances/jobs.py:384 instances/jobs.py:393 vrtManager/instance.py:1883 +#: instances/jobs.py:386 instances/jobs.py:395 vrtManager/instance.py:1884 #, python-format msgid "A snapshot named %(name)s already exists" msgstr "%(name)s adında bir anlık görüntü zaten var" -#: instances/jobs.py:399 +#: instances/jobs.py:401 msgid "Taking the snapshot" msgstr "Anlık görüntü alınıyor" -#: instances/jobs.py:412 +#: instances/jobs.py:414 msgid "libvirt is still taking the snapshot" msgstr "libvirt anlık görüntüyü hâlâ alıyor" -#: instances/jobs.py:415 +#: instances/jobs.py:417 msgid "The snapshot was not taken" msgstr "Anlık görüntü alınmadı" -#: instances/jobs.py:438 +#: instances/jobs.py:440 msgid "Revert snapshot" msgstr "Snapshota dön" -#: instances/jobs.py:469 +#: instances/jobs.py:471 msgid "Reverting the VM" msgstr "Sanal makine geri döndürülüyor" -#: instances/jobs.py:480 +#: instances/jobs.py:482 msgid "libvirt is still reverting the VM" msgstr "libvirt sanal makineyi hâlâ geri döndürüyor" -#: instances/jobs.py:484 +#: instances/jobs.py:486 msgid "" "It cannot be told whether the VM was reverted; check it, then revert again or" " resolve this job." @@ -2864,12 +2864,12 @@ msgstr "" "Sanal makinenin geri döndürülüp döndürülmediği bilinemiyor; kontrol edin, " "sonra yeniden geri döndürün ya da bu işi çözün." -#: instances/jobs.py:501 +#: instances/jobs.py:503 msgid "The VM must be running, paused or shut off to be cloned" msgstr "" "Klonlanmak için sanal makine çalışıyor, duraklatılmış ya da kapalı olmalı" -#: instances/jobs.py:502 +#: instances/jobs.py:504 #, python-format msgid "" "The guest's file systems could not be thawed (%(error)s); thaw them with: " @@ -2878,19 +2878,19 @@ msgstr "" "Konuğun dosya sistemleri çözülemedi (%(error)s); şununla çözün: virsh " "domfsthaw %(vm)s" -#: instances/jobs.py:521 +#: instances/jobs.py:523 msgid "Clone VM" msgstr "Sanal makineyi klonla" -#: instances/jobs.py:562 instances/jobs.py:569 instances/jobs.py:847 +#: instances/jobs.py:564 instances/jobs.py:571 instances/jobs.py:849 msgid "Copying disks" msgstr "Diskler kopyalanıyor" -#: instances/jobs.py:621 +#: instances/jobs.py:623 msgid "libvirt has no result of the copy" msgstr "libvirt'te kopyanın sonucu yok" -#: instances/jobs.py:632 +#: instances/jobs.py:634 #, python-format msgid "" "The copy failed: %(error)s. The guest's file systems could not be thawed " @@ -2899,11 +2899,11 @@ msgstr "" "Kopyalama başarısız oldu: %(error)s. Konuğun dosya sistemleri çözülemedi " "(%(thaw)s); şununla çözün: virsh domfsthaw %(vm)s" -#: instances/jobs.py:635 instances/jobs.py:722 +#: instances/jobs.py:637 instances/jobs.py:724 msgid "The copy failed" msgstr "Kopyalama başarısız oldu" -#: instances/jobs.py:660 +#: instances/jobs.py:662 #, python-format msgid "" "The VM is no longer on the compute the job ran on; check these volumes, then " @@ -2912,7 +2912,7 @@ msgstr "" "Sanal makine artık işin çalıştığı sunucuda değil; şu birimleri kontrol edin, " "sonra bu işi çözün: %(volumes)s" -#: instances/jobs.py:698 +#: instances/jobs.py:700 #, python-format msgid "" "A volume of the clone was being created; check these volumes, then resolve " @@ -2921,15 +2921,15 @@ msgstr "" "Klonun bir birimi oluşturuluyordu; şu birimleri kontrol edin, sonra bu işi " "çözün: %(volumes)s" -#: instances/jobs.py:702 +#: instances/jobs.py:704 msgid "The job stopped before the copy started" msgstr "İş, kopyalama başlamadan durdu" -#: instances/jobs.py:709 +#: instances/jobs.py:711 msgid "libvirt is still copying the disks" msgstr "libvirt diskleri hâlâ kopyalıyor" -#: instances/jobs.py:712 +#: instances/jobs.py:714 #, python-format msgid "" "Another backup runs on the VM; check these volumes, then resolve this job: " @@ -2938,7 +2938,7 @@ msgstr "" "Sanal makinede başka bir yedekleme çalışıyor; şu birimleri kontrol edin, " "sonra bu işi çözün: %(volumes)s" -#: instances/jobs.py:717 +#: instances/jobs.py:719 #, python-format msgid "" "libvirt has no result of the copy; check these volumes, then resolve this " @@ -2947,7 +2947,7 @@ msgstr "" "libvirt'te kopyanın sonucu yok; şu birimleri kontrol edin, sonra bu işi " "çözün: %(volumes)s" -#: instances/jobs.py:729 +#: instances/jobs.py:731 #, python-format msgid "" "The disks were copied, but the clone could not be defined: %(error)s; check " @@ -2956,74 +2956,74 @@ msgstr "" "Diskler kopyalandı, ancak klon tanımlanamadı: %(error)s; şu birimleri kontrol" " edin, sonra bu işi çözün: %(volumes)s" -#: instances/jobs.py:752 instances/templates/create_instance_w1.html:60 +#: instances/jobs.py:754 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:776 instances/jobs.py:821 instances/views.py:2463 +#: instances/jobs.py:778 instances/jobs.py:823 instances/views.py:2463 msgid "A virtual machine with this name already exists" msgstr "Bu isimde bir sanal makine mevcut" -#: instances/jobs.py:778 instances/jobs.py:818 +#: instances/jobs.py:780 instances/jobs.py:820 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:781 +#: instances/jobs.py:783 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:783 +#: instances/jobs.py:785 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:785 +#: instances/jobs.py:787 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:787 +#: instances/jobs.py:789 msgid "Invalid cache mode" msgstr "Geçersiz önbellek modu" -#: instances/jobs.py:789 +#: instances/jobs.py:791 #, 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:791 +#: instances/jobs.py:793 #, python-format msgid "A CD-ROM cannot use the %(bus)s bus" msgstr "CD-ROM %(bus)s veri yolunu kullanamaz" -#: instances/jobs.py:794 +#: instances/jobs.py:796 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:797 +#: instances/jobs.py:799 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:803 +#: instances/jobs.py:805 #, python-format msgid "This host offers no firmware %(firmware)s" msgstr "Bu sunucu %(firmware)s firmware'ini sunmuyor" -#: instances/jobs.py:805 vrtManager/instance.py:3015 +#: instances/jobs.py:807 vrtManager/instance.py:3020 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:866 +#: instances/jobs.py:868 msgid "Writing the cloud-init seed" msgstr "cloud-init tohumu yazılıyor" -#: instances/jobs.py:873 +#: instances/jobs.py:875 msgid "Defining the VM" msgstr "Sanal makine tanımlanıyor" -#: instances/jobs.py:906 +#: instances/jobs.py:908 #, python-format msgid "" "The panel failed unexpectedly; its log has the details. Remove these volumes " @@ -3032,114 +3032,114 @@ msgstr "" "Panel beklenmedik bir hatayla karşılaştı; ayrıntılar panelin günlüğünde. Bu " "birimleri elle kaldırın: %(volumes)s" -#: instances/jobs.py:910 +#: instances/jobs.py:912 #, python-format msgid "%(error)s. Remove these volumes by hand: %(volumes)s" msgstr "%(error)s. Şu birimleri elle silin: %(volumes)s" -#: instances/jobs.py:935 +#: instances/jobs.py:937 #, python-format msgid "The instance is already on %(compute)s" msgstr "Sanal makine zaten %(compute)s üzerinde" -#: instances/jobs.py:937 +#: instances/jobs.py:939 #, python-format msgid "%(compute)s is not reachable" msgstr "%(compute)s sunucusuna ulaşılamıyor" -#: instances/jobs.py:939 +#: instances/jobs.py:941 #, 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:973 +#: instances/jobs.py:981 #, 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:979 +#: instances/jobs.py:988 #, 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:986 +#: instances/jobs.py:996 msgid "Migrate VM" msgstr "Sanal makineyi taşı" -#: instances/jobs.py:1033 +#: instances/jobs.py:1043 msgid "Migrating the VM" msgstr "Sanal makine taşınıyor" -#: instances/jobs.py:1080 +#: instances/jobs.py:1094 msgid "libvirt is still migrating the VM" msgstr "libvirt sanal makineyi hâlâ taşıyor" -#: instances/jobs.py:1091 +#: instances/jobs.py:1105 msgid "The VM stayed on the source host" msgstr "Sanal makine kaynak sunucuda kaldı" -#: instances/jobs.py:1093 +#: instances/jobs.py:1107 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:1141 +#: instances/jobs.py:1155 msgid "Move disk" msgstr "Diski taşı" -#: instances/jobs.py:1148 +#: instances/jobs.py:1162 msgid "Choose a disk of the VM" msgstr "Sanal makinenin bir diskini seçin" -#: instances/jobs.py:1172 +#: instances/jobs.py:1186 msgid "Copying the disk" msgstr "Disk kopyalanıyor" -#: instances/jobs.py:1215 +#: instances/jobs.py:1229 #, 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:1221 +#: instances/jobs.py:1235 #, 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:1223 +#: instances/jobs.py:1237 #, 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" -#: instances/jobs.py:1230 +#: instances/jobs.py:1244 #, python-format msgid "Disk %(path)s was kept: a disk file that stays needs it" msgstr "%(path)s diski korundu: kalan bir disk dosyası ona ihtiyaç duyuyor" -#: instances/jobs.py:1231 +#: instances/jobs.py:1245 #, python-format msgid "The VM is removed, but disk %(path)s was not deleted: %(err)s" msgstr "Sanal makine kaldırıldı, ancak %(path)s diski silinmedi: %(err)s" -#: instances/jobs.py:1232 +#: instances/jobs.py:1246 #, python-format msgid "Disk %(path)s is used by another VM and was kept" msgstr "" "%(path)s diski başka bir sanal makine tarafından kullanılıyor ve korundu" -#: instances/jobs.py:1286 +#: instances/jobs.py:1300 msgid "Delete VM" msgstr "Sanal makineyi sil" -#: instances/jobs.py:1316 +#: instances/jobs.py:1330 msgid "Deleting the VM" msgstr "Sanal makine siliniyor" -#: instances/jobs.py:1328 +#: instances/jobs.py:1342 msgid "The VM was not deleted; it may have been shut off" msgstr "Sanal makine silinmedi; kapatılmış olabilir" @@ -6351,7 +6351,7 @@ msgstr "%(quota)s: '%(instance_name)s' şablon olarak kalıyor." msgid "The XML has no name" msgstr "XML'de ad yok" -#: instances/views.py:2475 webvirtcloud/middleware.py:126 +#: instances/views.py:2475 webvirtcloud/middleware.py:151 #, python-format msgid "libvirt Error - %(exception)s" msgstr "libvirt Hatası - %(exception)s" @@ -7937,7 +7937,8 @@ msgstr "" msgid "The worker stopped before the job changed anything" msgstr "Worker, iş herhangi bir şeyi değiştirmeden önce durdu" -#: storages/api/viewsets.py:203 storages/api/viewsets.py:224 +#: storages/api/viewsets.py:147 storages/api/viewsets.py:164 +#: storages/api/viewsets.py:202 storages/api/viewsets.py:223 msgid "Pool is not active" msgstr "Havuz etkin değil" @@ -8883,7 +8884,6 @@ msgstr "Bağlantı başarısız: %(host)s bağlı değil" #: vrtManager/connection.py:502 #, python-format -#| msgid "This host has no secrets." msgid "This host has no architecture %(arch)s" msgstr "Bu sunucuda %(arch)s mimarisi yok" @@ -9108,56 +9108,60 @@ msgid "Please shutdown the instance then try to enable vCPU hotplug" msgstr "" "vCPU hotplug özelliğini etkinleştirmek için önce sanal makineyi kapatın" -#: vrtManager/instance.py:1842 +#: vrtManager/instance.py:1669 +msgid "The VM has no video card" +msgstr "Sanal makinede ekran kartı yok" + +#: vrtManager/instance.py:1843 msgid "" "The VM has external snapshots; internal snapshots cannot be mixed with them" msgstr "" "Sanal makinenin harici anlık görüntüleri var; dahili anlık görüntüler " "bunlarla karıştırılamaz" -#: vrtManager/instance.py:1874 +#: vrtManager/instance.py:1875 msgid "A snapshot with memory is not quiesced; it keeps the whole guest state" msgstr "" "Bellekli bir anlık görüntüde dosya sistemleri dondurulmaz; tüm konuk durumunu" " saklar" -#: vrtManager/instance.py:1876 +#: vrtManager/instance.py:1877 msgid "Only a running VM's file systems can be frozen" msgstr "Yalnızca çalışan bir sanal makinenin dosya sistemleri dondurulabilir" -#: vrtManager/instance.py:1878 +#: vrtManager/instance.py:1879 msgid "The QEMU guest agent is not connected; it freezes the file systems" msgstr "QEMU konuk aracısı bağlı değil; dosya sistemlerini o dondurur" -#: vrtManager/instance.py:1881 +#: vrtManager/instance.py:1882 msgid "" "The VM has internal snapshots; external snapshots cannot be mixed with them" msgstr "" "Sanal makinenin dahili anlık görüntüleri var; harici anlık görüntüler " "bunlarla karıştırılamaz" -#: vrtManager/instance.py:1886 +#: vrtManager/instance.py:1887 msgid "Newer snapshots exist after the last revert; delete them first" msgstr "" "Son geri dönüşten sonra daha yeni anlık görüntüler var; önce onları silin" -#: vrtManager/instance.py:1889 +#: vrtManager/instance.py:1890 msgid "No snapshot is current; revert to a snapshot first" msgstr "Geçerli anlık görüntü yok; önce bir anlık görüntüye geri dönün" -#: vrtManager/instance.py:1895 +#: vrtManager/instance.py:1896 #, python-format msgid "Eject the media from %(dev)s first" msgstr "Önce %(dev)s içindeki ortamı çıkarın" -#: vrtManager/instance.py:1897 +#: vrtManager/instance.py:1898 #, python-format msgid "Disk %(dev)s is not a file; external snapshots need file disks" msgstr "" "Disk %(dev)s bir dosya değil; harici anlık görüntüler dosya diskleri " "gerektirir" -#: vrtManager/instance.py:1905 +#: vrtManager/instance.py:1906 #, python-format msgid "" "The storage pools could not be refreshed, so their free space is unknown: " @@ -9165,21 +9169,21 @@ msgid "" msgstr "" "Depolama havuzları yenilenemedi, bu yüzden boş alanları bilinmiyor: %(err)s" -#: vrtManager/instance.py:1926 +#: vrtManager/instance.py:1927 msgid "Memory is saved only from a running VM" msgstr "Bellek yalnızca çalışan bir sanal makineden kaydedilir" -#: vrtManager/instance.py:1928 +#: vrtManager/instance.py:1929 msgid "The VM has no disk to keep its memory next to" msgstr "Sanal makinenin, belleğinin yanına kaydedileceği bir diski yok" -#: vrtManager/instance.py:1932 +#: vrtManager/instance.py:1933 #, python-format msgid "%(disk)s is not in a storage pool; memory is saved only into one" msgstr "" "%(disk)s bir depolama havuzunda değil; bellek yalnızca bir havuza kaydedilir" -#: vrtManager/instance.py:1963 +#: vrtManager/instance.py:1964 #, python-format msgid "" "Storage pool %(pool)s has %(free).1f GiB free; the snapshot needs %(size).1f " @@ -9190,7 +9194,7 @@ msgstr "" "%(size).1f GiB gerektiriyor (sanal makinenin oradaki disklerinin " "%%%(percent)g kadarı, en az %(minimum)g GiB, ve kaydediliyorsa bellek)" -#: vrtManager/instance.py:1981 +#: vrtManager/instance.py:1982 msgid "" "This snapshot was taken by an older version on a UEFI VM and cannot be " "reverted; delete it instead" @@ -9198,7 +9202,7 @@ msgstr "" "Bu anlık görüntü UEFI bir sanal makinede eski bir sürümle alındı ve geri " "dönülemez; bunun yerine silin" -#: vrtManager/instance.py:2011 +#: vrtManager/instance.py:2012 #, python-format msgid "" "The revert failed twice; try again in a moment (%(error)s). The VM is " @@ -9207,7 +9211,7 @@ msgstr "" "Geri dönüş iki kez başarısız oldu; biraz sonra tekrar deneyin (%(error)s). " "Sanal makine çalışıyor." -#: vrtManager/instance.py:2013 +#: vrtManager/instance.py:2014 #, python-format msgid "" "The revert failed twice; try again in a moment (%(error)s). The VM is shut " @@ -9216,13 +9220,13 @@ msgstr "" "Geri dönüş iki kez başarısız oldu; biraz sonra tekrar deneyin (%(error)s). " "Sanal makine kapalı." -#: vrtManager/instance.py:2176 +#: vrtManager/instance.py:2177 msgid "A disk moves when its VM is defined and running, paused or shut off" msgstr "" "Bir disk, sanal makinesi tanımlıyken ve çalışırken, duraklatılmışken veya " "kapalıyken taşınır" -#: vrtManager/instance.py:2178 +#: vrtManager/instance.py:2179 msgid "" "A disk of a VM with snapshots, a saved state or a running block job does not " "move" @@ -9230,7 +9234,7 @@ msgstr "" "Anlık görüntüleri, kaydedilmiş durumu veya çalışan bir blok işi olan sanal " "makinenin diski taşınmaz" -#: vrtManager/instance.py:2186 +#: vrtManager/instance.py:2187 #, python-format msgid "" "%(dev)s is not a disk file of the VM, the same in its running and next " @@ -9239,132 +9243,132 @@ msgstr "" "%(dev)s, sanal makinenin çalışan ve sonraki başlatma tanımlarında aynı olan " "bir disk dosyası değil" -#: vrtManager/instance.py:2191 +#: vrtManager/instance.py:2192 #, python-format msgid "%(dev)s does not move: only writable raw and qcow2 disks do" msgstr "%(dev)s taşınmaz: yalnızca yazılabilir raw ve qcow2 diskler taşınır" -#: vrtManager/instance.py:2198 +#: vrtManager/instance.py:2199 #, python-format msgid "%(dev)s does not move: no storage pool has %(path)s" msgstr "%(dev)s taşınmaz: hiçbir depolama havuzunda %(path)s yok" -#: vrtManager/instance.py:2200 +#: vrtManager/instance.py:2201 #, python-format msgid "%(dev)s does not move: it has a backing file" msgstr "%(dev)s taşınmaz: temel (backing) dosyası var" -#: vrtManager/instance.py:2203 +#: vrtManager/instance.py:2204 #, python-format msgid "%(pool_name)s is not an active pool of disk files of this host" msgstr "%(pool_name)s bu sunucunun etkin bir disk dosyası havuzu değil" -#: vrtManager/instance.py:2206 +#: vrtManager/instance.py:2207 #, python-format msgid "%(dev)s is in pool %(pool_name)s already" msgstr "%(dev)s zaten %(pool_name)s havuzunda" -#: vrtManager/instance.py:2210 +#: vrtManager/instance.py:2211 #, python-format msgid "Pool %(pool_name)s already has a file %(name)s" msgstr "%(pool_name)s havuzunda zaten %(name)s dosyası var" -#: vrtManager/instance.py:2234 +#: vrtManager/instance.py:2235 #, python-format msgid "The copy of %(dev)s ended before it was complete" msgstr "%(dev)s kopyası tamamlanmadan sona erdi" -#: vrtManager/instance.py:2279 +#: vrtManager/instance.py:2280 #, python-format msgid "%(error)s; remove %(dest)s if it is there: %(cleanup_error)s" msgstr "%(error)s; varsa %(dest)s dosyasını kaldırın: %(cleanup_error)s" -#: vrtManager/instance.py:2281 +#: vrtManager/instance.py:2282 #, python-format msgid "%(error)s; the VM may use %(dest)s now: check its disk %(dev)s" msgstr "" "%(error)s; sanal makine şu anda %(dest)s dosyasını kullanıyor olabilir: " "%(dev)s diskini kontrol edin" -#: vrtManager/instance.py:2288 +#: vrtManager/instance.py:2289 #, python-format msgid "The VM has no internal snapshot named %(name)s" msgstr "Sanal makinenin %(name)s adlı dahili anlık görüntüsü yok" -#: vrtManager/instance.py:2297 +#: vrtManager/instance.py:2298 msgid "The VM has a saved state; start it or remove the saved state first" msgstr "" "Sanal makinenin kaydedilmiş durumu var; önce başlatın veya kaydedilmiş durumu" " kaldırın" -#: vrtManager/instance.py:2299 +#: vrtManager/instance.py:2300 msgid "A snapshot job is still running on this VM; try again when it ends" msgstr "" "Bu sanal makinede hâlâ bir anlık görüntü işi çalışıyor; bittiğinde tekrar " "deneyin" -#: vrtManager/instance.py:2309 +#: vrtManager/instance.py:2310 msgid "Shut the VM down to revert to an internal snapshot" msgstr "Dahili bir anlık görüntüye geri dönmek için sanal makineyi kapatın" -#: vrtManager/instance.py:2407 +#: vrtManager/instance.py:2408 msgid "Cannot clone a VM whose NVRAM is not a file" msgstr "NVRAM'i dosya olmayan bir sanal makine klonlanamaz" -#: vrtManager/instance.py:2475 +#: vrtManager/instance.py:2476 #, python-format msgid "Clone failed (%(err)s); remove these copies by hand: %(path)s" msgstr "" "Klonlama başarısız oldu (%(err)s); bu kopyaları elle kaldırın: %(path)s" -#: vrtManager/instance.py:2485 +#: vrtManager/instance.py:2486 msgid "Shut the VM down before cloning it" msgstr "Klonlamadan önce sanal makineyi kapatın" -#: vrtManager/instance.py:2539 +#: vrtManager/instance.py:2540 #, python-format msgid "Clone failed (%(err)s); remove these copies by hand: %(leftovers)s" msgstr "" "Klonlama başarısız oldu (%(err)s); bu kopyaları elle kaldırın: %(leftovers)s" -#: vrtManager/instance.py:2550 +#: vrtManager/instance.py:2551 msgid "Live clone needs libvirt 11.10 or later; shut the VM down to clone it" msgstr "" "Canlı klonlama libvirt 11.10 veya sonrasını gerektirir; klonlamak için sanal " "makineyi kapatın" -#: vrtManager/instance.py:2553 +#: vrtManager/instance.py:2554 msgid "A VM with a TPM is cloned only shut down" msgstr "TPM'i olan bir sanal makine yalnızca kapalıyken klonlanır" -#: vrtManager/instance.py:2556 +#: vrtManager/instance.py:2557 #, python-format msgid "Disk %(dev)s is not a local file; shut the VM down to clone it" msgstr "" "Disk %(dev)s yerel bir dosya değil; klonlamak için sanal makineyi kapatın" -#: vrtManager/instance.py:2558 +#: vrtManager/instance.py:2559 #, python-format msgid "Disk %(dev)s is shareable; shut the VM down to clone it" msgstr "Disk %(dev)s paylaşılabilir; klonlamak için sanal makineyi kapatın" -#: vrtManager/instance.py:2563 +#: vrtManager/instance.py:2564 #, python-format msgid "Disk %(dev)s changes at the next start; shut the VM down to clone it" msgstr "" "Disk %(dev)s sonraki başlatmada değişiyor; klonlamak için sanal makineyi " "kapatın" -#: vrtManager/instance.py:2565 +#: vrtManager/instance.py:2566 msgid "Another job is running on the VM; clone it when that ends" msgstr "Sanal makinede başka bir iş çalışıyor; o bittiğinde klonlayın" -#: vrtManager/instance.py:2578 +#: vrtManager/instance.py:2579 #, python-format msgid "Volume %(name)s already exists in storage pool %(pool)s" msgstr "%(name)s birimi %(pool)s depolama havuzunda zaten var" -#: vrtManager/instance.py:2597 +#: vrtManager/instance.py:2598 #, python-format msgid "" "Storage pool %(pool)s has %(free).1f GiB free; the clone needs %(size).1f GiB" @@ -9374,20 +9378,20 @@ msgstr "" "gerektiriyor (kopyalanan diskler, artı bunların %%%(percent)g kadarı, en az " "%(minimum)g GiB)" -#: vrtManager/instance.py:2736 +#: vrtManager/instance.py:2737 msgid "A network interface that is a host device is removed in the XML editor" msgstr "Sunucu aygıtı olan bir ağ arabirimi XML düzenleyicide kaldırılır" -#: vrtManager/instance.py:2752 +#: vrtManager/instance.py:2753 #, python-format msgid "No network interface with MAC %(old_mac)s" msgstr "MAC adresi %(old_mac)s olan ağ arabirimi yok" -#: vrtManager/instance.py:2755 +#: vrtManager/instance.py:2756 msgid "A network interface that is a host device is changed in the XML editor" msgstr "Sunucu aygıtı olan bir ağ arabirimi XML düzenleyicide değiştirilir" -#: vrtManager/instance.py:2846 +#: vrtManager/instance.py:2847 msgid "" "A VM changes its PCI devices while it runs, or is shut off without a saved " "state" @@ -9395,14 +9399,14 @@ msgstr "" "Sanal makine PCI aygıtlarını çalışırken veya kaydedilmiş durumu olmadan " "kapalıyken değiştirir" -#: vrtManager/instance.py:2858 +#: vrtManager/instance.py:2859 #, python-format msgid "%(err)s; whether the VM has PCI device %(address)s now is not known" msgstr "" "%(err)s; sanal makinede şu anda %(address)s PCI aygıtının olup olmadığı " "bilinmiyor" -#: vrtManager/instance.py:2860 +#: vrtManager/instance.py:2861 #, python-format msgid "" "%(err)s; PCI device %(address)s: the VM is not running, its next start has it" @@ -9410,7 +9414,7 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: sanal makine çalışmıyor, sonraki başlatmada " "var" -#: vrtManager/instance.py:2862 +#: vrtManager/instance.py:2863 #, python-format msgid "" "%(err)s; PCI device %(address)s: the VM is not running, its next start does " @@ -9419,7 +9423,7 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: sanal makine çalışmıyor, sonraki başlatmada " "yok" -#: vrtManager/instance.py:2863 +#: vrtManager/instance.py:2864 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM has it, its next start has it" @@ -9427,7 +9431,7 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: çalışan sanal makinede var, sonraki " "başlatmada var" -#: vrtManager/instance.py:2865 +#: vrtManager/instance.py:2866 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM has it, its next start does " @@ -9436,7 +9440,7 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: çalışan sanal makinede var, sonraki " "başlatmada yok" -#: vrtManager/instance.py:2867 +#: vrtManager/instance.py:2868 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM does not have it, its next " @@ -9445,7 +9449,7 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: çalışan sanal makinede yok, sonraki " "başlatmada var" -#: vrtManager/instance.py:2869 +#: vrtManager/instance.py:2870 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM does not have it, its next " @@ -9454,29 +9458,34 @@ msgstr "" "%(err)s; PCI aygıtı %(address)s: çalışan sanal makinede yok, sonraki " "başlatmada yok" -#: vrtManager/instance.py:2903 +#: vrtManager/instance.py:2904 #, python-format msgid "The VM has no PCI device %(address)s" msgstr "Sanal makinede %(address)s PCI aygıtı yok" -#: vrtManager/instance.py:2921 +#: vrtManager/instance.py:2922 msgid "The VM has no USB controller" msgstr "Sanal makinede USB denetleyicisi yok" -#: vrtManager/instance.py:2940 +#: vrtManager/instance.py:2941 msgid "The VM has no such USB device" msgstr "Sanal makinede böyle bir USB aygıtı yok" -#: vrtManager/instance.py:2981 +#: vrtManager/instance.py:2955 +#, python-format +msgid "The VM has no network interface %(mac)s" +msgstr "Sanal makinede %(mac)s ağ arabirimi yok" + +#: vrtManager/instance.py:2986 msgid "The VM has its own RNG device: change it in the XML editor" msgstr "" "Sanal makinenin kendi RNG aygıtı var: bunu XML düzenleyicide değiştirin" -#: vrtManager/instance.py:3008 +#: vrtManager/instance.py:3013 msgid "The VM has its own TPM: change it in the XML editor" msgstr "Sanal makinenin kendi TPM'i var: bunu XML düzenleyicide değiştirin" -#: vrtManager/instance.py:3027 +#: vrtManager/instance.py:3032 #, python-format msgid "Unknown crash action %(action)s" msgstr "Bilinmeyen çökme eylemi %(action)s" @@ -9877,16 +9886,16 @@ msgstr "" "%(name)s birimi kullanımda: bir sanal makinenin diski, bir temel (backing) " "dosya ya da bir anlık görüntünün dosyası" -#: vrtManager/util.py:95 +#: vrtManager/util.py:99 #, python-format msgid "The XML is not valid: %(err)s" msgstr "XML geçerli değil: %(err)s" -#: vrtManager/util.py:97 +#: vrtManager/util.py:101 msgid "The XML may not declare a DTD" msgstr "XML bir DTD bildiremez" -#: vrtManager/util.py:143 +#: vrtManager/util.py:147 #, python-format msgid "MAC address must be of the format AA:BB:CC:DD:EE:FF, was %(mac)s" msgstr "MAC adresi AA:BB:CC:DD:EE:FF biçiminde olmalı; girilen: %(mac)s" diff --git a/locale/zh/LC_MESSAGES/django.mo b/locale/zh/LC_MESSAGES/django.mo index 91a258eb..7faa28fb 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 0995a6d3..48c66a84 100644 --- a/locale/zh/LC_MESSAGES/django.po +++ b/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-11-06 21:10+0000\n" +"POT-Creation-Date: 2026-11-07 11:22+0000\n" "PO-Revision-Date: \n" "Last-Translator: \n" "Language-Team: \n" @@ -18,7 +18,7 @@ msgstr "" "Plural-Forms: nplurals=1; plural=0;\n" "X-Generator: Poedit 3.0.1\n" -#: accounts/auth.py:22 +#: accounts/auth.py:22 accounts/middleware.py:28 msgid "Change your password before using the API." msgstr "使用 API 前请先修改密码。" @@ -1128,12 +1128,12 @@ msgstr "其对虚拟机、SSH 密钥和设置的访问将被移除;虚拟机 msgid "deleted user" msgstr "已删除的用户" -#: admin/views.py:348 +#: admin/views.py:349 #, python-format msgid "Removed VM %(name)s deleted" msgstr "已删除已移除的虚拟机 %(name)s" -#: admin/views.py:355 +#: admin/views.py:356 msgid "Its owners no longer get the VM back if its UUID shows up again." msgstr "若其 UUID 再次出现,其所有者将不再恢复对该虚拟机的所有权。" @@ -2653,7 +2653,7 @@ msgstr "请从可用名称列表中选择克隆的名称" msgid "%(quota)s: cannot create '%(clone_name)s'." msgstr "%(quota)s:无法创建“%(clone_name)s”。" -#: instances/jobs.py:200 instances/jobs.py:552 +#: instances/jobs.py:200 instances/jobs.py:554 #, python-format msgid "Instance '%(clone_name)s' already exists!" msgstr "虚拟机 '%(clone_name)s' 已存在!" @@ -2678,339 +2678,339 @@ msgstr "磁盘名称 '%(disk_name)s' 包含无效字符!" msgid "The clone is made, but not as asked: %(error)s" msgstr "克隆已创建,但与要求不符:%(error)s" -#: instances/jobs.py:283 +#: instances/jobs.py:285 msgid "The VM was not defined and no volume of it was left." msgstr "虚拟机未定义,也没有留下它的卷。" -#: instances/jobs.py:285 +#: instances/jobs.py:287 #, python-format msgid "" "The VM was not defined; check these volumes, then resolve this job: " "%(volumes)s" msgstr "虚拟机未定义;请检查这些卷,然后处理此任务:%(volumes)s" -#: instances/jobs.py:306 +#: instances/jobs.py:308 msgid "Delete snapshot" msgstr "删除快照" -#: instances/jobs.py:316 vrtManager/instance.py:2287 +#: instances/jobs.py:318 vrtManager/instance.py:2288 #, python-format msgid "The VM has no external snapshot named %(name)s" msgstr "虚拟机没有名为 %(name)s 的外部快照" -#: instances/jobs.py:329 +#: instances/jobs.py:331 msgid "Merging the snapshot" msgstr "正在合并快照" -#: instances/jobs.py:348 +#: instances/jobs.py:350 msgid "The VM is no longer on its compute" msgstr "虚拟机已不在其计算节点上" -#: instances/jobs.py:351 +#: instances/jobs.py:353 msgid "libvirt is still merging the snapshot" msgstr "libvirt 仍在合并快照" -#: instances/jobs.py:359 +#: instances/jobs.py:361 msgid "" "The snapshot is still listed and no merge runs. It cannot be told whether the" " disks changed; check the VM before deleting the snapshot again or reverting " "to it." msgstr "快照仍在列表中,且没有正在运行的合并。无法判断磁盘是否已更改;再次删除该快照或恢复到它之前,请检查虚拟机。" -#: instances/jobs.py:366 +#: instances/jobs.py:368 msgid "The VM is no longer on the compute the job ran on" msgstr "虚拟机已不在运行该任务的计算节点上" -#: instances/jobs.py:372 +#: instances/jobs.py:374 msgid "Take snapshot" msgstr "创建快照" -#: instances/jobs.py:384 instances/jobs.py:393 vrtManager/instance.py:1883 +#: instances/jobs.py:386 instances/jobs.py:395 vrtManager/instance.py:1884 #, python-format msgid "A snapshot named %(name)s already exists" msgstr "名为 %(name)s 的快照已存在" -#: instances/jobs.py:399 +#: instances/jobs.py:401 msgid "Taking the snapshot" msgstr "正在创建快照" -#: instances/jobs.py:412 +#: instances/jobs.py:414 msgid "libvirt is still taking the snapshot" msgstr "libvirt 仍在创建快照" -#: instances/jobs.py:415 +#: instances/jobs.py:417 msgid "The snapshot was not taken" msgstr "未创建快照" -#: instances/jobs.py:438 +#: instances/jobs.py:440 msgid "Revert snapshot" msgstr "恢复快照" -#: instances/jobs.py:469 +#: instances/jobs.py:471 msgid "Reverting the VM" msgstr "正在恢复虚拟机" -#: instances/jobs.py:480 +#: instances/jobs.py:482 msgid "libvirt is still reverting the VM" msgstr "libvirt 仍在恢复虚拟机" -#: instances/jobs.py:484 +#: instances/jobs.py:486 msgid "" "It cannot be told whether the VM was reverted; check it, then revert again or" " resolve this job." msgstr "无法判断虚拟机是否已恢复;请检查后再次恢复,或处理此任务。" -#: instances/jobs.py:501 +#: instances/jobs.py:503 msgid "The VM must be running, paused or shut off to be cloned" msgstr "虚拟机必须处于运行、暂停或关机状态才能克隆" -#: instances/jobs.py:502 +#: instances/jobs.py:504 #, python-format msgid "" "The guest's file systems could not be thawed (%(error)s); thaw them with: " "virsh domfsthaw %(vm)s" msgstr "无法解冻客户机的文件系统(%(error)s);请用以下命令解冻:virsh domfsthaw %(vm)s" -#: instances/jobs.py:521 +#: instances/jobs.py:523 msgid "Clone VM" msgstr "克隆虚拟机" -#: instances/jobs.py:562 instances/jobs.py:569 instances/jobs.py:847 +#: instances/jobs.py:564 instances/jobs.py:571 instances/jobs.py:849 msgid "Copying disks" msgstr "正在复制磁盘" -#: instances/jobs.py:621 +#: instances/jobs.py:623 msgid "libvirt has no result of the copy" msgstr "libvirt 没有复制的结果" -#: instances/jobs.py:632 +#: instances/jobs.py:634 #, python-format msgid "" "The copy failed: %(error)s. The guest's file systems could not be thawed " "(%(thaw)s); thaw them with: virsh domfsthaw %(vm)s" msgstr "复制失败:%(error)s。无法解冻客户机的文件系统(%(thaw)s);请用以下命令解冻:virsh domfsthaw %(vm)s" -#: instances/jobs.py:635 instances/jobs.py:722 +#: instances/jobs.py:637 instances/jobs.py:724 msgid "The copy failed" msgstr "复制失败" -#: instances/jobs.py:660 +#: instances/jobs.py:662 #, python-format msgid "" "The VM is no longer on the compute the job ran on; check these volumes, then " "resolve this job: %(volumes)s" msgstr "虚拟机已不在运行该任务的计算节点上;请检查这些卷,然后处理此任务:%(volumes)s" -#: instances/jobs.py:698 +#: instances/jobs.py:700 #, python-format msgid "" "A volume of the clone was being created; check these volumes, then resolve " "this job: %(volumes)s" msgstr "克隆的一个卷正在创建中;请检查这些卷,然后处理此任务:%(volumes)s" -#: instances/jobs.py:702 +#: instances/jobs.py:704 msgid "The job stopped before the copy started" msgstr "任务在复制开始前停止" -#: instances/jobs.py:709 +#: instances/jobs.py:711 msgid "libvirt is still copying the disks" msgstr "libvirt 仍在复制磁盘" -#: instances/jobs.py:712 +#: instances/jobs.py:714 #, python-format msgid "" "Another backup runs on the VM; check these volumes, then resolve this job: " "%(volumes)s" msgstr "虚拟机上正在运行另一个备份;请检查这些卷,然后处理此任务:%(volumes)s" -#: instances/jobs.py:717 +#: instances/jobs.py:719 #, python-format msgid "" "libvirt has no result of the copy; check these volumes, then resolve this " "job: %(volumes)s" msgstr "libvirt 没有复制的结果;请检查这些卷,然后处理此任务:%(volumes)s" -#: instances/jobs.py:729 +#: instances/jobs.py:731 #, 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:752 instances/templates/create_instance_w1.html:60 +#: instances/jobs.py:754 instances/templates/create_instance_w1.html:60 #: instances/templates/create_instance_w2.html:356 msgid "Create VM" msgstr "创建虚拟机" -#: instances/jobs.py:776 instances/jobs.py:821 instances/views.py:2463 +#: instances/jobs.py:778 instances/jobs.py:823 instances/views.py:2463 msgid "A virtual machine with this name already exists" msgstr "同名虚拟机已存在" -#: instances/jobs.py:778 instances/jobs.py:818 +#: instances/jobs.py:780 instances/jobs.py:820 msgid "There is an instance with same name. Remove it and try again!" msgstr "已存在同名虚拟机。请删除后重试!" -#: instances/jobs.py:781 +#: instances/jobs.py:783 msgid "First you need to create or select an image" msgstr "请先创建或选择一个镜像" -#: instances/jobs.py:783 +#: instances/jobs.py:785 msgid "Choose one disk source: a new disk, existing volumes or a template" msgstr "请选择一种磁盘来源:新磁盘、现有卷或模板" -#: instances/jobs.py:785 +#: instances/jobs.py:787 msgid "Image has already exist. Please check volumes or change instance name" msgstr "镜像已存在。请检查卷或更改虚拟机名称" -#: instances/jobs.py:787 +#: instances/jobs.py:789 msgid "Invalid cache mode" msgstr "无效的缓存模式" -#: instances/jobs.py:789 +#: instances/jobs.py:791 #, python-format msgid "This host offers no CPU mode or model %(mode)s" msgstr "此主机不提供 CPU 模式或型号 %(mode)s" -#: instances/jobs.py:791 +#: instances/jobs.py:793 #, python-format msgid "A CD-ROM cannot use the %(bus)s bus" msgstr "CD-ROM 不能使用 %(bus)s 总线" -#: instances/jobs.py:794 +#: instances/jobs.py:796 msgid "The cloud-init seed is read from a CD-ROM: choose its bus" msgstr "cloud-init 种子从 CD-ROM 读取:请选择其总线" -#: instances/jobs.py:797 +#: instances/jobs.py:799 msgid "The cloud-init seed needs a directory storage pool for the disk" msgstr "cloud-init 种子需要为磁盘使用目录类型的存储池" -#: instances/jobs.py:803 +#: instances/jobs.py:805 #, python-format msgid "This host offers no firmware %(firmware)s" msgstr "此主机不提供固件 %(firmware)s" -#: instances/jobs.py:805 vrtManager/instance.py:3015 +#: instances/jobs.py:807 vrtManager/instance.py:3020 msgid "This host offers no TPM 2.0: install swtpm on it" msgstr "此主机不提供 TPM 2.0:请在主机上安装 swtpm" -#: instances/jobs.py:866 +#: instances/jobs.py:868 msgid "Writing the cloud-init seed" msgstr "正在写入 cloud-init 种子" -#: instances/jobs.py:873 +#: instances/jobs.py:875 msgid "Defining the VM" msgstr "正在定义虚拟机" -#: instances/jobs.py:906 +#: instances/jobs.py:908 #, python-format msgid "" "The panel failed unexpectedly; its log has the details. Remove these volumes " "by hand: %(volumes)s" msgstr "面板发生意外错误;详细信息见面板日志。请手动删除这些卷:%(volumes)s" -#: instances/jobs.py:910 +#: instances/jobs.py:912 #, python-format msgid "%(error)s. Remove these volumes by hand: %(volumes)s" msgstr "%(error)s。请手动删除这些卷:%(volumes)s" -#: instances/jobs.py:935 +#: instances/jobs.py:937 #, python-format msgid "The instance is already on %(compute)s" msgstr "虚拟机已在 %(compute)s 上" -#: instances/jobs.py:937 +#: instances/jobs.py:939 #, python-format msgid "%(compute)s is not reachable" msgstr "无法连接 %(compute)s" -#: instances/jobs.py:939 +#: instances/jobs.py:941 #, python-format msgid "%(compute)s is the VM's own host under another name" msgstr "%(compute)s 是该虚拟机所在主机的另一个名称" -#: instances/jobs.py:973 +#: instances/jobs.py:981 #, python-format msgid "" "The VM migrated, but autostart could not be set on the destination: %(error)s" msgstr "虚拟机已迁移,但无法在目标主机上设置自动启动:%(error)s" -#: instances/jobs.py:979 +#: instances/jobs.py:988 #, python-format msgid "The VM migrated, but its NVRAM file stays on the source: %(error)s" msgstr "虚拟机已迁移,但其 NVRAM 文件仍留在源主机上:%(error)s" -#: instances/jobs.py:986 +#: instances/jobs.py:996 msgid "Migrate VM" msgstr "迁移虚拟机" -#: instances/jobs.py:1033 +#: instances/jobs.py:1043 msgid "Migrating the VM" msgstr "正在迁移虚拟机" -#: instances/jobs.py:1080 +#: instances/jobs.py:1094 msgid "libvirt is still migrating the VM" msgstr "libvirt 仍在迁移虚拟机" -#: instances/jobs.py:1091 +#: instances/jobs.py:1105 msgid "The VM stayed on the source host" msgstr "虚拟机留在了源主机上" -#: instances/jobs.py:1093 +#: instances/jobs.py:1107 msgid "" "The VM is on both computes or on neither; check them, then resolve this job." msgstr "虚拟机同时位于两个计算节点上,或都不在;请检查后处理此任务。" -#: instances/jobs.py:1141 +#: instances/jobs.py:1155 msgid "Move disk" msgstr "移动磁盘" -#: instances/jobs.py:1148 +#: instances/jobs.py:1162 msgid "Choose a disk of the VM" msgstr "请选择虚拟机的一个磁盘" -#: instances/jobs.py:1172 +#: instances/jobs.py:1186 msgid "Copying the disk" msgstr "正在复制磁盘" -#: instances/jobs.py:1215 +#: instances/jobs.py:1229 #, python-format msgid "Another block job runs on %(dev)s; check the VM" msgstr "%(dev)s 上正在运行另一个块任务;请检查虚拟机" -#: instances/jobs.py:1221 +#: instances/jobs.py:1235 #, python-format msgid "The disk stayed in %(path)s; remove %(dest)s if it is there" msgstr "磁盘仍在 %(path)s;如果 %(dest)s 存在,请将其删除" -#: instances/jobs.py:1223 +#: instances/jobs.py:1237 #, python-format msgid "The definitions of %(dev)s name %(paths)s; check the VM" msgstr "%(dev)s 的定义指向 %(paths)s;请检查虚拟机" -#: instances/jobs.py:1230 +#: instances/jobs.py:1244 #, python-format msgid "Disk %(path)s was kept: a disk file that stays needs it" msgstr "磁盘 %(path)s 已保留:某个保留的磁盘文件依赖它" -#: instances/jobs.py:1231 +#: instances/jobs.py:1245 #, python-format msgid "The VM is removed, but disk %(path)s was not deleted: %(err)s" msgstr "虚拟机已删除,但磁盘 %(path)s 未被删除:%(err)s" -#: instances/jobs.py:1232 +#: instances/jobs.py:1246 #, python-format msgid "Disk %(path)s is used by another VM and was kept" msgstr "磁盘 %(path)s 正被另一台虚拟机使用,已保留" -#: instances/jobs.py:1286 +#: instances/jobs.py:1300 msgid "Delete VM" msgstr "删除虚拟机" -#: instances/jobs.py:1316 +#: instances/jobs.py:1330 msgid "Deleting the VM" msgstr "正在删除虚拟机" -#: instances/jobs.py:1328 +#: instances/jobs.py:1342 msgid "The VM was not deleted; it may have been shut off" msgstr "虚拟机未被删除;它可能已被关闭" @@ -6014,7 +6014,7 @@ msgstr "%(quota)s:“%(instance_name)s”仍为模板。" msgid "The XML has no name" msgstr "XML 中没有名称" -#: instances/views.py:2475 webvirtcloud/middleware.py:126 +#: instances/views.py:2475 webvirtcloud/middleware.py:151 #, python-format msgid "libvirt Error - %(exception)s" msgstr "libvirt 错误 - %(exception)s" @@ -7517,7 +7517,8 @@ msgstr "恢复在一小时内无法读取任务状态;请检查虚拟机,然 msgid "The worker stopped before the job changed anything" msgstr "工作进程在任务做出任何更改之前停止" -#: storages/api/viewsets.py:203 storages/api/viewsets.py:224 +#: storages/api/viewsets.py:147 storages/api/viewsets.py:164 +#: storages/api/viewsets.py:202 storages/api/viewsets.py:223 msgid "Pool is not active" msgstr "存储池未激活" @@ -8421,7 +8422,6 @@ msgstr "连接失败:%(host)s 未连接" #: vrtManager/connection.py:502 #, python-format -#| msgid "This host has no secrets." msgid "This host has no architecture %(arch)s" msgstr "此主机没有 %(arch)s 架构" @@ -8628,67 +8628,71 @@ msgstr "磁盘 %(target_dev)s 不在虚拟机的定义中" msgid "Please shutdown the instance then try to enable vCPU hotplug" msgstr "请先关闭虚拟机,再启用 vCPU 热插拔" -#: vrtManager/instance.py:1842 +#: vrtManager/instance.py:1669 +msgid "The VM has no video card" +msgstr "虚拟机没有显卡" + +#: vrtManager/instance.py:1843 msgid "" "The VM has external snapshots; internal snapshots cannot be mixed with them" msgstr "虚拟机有外部快照;内部快照不能与其混用" -#: vrtManager/instance.py:1874 +#: vrtManager/instance.py:1875 msgid "A snapshot with memory is not quiesced; it keeps the whole guest state" msgstr "含内存的快照不会静默文件系统;它保存整个客户机状态" -#: vrtManager/instance.py:1876 +#: vrtManager/instance.py:1877 msgid "Only a running VM's file systems can be frozen" msgstr "只有运行中的虚拟机的文件系统可以冻结" -#: vrtManager/instance.py:1878 +#: vrtManager/instance.py:1879 msgid "The QEMU guest agent is not connected; it freezes the file systems" msgstr "QEMU 客户机代理未连接;文件系统由它冻结" -#: vrtManager/instance.py:1881 +#: vrtManager/instance.py:1882 msgid "" "The VM has internal snapshots; external snapshots cannot be mixed with them" msgstr "虚拟机有内部快照;外部快照不能与其混用" -#: vrtManager/instance.py:1886 +#: vrtManager/instance.py:1887 msgid "Newer snapshots exist after the last revert; delete them first" msgstr "上次还原后存在更新的快照;请先删除它们" -#: vrtManager/instance.py:1889 +#: vrtManager/instance.py:1890 msgid "No snapshot is current; revert to a snapshot first" msgstr "没有当前快照;请先还原到某个快照" -#: vrtManager/instance.py:1895 +#: vrtManager/instance.py:1896 #, python-format msgid "Eject the media from %(dev)s first" msgstr "请先弹出 %(dev)s 中的介质" -#: vrtManager/instance.py:1897 +#: vrtManager/instance.py:1898 #, python-format msgid "Disk %(dev)s is not a file; external snapshots need file disks" msgstr "磁盘 %(dev)s 不是文件;外部快照需要文件磁盘" -#: vrtManager/instance.py:1905 +#: vrtManager/instance.py:1906 #, python-format msgid "" "The storage pools could not be refreshed, so their free space is unknown: " "%(err)s" msgstr "无法刷新存储池,其可用空间未知:%(err)s" -#: vrtManager/instance.py:1926 +#: vrtManager/instance.py:1927 msgid "Memory is saved only from a running VM" msgstr "只能保存运行中的虚拟机的内存" -#: vrtManager/instance.py:1928 +#: vrtManager/instance.py:1929 msgid "The VM has no disk to keep its memory next to" msgstr "虚拟机没有可存放其内存的磁盘" -#: vrtManager/instance.py:1932 +#: vrtManager/instance.py:1933 #, python-format msgid "%(disk)s is not in a storage pool; memory is saved only into one" msgstr "%(disk)s 不在存储池中;内存只能保存到存储池中" -#: vrtManager/instance.py:1963 +#: vrtManager/instance.py:1964 #, python-format msgid "" "Storage pool %(pool)s has %(free).1f GiB free; the snapshot needs %(size).1f " @@ -8698,156 +8702,156 @@ msgstr "" "存储池 %(pool)s 可用 %(free).1f GiB;快照需要 %(size).1f GiB(其中虚拟机磁盘的 %(percent)g%%,至少 " "%(minimum)g GiB,若保存内存则另加内存)" -#: vrtManager/instance.py:1981 +#: vrtManager/instance.py:1982 msgid "" "This snapshot was taken by an older version on a UEFI VM and cannot be " "reverted; delete it instead" msgstr "此快照由旧版本在 UEFI 虚拟机上创建,无法还原;请将其删除" -#: vrtManager/instance.py:2011 +#: vrtManager/instance.py:2012 #, python-format msgid "" "The revert failed twice; try again in a moment (%(error)s). The VM is " "running." msgstr "还原两次失败;请稍后重试(%(error)s)。虚拟机正在运行。" -#: vrtManager/instance.py:2013 +#: vrtManager/instance.py:2014 #, python-format msgid "" "The revert failed twice; try again in a moment (%(error)s). The VM is shut " "off." msgstr "还原两次失败;请稍后重试(%(error)s)。虚拟机已关机。" -#: vrtManager/instance.py:2176 +#: vrtManager/instance.py:2177 msgid "A disk moves when its VM is defined and running, paused or shut off" msgstr "仅当虚拟机已定义且处于运行、暂停或关机状态时才能移动磁盘" -#: vrtManager/instance.py:2178 +#: vrtManager/instance.py:2179 msgid "" "A disk of a VM with snapshots, a saved state or a running block job does not " "move" msgstr "有快照、保存的状态或正在运行块任务的虚拟机的磁盘不能移动" -#: vrtManager/instance.py:2186 +#: vrtManager/instance.py:2187 #, python-format msgid "" "%(dev)s is not a disk file of the VM, the same in its running and next " "start's definition" msgstr "%(dev)s 不是该虚拟机在运行中的定义和下次启动定义中相同的磁盘文件" -#: vrtManager/instance.py:2191 +#: vrtManager/instance.py:2192 #, python-format msgid "%(dev)s does not move: only writable raw and qcow2 disks do" msgstr "%(dev)s 不能移动:只能移动可写的 raw 和 qcow2 磁盘" -#: vrtManager/instance.py:2198 +#: vrtManager/instance.py:2199 #, python-format msgid "%(dev)s does not move: no storage pool has %(path)s" msgstr "%(dev)s 不能移动:没有存储池包含 %(path)s" -#: vrtManager/instance.py:2200 +#: vrtManager/instance.py:2201 #, python-format msgid "%(dev)s does not move: it has a backing file" msgstr "%(dev)s 不能移动:它有后备文件" -#: vrtManager/instance.py:2203 +#: vrtManager/instance.py:2204 #, python-format msgid "%(pool_name)s is not an active pool of disk files of this host" msgstr "%(pool_name)s 不是此主机上活动的磁盘文件存储池" -#: vrtManager/instance.py:2206 +#: vrtManager/instance.py:2207 #, python-format msgid "%(dev)s is in pool %(pool_name)s already" msgstr "%(dev)s 已在存储池 %(pool_name)s 中" -#: vrtManager/instance.py:2210 +#: vrtManager/instance.py:2211 #, python-format msgid "Pool %(pool_name)s already has a file %(name)s" msgstr "存储池 %(pool_name)s 中已有文件 %(name)s" -#: vrtManager/instance.py:2234 +#: vrtManager/instance.py:2235 #, python-format msgid "The copy of %(dev)s ended before it was complete" msgstr "%(dev)s 的复制在完成前结束" -#: vrtManager/instance.py:2279 +#: vrtManager/instance.py:2280 #, python-format msgid "%(error)s; remove %(dest)s if it is there: %(cleanup_error)s" msgstr "%(error)s;如果 %(dest)s 存在请将其删除:%(cleanup_error)s" -#: vrtManager/instance.py:2281 +#: vrtManager/instance.py:2282 #, python-format msgid "%(error)s; the VM may use %(dest)s now: check its disk %(dev)s" msgstr "%(error)s;虚拟机现在可能在使用 %(dest)s:请检查其磁盘 %(dev)s" -#: vrtManager/instance.py:2288 +#: vrtManager/instance.py:2289 #, python-format msgid "The VM has no internal snapshot named %(name)s" msgstr "虚拟机没有名为 %(name)s 的内部快照" -#: vrtManager/instance.py:2297 +#: vrtManager/instance.py:2298 msgid "The VM has a saved state; start it or remove the saved state first" msgstr "虚拟机有保存的状态;请先启动它或删除保存的状态" -#: vrtManager/instance.py:2299 +#: vrtManager/instance.py:2300 msgid "A snapshot job is still running on this VM; try again when it ends" msgstr "此虚拟机上仍有快照任务在运行;请在其结束后重试" -#: vrtManager/instance.py:2309 +#: vrtManager/instance.py:2310 msgid "Shut the VM down to revert to an internal snapshot" msgstr "请关闭虚拟机后再还原到内部快照" -#: vrtManager/instance.py:2407 +#: vrtManager/instance.py:2408 msgid "Cannot clone a VM whose NVRAM is not a file" msgstr "NVRAM 不是文件的虚拟机无法克隆" -#: vrtManager/instance.py:2475 +#: vrtManager/instance.py:2476 #, python-format msgid "Clone failed (%(err)s); remove these copies by hand: %(path)s" msgstr "克隆失败(%(err)s);请手动删除这些副本:%(path)s" -#: vrtManager/instance.py:2485 +#: vrtManager/instance.py:2486 msgid "Shut the VM down before cloning it" msgstr "请先关闭虚拟机再克隆" -#: vrtManager/instance.py:2539 +#: vrtManager/instance.py:2540 #, python-format msgid "Clone failed (%(err)s); remove these copies by hand: %(leftovers)s" msgstr "克隆失败(%(err)s);请手动删除这些副本:%(leftovers)s" -#: vrtManager/instance.py:2550 +#: vrtManager/instance.py:2551 msgid "Live clone needs libvirt 11.10 or later; shut the VM down to clone it" msgstr "在线克隆需要 libvirt 11.10 或更高版本;请关闭虚拟机后再克隆" -#: vrtManager/instance.py:2553 +#: vrtManager/instance.py:2554 msgid "A VM with a TPM is cloned only shut down" msgstr "带 TPM 的虚拟机只能在关机时克隆" -#: vrtManager/instance.py:2556 +#: vrtManager/instance.py:2557 #, python-format msgid "Disk %(dev)s is not a local file; shut the VM down to clone it" msgstr "磁盘 %(dev)s 不是本地文件;请关闭虚拟机后再克隆" -#: vrtManager/instance.py:2558 +#: vrtManager/instance.py:2559 #, python-format msgid "Disk %(dev)s is shareable; shut the VM down to clone it" msgstr "磁盘 %(dev)s 是可共享的;请关闭虚拟机后再克隆" -#: vrtManager/instance.py:2563 +#: vrtManager/instance.py:2564 #, python-format msgid "Disk %(dev)s changes at the next start; shut the VM down to clone it" msgstr "磁盘 %(dev)s 在下次启动时会改变;请关闭虚拟机后再克隆" -#: vrtManager/instance.py:2565 +#: vrtManager/instance.py:2566 msgid "Another job is running on the VM; clone it when that ends" msgstr "虚拟机上有另一个任务正在运行;请在其结束后再克隆" -#: vrtManager/instance.py:2578 +#: vrtManager/instance.py:2579 #, python-format msgid "Volume %(name)s already exists in storage pool %(pool)s" msgstr "卷 %(name)s 已存在于存储池 %(pool)s 中" -#: vrtManager/instance.py:2597 +#: vrtManager/instance.py:2598 #, python-format msgid "" "Storage pool %(pool)s has %(free).1f GiB free; the clone needs %(size).1f GiB" @@ -8856,92 +8860,97 @@ msgstr "" "存储池 %(pool)s 可用 %(free).1f GiB;克隆需要 %(size).1f GiB(复制的磁盘,再加其 %(percent)g%%,至少" " %(minimum)g GiB)" -#: vrtManager/instance.py:2736 +#: vrtManager/instance.py:2737 msgid "A network interface that is a host device is removed in the XML editor" msgstr "作为主机设备的网络接口需在 XML 编辑器中删除" -#: vrtManager/instance.py:2752 +#: vrtManager/instance.py:2753 #, python-format msgid "No network interface with MAC %(old_mac)s" msgstr "没有 MAC 为 %(old_mac)s 的网络接口" -#: vrtManager/instance.py:2755 +#: vrtManager/instance.py:2756 msgid "A network interface that is a host device is changed in the XML editor" msgstr "作为主机设备的网络接口需在 XML 编辑器中修改" -#: vrtManager/instance.py:2846 +#: vrtManager/instance.py:2847 msgid "" "A VM changes its PCI devices while it runs, or is shut off without a saved " "state" msgstr "虚拟机只能在运行时或无保存的状态的关机时更改其 PCI 设备" -#: vrtManager/instance.py:2858 +#: vrtManager/instance.py:2859 #, python-format msgid "%(err)s; whether the VM has PCI device %(address)s now is not known" msgstr "%(err)s;虚拟机现在是否有 PCI 设备 %(address)s 未知" -#: vrtManager/instance.py:2860 +#: vrtManager/instance.py:2861 #, python-format msgid "" "%(err)s; PCI device %(address)s: the VM is not running, its next start has it" msgstr "%(err)s;PCI 设备 %(address)s:虚拟机未运行,下次启动时有它" -#: vrtManager/instance.py:2862 +#: vrtManager/instance.py:2863 #, python-format msgid "" "%(err)s; PCI device %(address)s: the VM is not running, its next start does " "not have it" msgstr "%(err)s;PCI 设备 %(address)s:虚拟机未运行,下次启动时没有它" -#: vrtManager/instance.py:2863 +#: vrtManager/instance.py:2864 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM has it, its next start has it" msgstr "%(err)s;PCI 设备 %(address)s:运行中的虚拟机有它,下次启动时有它" -#: vrtManager/instance.py:2865 +#: vrtManager/instance.py:2866 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM has it, its next start does " "not have it" msgstr "%(err)s;PCI 设备 %(address)s:运行中的虚拟机有它,下次启动时没有它" -#: vrtManager/instance.py:2867 +#: vrtManager/instance.py:2868 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM does not have it, its next " "start has it" msgstr "%(err)s;PCI 设备 %(address)s:运行中的虚拟机没有它,下次启动时有它" -#: vrtManager/instance.py:2869 +#: vrtManager/instance.py:2870 #, python-format msgid "" "%(err)s; PCI device %(address)s: the running VM does not have it, its next " "start does not have it" msgstr "%(err)s;PCI 设备 %(address)s:运行中的虚拟机没有它,下次启动时没有它" -#: vrtManager/instance.py:2903 +#: vrtManager/instance.py:2904 #, python-format msgid "The VM has no PCI device %(address)s" msgstr "虚拟机没有 PCI 设备 %(address)s" -#: vrtManager/instance.py:2921 +#: vrtManager/instance.py:2922 msgid "The VM has no USB controller" msgstr "虚拟机没有 USB 控制器" -#: vrtManager/instance.py:2940 +#: vrtManager/instance.py:2941 msgid "The VM has no such USB device" msgstr "虚拟机没有此 USB 设备" -#: vrtManager/instance.py:2981 +#: vrtManager/instance.py:2955 +#, python-format +msgid "The VM has no network interface %(mac)s" +msgstr "虚拟机没有网络接口 %(mac)s" + +#: vrtManager/instance.py:2986 msgid "The VM has its own RNG device: change it in the XML editor" msgstr "虚拟机有自己的 RNG 设备:请在 XML 编辑器中修改" -#: vrtManager/instance.py:3008 +#: vrtManager/instance.py:3013 msgid "The VM has its own TPM: change it in the XML editor" msgstr "虚拟机有自己的 TPM:请在 XML 编辑器中修改" -#: vrtManager/instance.py:3027 +#: vrtManager/instance.py:3032 #, python-format msgid "Unknown crash action %(action)s" msgstr "未知的崩溃操作 %(action)s" @@ -9314,16 +9323,16 @@ msgid "" "Volume %(name)s is in use: a VM's disk, a backing file or a snapshot's file" msgstr "卷 %(name)s 正在使用中:它是虚拟机的磁盘、后备文件或快照文件" -#: vrtManager/util.py:95 +#: vrtManager/util.py:99 #, python-format msgid "The XML is not valid: %(err)s" msgstr "XML 无效:%(err)s" -#: vrtManager/util.py:97 +#: vrtManager/util.py:101 msgid "The XML may not declare a DTD" msgstr "XML 不能声明 DTD" -#: vrtManager/util.py:143 +#: vrtManager/util.py:147 #, python-format msgid "MAC address must be of the format AA:BB:CC:DD:EE:FF, was %(mac)s" msgstr "MAC 地址的格式必须为 AA:BB:CC:DD:EE:FF,实际为 %(mac)s" diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 49177131..7cdadf0d 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -1666,6 +1666,7 @@ class wvmInstance(wvmConnect): new_model.set("primary", video_model.get("primary")) self._defineXML(etree.tostring(tree).decode()) return + raise util.OperationError(gettext_noop("The VM has no video card")) def resize_cpu(self, cur_vcpu, vcpu): """ @@ -2940,14 +2941,18 @@ class wvmInstance(wvmConnect): raise util.OperationError(gettext_noop("The VM has no such USB device")) def set_link_state(self, mac_address, state): + found = False for xml_flags, affect in self._definitions(): nic = self._nic(xml_flags, mac_address) if nic is None: continue + found = True for link in nic.findall("link"): nic.remove(link) etree.SubElement(nic, "link", state=state) self.instance.updateDeviceFlags(etree.tostring(nic).decode(), affect) + if not found: + raise util.OperationError(gettext_noop("The VM has no network interface %(mac)s"), mac=mac_address) def _set_options(self, tree, options): for o in ["title", "description"]: diff --git a/vrtManager/tests/test_device_edits.py b/vrtManager/tests/test_device_edits.py index 26887f0c..3335a9a3 100644 --- a/vrtManager/tests/test_device_edits.py +++ b/vrtManager/tests/test_device_edits.py @@ -110,6 +110,18 @@ class DeviceEditsTestCase(unittest.TestCase): self.assertEqual(model.attrib, {"type": "vga' heads='9"}) p._defineXML.assert_called_once() + def test_a_device_the_vm_does_not_have_is_refused(self): + # a stale form: no change, and the audit row says it failed + p = proxy() + p._definitions = lambda: [(PERSISTENT_XML, VIR_DOMAIN_AFFECT_CONFIG)] + with self.assertRaisesRegex(util.OperationError, "no network interface 52:54:10:00:00:09"): + p.set_link_state("52:54:10:00:00:09", "down") + p._XMLDesc = lambda flags: PERSISTENT.replace("", "") + with self.assertRaisesRegex(util.OperationError, "no video card"): + p.set_video_model("virtio") + p.instance.updateDeviceFlags.assert_not_called() + p._defineXML.assert_not_called() + def test_qos_changes_go_through_define_xml(self): for change in ( lambda p: p.set_qos("52:54:10:00:00:01", "outbound", 1, 2, 3),