From 2a05576e18fe84b12c185901faf5337f9809ebd5 Mon Sep 17 00:00:00 2001 From: catborise Date: Sun, 1 Nov 2026 15:06:54 +0300 Subject: [PATCH] feat(instances): a CD-ROM is added on the bus chosen, with its ISO A running VM got its new CD-ROM on SCSI, the one bus the panel could plug in then, and Windows does not see a virtio-scsi drive before the driver on that ISO is installed. Add CD-ROM is now a dialog that chooses the bus and the ISO: a running VM takes USB, first, which every guest sees and which hot-plugs, or SCSI; a shut-off one SATA (IDE on pc), SCSI or USB. libvirt has no empty USB drive, so a USB CD-ROM takes its ISO when it is added and is detached instead of ejected, also while the VM runs. A USB CD-ROM is a CD drive on QEMU's usb-bot only. Where QEMU has none (RHEL 9), libvirt took usb-storage, which shows the guest a hard disk that Windows does not read; the drive now asks for usb-bot, and such a host refuses it with libvirt's message. --- instances/templates/add_instance_cdrom.html | 43 +++ .../templates/instances/settings_tab.html | 14 +- instances/tests/test_live_dataloss.py | 27 ++ instances/tests/test_new_disk_name.py | 39 ++- instances/views.py | 53 ++-- locale/tr/LC_MESSAGES/django.mo | Bin 155411 -> 156422 bytes locale/tr/LC_MESSAGES/django.po | 279 ++++++++++-------- vrtManager/instance.py | 13 +- vrtManager/tests/test_device_edits.py | 10 + 9 files changed, 313 insertions(+), 165 deletions(-) create mode 100644 instances/templates/add_instance_cdrom.html diff --git a/instances/templates/add_instance_cdrom.html b/instances/templates/add_instance_cdrom.html new file mode 100644 index 00000000..7ae858a5 --- /dev/null +++ b/instances/templates/add_instance_cdrom.html @@ -0,0 +1,43 @@ +{% load i18n %} +{% load bootstrap_icons %} + + + diff --git a/instances/templates/instances/settings_tab.html b/instances/templates/instances/settings_tab.html index 62daa717..0a5bb40b 100644 --- a/instances/templates/instances/settings_tab.html +++ b/instances/templates/instances/settings_tab.html @@ -147,14 +147,12 @@
{# its CD-ROM drives, then its disks as list rows; the forms are those of the actions #} {% if instance.status != 5 %} -

{% bs_icon 'lock' %} {% trans "The VM is running: CD-ROM drives added now use the SCSI bus. Shut it down to detach other CD-ROM drives, or to detach or delete disks." %}

+

{% bs_icon 'lock' %} {% trans "The VM is running: CD-ROM drives added now use the USB or SCSI bus. Shut it down to detach a SATA or IDE CD-ROM drive, or to detach or delete disks." %}

{% endif %}

{% trans "Removable media" %}

-
{% csrf_token %} - -
+ {% include 'add_instance_cdrom.html' %}
{% for cd in instance.media %}
@@ -168,11 +166,13 @@
{{ cd.dev }} · {{ cd.bus }} · {% if cd.image %}{{ cd.image }}{% else %}{% trans "No media" %}{% endif %}
- {% if not cd.image %} - + {% if not cd.image or cd.bus == "usb" %} + {% endif %} - {% if cd.image %} + {% if cd.bus == "usb" %} + {# libvirt has no empty USB drive: it is detached, not ejected #} + {% elif cd.image %} {% else %} diff --git a/instances/tests/test_live_dataloss.py b/instances/tests/test_live_dataloss.py index 38f23329..604080d7 100644 --- a/instances/tests/test_live_dataloss.py +++ b/instances/tests/test_live_dataloss.py @@ -601,6 +601,33 @@ class LiveDataLossTestCase(TestCase): tree = etree.fromstring(dom.XMLDesc(flags)) self.assertEqual(tree.xpath("devices/disk[@device='cdrom']/target/@dev"), ["sda"]) + def test_a_usb_cdrom_with_its_iso_is_plugged_into_a_running_vm_and_detached(self): + from lxml import etree + + pool = self.conn.storagePoolLookupByName(livetest.POOL) + iso = pool.createXML(f"{P}usb.iso1" + "", 0) + dom, inst = self.vm("cdrom-usb", [], extra_devices="") + dom.create() + + def cdroms(flags): + tree = etree.fromstring(dom.XMLDesc(flags)) + return [(d.find("target").get("bus"), d.get("model"), d.xpath("string(source/@file)")) + for d in tree.xpath("devices/disk[@device='cdrom']")] + + response = self.client.post(reverse("instances:add_cdrom", args=[inst.id]), + {"bus": "usb", "media": f"{P}usb.iso"}, follow=True) + if not cdroms(0): # a QEMU without usb-bot (RHEL 9): refused, nothing added + self.assertIn("usb-bot", " ".join(str(m) for m in response.context["messages"])) + self.assertEqual(cdroms(libvirt.VIR_DOMAIN_XML_INACTIVE), []) + return + for flags in (0, libvirt.VIR_DOMAIN_XML_INACTIVE): + self.assertEqual(cdroms(flags), [("usb", "usb-bot", iso.path())]) + self.client.post(reverse("instances:detach_cdrom", args=[inst.id, "sda"]), + HTTP_REFERER=reverse("instances:instance", args=[inst.id])) + for flags in (0, libvirt.VIR_DOMAIN_XML_INACTIVE): + self.assertEqual(cdroms(flags), []) + def test_pending_qos_change_is_shown_and_kept_by_the_next_edit(self): from lxml import etree, html diff --git a/instances/tests/test_new_disk_name.py b/instances/tests/test_new_disk_name.py index a7bab22f..8cc6d786 100644 --- a/instances/tests/test_new_disk_name.py +++ b/instances/tests/test_new_disk_name.py @@ -30,7 +30,8 @@ class NewDiskNameTestCase(TestCase): proxy = wvm.return_value proxy.get_disk_devices.side_effect = lambda **kw: list((config if kw.get("config") else live)[0]) proxy.get_media_devices.side_effect = lambda **kw: list((config if kw.get("config") else live)[1]) - proxy.get_disk_bus_types.return_value = ["virtio", "sata", "scsi"] + proxy.get_disk_bus_types.return_value = ["virtio", "sata", "scsi", "usb"] + proxy.get_iso_path.side_effect = lambda image: f"/iso/{image}" proxy.get_cache_modes.return_value = {"default": "", "none": ""} proxy.get_machine_type.return_value = "q35" proxy.get_status.return_value = status @@ -39,14 +40,14 @@ class NewDiskNameTestCase(TestCase): storage.return_value.get_volume_type.return_value = "file" storage.return_value.get_volume_format_type.return_value = "qcow2" create.return_value.create_volume.return_value = "/pool/data.qcow2" - self.client.post(reverse(f"instances:{view}", args=[self.instance.id]), data) - proxy.attach_disk.assert_called_once() + self.response = self.client.post(reverse(f"instances:{view}", args=[self.instance.id]), data, follow=True) + self.proxy = proxy return proxy.attach_disk.call_args VIEWS = ( - ("add_new_vol", {"storage": "default", "name": "data", "format": "qcow2", "size": "1", "cache": "default"}), - ("add_existing_vol", {"selected_storage": "default", "vols": "data.qcow2", "cache": "default"}), - ("add_cdrom", {}), + ("add_new_vol", {"storage": "default", "name": "data", "format": "qcow2", "size": "1", "cache": "default"}, 1), + ("add_existing_vol", {"selected_storage": "default", "vols": "data.qcow2", "cache": "default"}, 1), + ("add_cdrom", {}, 5), # shut off: a running VM takes USB and SCSI CD-ROMs only ) def test_a_name_either_definition_uses_is_not_given_again(self): @@ -58,12 +59,28 @@ class NewDiskNameTestCase(TestCase): ("virtio", ([vda, dev("vdb", "virtio")], []), ([vda], []), "vdc"), # not yet released by the guest ("virtio", ([vda], []), ([vda], []), "vdb"), # both the same, as on a stopped VM ) - for view, data in self.VIEWS: + for view, data, status in self.VIEWS: for bus, live, config, expected in cases: + if view == "add_cdrom" and bus == "virtio": # no virtio CD-ROM + continue with self.subTest(view=view, bus=bus, expected=expected): - self.assertEqual(self.attach(view, {**data, "bus": bus}, live, config).args[0], expected) + call = self.attach(view, {**data, "bus": bus}, live, config, status=status) + self.assertEqual(call.args[0], expected) - def test_a_cdrom_of_a_running_vm_is_on_the_bus_that_can_be_hotplugged(self): - for status, bus in ((1, "scsi"), (3, "scsi"), (5, "sata")): # running, paused, shut off + def test_a_cdrom_of_a_running_vm_is_on_a_bus_that_can_be_hotplugged(self): + # running, paused: USB first, which every guest sees; shut off: SATA + for status, bus in ((1, "usb"), (3, "usb"), (5, "sata")): with self.subTest(status=status): - self.assertEqual(self.attach("add_cdrom", {}, status=status).kwargs["target_bus"], bus) + call = self.attach("add_cdrom", {"media": "virtio-win.iso"}, status=status) + self.assertEqual((call.kwargs["target_bus"], call.args[1]), (bus, "/iso/virtio-win.iso")) + + def test_a_running_vm_refuses_a_sata_cdrom_and_a_usb_one_without_media(self): + for data in ({"bus": "sata", "media": "virtio-win.iso"}, {"bus": "usb", "media": ""}): + with self.subTest(data): + self.attach("add_cdrom", data, status=1) + self.proxy.attach_disk.assert_not_called() + self.assertTrue(list(self.response.context["messages"])) + + def test_a_cdrom_without_media_is_empty(self): + call = self.attach("add_cdrom", {"bus": "scsi", "media": ""}, status=1) + self.assertEqual((call.kwargs["target_bus"], call.args[1]), ("scsi", "")) diff --git a/instances/views.py b/instances/views.py index 397633ec..4926a993 100755 --- a/instances/views.py +++ b/instances/views.py @@ -307,6 +307,7 @@ def _instance_page(request, pk, instance, compute): vcpu_host = len(instance.vcpu_range) memory_host = instance.proxy.get_max_memory() bus_host = instance.proxy.get_disk_bus_types(instance.arch, instance.machine) + cdrom_buses = cdrom_bus_choices(instance) networks_host = sorted(instance.proxy.get_networks()) interfaces_host = sorted(instance.proxy.get_ifaces()) nwfilters_host = instance.proxy.get_nwfilters() @@ -1575,31 +1576,49 @@ def detach_vol(request, pk): @serialize_instance_mutation def add_cdrom(request, pk): instance = get_instance(request.user, pk, perm_type="change") - if instance.status == 5: - default_bus = "ide" if instance.machine == "pc" else "sata" - else: # IDE and SATA drives cannot be plugged into a running VM, SCSI ones can - default_bus = "scsi" - bus = request.POST.get("bus", default_bus) - invalid = invalid_disk_options(instance, bus=bus) - if invalid: - return reject_disk_options(request, pk, invalid) + buses = cdrom_bus_choices(instance) + bus = request.POST.get("bus", buses[0] if buses else "") + image = request.POST.get("media", "") + if bus not in buses: + messages.error(request, _("This VM cannot take a CD-ROM on the %(bus)s bus now") % {"bus": bus}) + return _back(request, pk, "disks") + if bus == "usb" and not image: + messages.error(request, _("A USB CD-ROM needs an ISO image: libvirt has no empty USB drive")) + return _back(request, pk, "disks") target = utils.get_new_disk_dev( instance.media + instance.config_media, instance.disks + instance.config_disks, bus ) - with audit_vm(request, instance, f"add CD-ROM {target}"): - instance.proxy.attach_disk( - target, - "", - disk_device="cdrom", - cache_mode="none", - target_bus=bus, - readonly=True, - ) + try: + with audit_vm(request, instance, f"add CD-ROM {target}" + (f" with {image}" if image else "")): + instance.proxy.attach_disk( + target, + instance.proxy.get_iso_path(image) if image else "", + disk_device="cdrom", + cache_mode="none", + target_bus=bus, + readonly=True, + ) + except (libvirtError, util.OperationError) as err: + messages.error(request, err) return _back(request, pk, "disks") +def cdrom_bus_choices(instance): + """The buses a CD-ROM can be added on now, the default first. A running VM + takes USB and SCSI drives only; Windows sees USB, SATA and IDE ones + without a driver.""" + if instance.status != 5: + order = ("usb", "scsi") + elif instance.machine == "pc": + order = ("ide", "sata", "scsi", "usb") + else: + order = ("sata", "scsi", "usb") + offered = instance.proxy.get_disk_bus_types(instance.arch, instance.machine) + return [bus for bus in order if bus in offered] + + @require_POST @superuser_only @serialize_instance_mutation diff --git a/locale/tr/LC_MESSAGES/django.mo b/locale/tr/LC_MESSAGES/django.mo index 259f3dd5f1bdb98c457036be438d1eebc6cf3ffe..36f2a5209cab6b2682eee535e5b3e5ac9c1e1067 100644 GIT binary patch delta 38248 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 C0|d7K diff --git a/locale/tr/LC_MESSAGES/django.po b/locale/tr/LC_MESSAGES/django.po index d535f790..56e6793c 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-01 10:56+0000\n" +"POT-Creation-Date: 2026-11-01 13:18+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/" @@ -271,6 +271,7 @@ msgstr "Parola Değiştir" #: admin/templates/admin/user_form.html:137 #: computes/templates/computes/form.html:45 #: console/templates/console-vnc.html:86 +#: instances/templates/add_instance_cdrom.html:38 #: instances/templates/add_instance_network_block.html:55 #: instances/templates/add_instance_owner_block.html:29 #: instances/templates/add_instance_volume.html:108 @@ -504,6 +505,7 @@ msgid "Add key to user form" msgstr "Kullanıcıya anahtar ekleme formu" #: accounts/templates/profile.html:55 +#: instances/templates/add_instance_cdrom.html:39 #: instances/templates/add_instance_network_block.html:56 #: instances/templates/add_instance_owner_block.html:30 #: instances/templates/create_flavors.html:20 @@ -2465,6 +2467,7 @@ msgid "Connect" msgstr "Bağlan" #: console/templates/console-vnc.html:98 +#: instances/templates/add_instance_cdrom.html:10 #: instances/templates/add_instance_network_block.html:11 #: instances/templates/add_instance_owner_block.html:11 #: instances/templates/add_instance_volume.html:15 @@ -2728,7 +2731,7 @@ msgstr "libvirt diskleri hâlâ kopyalıyor" msgid "Create VM" msgstr "Sanal makine oluştur" -#: instances/jobs.py:685 instances/views.py:2408 +#: instances/jobs.py:685 instances/views.py:2427 msgid "A virtual machine with this name already exists" msgstr "Bu isimde bir sanal makine mevcut" @@ -2865,7 +2868,7 @@ msgid "Disk" msgstr "Disk" #: instances/models.py:223 instances/templates/create_instance_w2.html:309 -#: instances/templates/instances/settings_tab.html:167 +#: instances/templates/instances/settings_tab.html:165 msgid "CD-ROM" msgstr "CD-ROM" @@ -2946,6 +2949,45 @@ msgstr "Çöktü" msgid "PM suspended" msgstr "Güç yönetimiyle askıya alındı" +#: instances/templates/add_instance_cdrom.html:3 +#: instances/templates/add_instance_cdrom.html:9 +msgid "Add CD-ROM" +msgstr "CD-ROM Ekle" + +#: instances/templates/add_instance_cdrom.html:7 +msgid "Add CD-ROM form" +msgstr "CD-ROM ekleme formu" + +#: instances/templates/add_instance_cdrom.html:15 +#: instances/templates/add_instance_volume.html:90 +#: instances/templates/create_instance_w2.html:181 +#: instances/templates/instances/edit_instance_volume.html:82 +#: instances/templates/instances/edit_instance_volume.html:87 +msgid "Bus" +msgstr "Veriyolu" + +#: instances/templates/add_instance_cdrom.html:23 +#: instances/templates/instances/settings_tab.html:179 +msgid "ISO image" +msgstr "ISO imajı" + +#: instances/templates/add_instance_cdrom.html:28 +#: instances/templates/instances/settings_tab.html:166 +msgid "No media" +msgstr "Ortam yok" + +#: instances/templates/add_instance_cdrom.html:32 +msgid "" +"Windows sees USB, SATA and IDE drives without a driver; SCSI ones need the " +"virtio driver." +msgstr "Windows USB, SATA ve IDE sürücüleri sürücü yazılımı olmadan görür; SCSI olanlar için virtio sürücüsü gerekir." + +#: instances/templates/add_instance_cdrom.html:33 +msgid "" +"A running VM takes USB and SCSI drives. A USB drive needs an ISO image, is " +"detached instead of ejected, and needs a QEMU with usb-bot (not RHEL 9's)." +msgstr "Çalışan bir sanal makineye USB ve SCSI sürücüler eklenebilir. USB sürücü bir ISO imajı ister, çıkarılmaz, ayrılır ve usb-bot destekli bir QEMU gerektirir (RHEL 9'unki desteklemez)." + #: instances/templates/add_instance_network_block.html:4 #: instances/templates/add_instance_network_block.html:10 msgid "Add network device" @@ -3016,7 +3058,7 @@ msgid "Disk source" msgstr "Disk kaynağı" #: instances/templates/add_instance_volume.html:21 -#: instances/templates/create_instance_w2.html:131 instances/views.py:2427 +#: instances/templates/create_instance_w2.html:131 instances/views.py:2446 msgid "New disk" msgstr "Yeni disk" @@ -3076,13 +3118,6 @@ msgstr "Birim" msgid "Choose a pool first" msgstr "Önce bir havuz seçin" -#: instances/templates/add_instance_volume.html:90 -#: instances/templates/create_instance_w2.html:181 -#: instances/templates/instances/edit_instance_volume.html:82 -#: instances/templates/instances/edit_instance_volume.html:87 -msgid "Bus" -msgstr "Veriyolu" - #: instances/templates/add_instance_volume.html:98 #: instances/templates/instances/edit_instance_volume.html:45 msgid "Cache mode" @@ -3385,7 +3420,7 @@ msgstr "Özel CPU modelleri" msgid "Size (GiB)" msgstr "Boyut (GiB)" -#: instances/templates/create_instance_w2.html:158 instances/views.py:2428 +#: instances/templates/create_instance_w2.html:158 instances/views.py:2447 msgid "Existing volumes" msgstr "Mevcut birimler" @@ -3420,7 +3455,7 @@ msgstr "Aygıt" msgid "Remove" msgstr "Kaldır" -#: instances/templates/create_instance_w2.html:191 instances/views.py:2429 +#: instances/templates/create_instance_w2.html:191 instances/views.py:2448 msgid "Clone from template" msgstr "Şablondan klonla" @@ -3755,7 +3790,7 @@ msgstr "" msgid "Start the VM to open its console." msgstr "Konsolunu açmak için sanal makineyi başlatın." -#: instances/templates/instances/access_tab.html:41 instances/views.py:1075 +#: instances/templates/instances/access_tab.html:41 instances/views.py:1076 msgid "Reset root password" msgstr "Root parolasını sıfırla" @@ -4538,30 +4573,15 @@ msgstr "Sırayı uygula" #: instances/templates/instances/settings_tab.html:150 msgid "" -"The VM is running: CD-ROM drives added now use the SCSI bus. Shut it down to " -"detach other CD-ROM drives, or to detach or delete disks." -msgstr "" -"Sanal makine çalışıyor: şimdi eklenen CD-ROM sürücüleri SCSI veri yolunu " -"kullanır. Diğer CD-ROM sürücülerini ayırmak ya da diskleri ayırmak veya " -"silmek için sanal makineyi kapatın." +"The VM is running: CD-ROM drives added now use the USB or SCSI bus. Shut it " +"down to detach a SATA or IDE CD-ROM drive, or to detach or delete disks." +msgstr "Sanal makine çalışıyor: şimdi eklenen CD-ROM sürücüleri USB veya SCSI veri yolunu kullanır. SATA veya IDE bir CD-ROM sürücüsünü ayırmak ya da diskleri ayırmak veya silmek için sanal makineyi kapatın." #: instances/templates/instances/settings_tab.html:154 msgid "Removable media" msgstr "Çıkarılabilir ortam" -#: instances/templates/instances/settings_tab.html:155 -msgid "Add CD-ROM form" -msgstr "CD-ROM ekleme formu" - -#: instances/templates/instances/settings_tab.html:156 -msgid "Add CD-ROM" -msgstr "CD-ROM Ekle" - -#: instances/templates/instances/settings_tab.html:168 -msgid "No media" -msgstr "Ortam yok" - -#: instances/templates/instances/settings_tab.html:172 +#: instances/templates/instances/settings_tab.html:170 #: instances/templates/instances/settings_tab.html:249 msgid "Detach" msgstr "Ayır" @@ -4570,10 +4590,6 @@ msgstr "Ayır" msgid "Unmount" msgstr "Ayır" -#: instances/templates/instances/settings_tab.html:179 -msgid "ISO image" -msgstr "ISO imajı" - #: instances/templates/instances/settings_tab.html:191 msgid "Mount" msgstr "Bağla" @@ -5542,16 +5558,16 @@ msgstr "'%(name)s' sanal makinesinin sunucusuna şu anda erişilemiyor." msgid " Jobs on it can be followed under Jobs in the menu." msgstr " Üzerindeki işler menüdeki İşler bölümünden izlenebilir." -#: instances/views.py:333 +#: instances/views.py:334 msgid "There is no other host to migrate to." msgstr "Taşınacak başka bir sunucu yok." -#: instances/views.py:335 +#: instances/views.py:336 msgid "A VM migrates when it is running, paused or shut off." msgstr "" "Sanal makine çalışırken, duraklatılmışken ya da kapalıyken taşınabilir." -#: instances/views.py:337 +#: instances/views.py:338 msgid "" "A VM with snapshots cannot be migrated: its snapshots stay on this host. " "Delete them first." @@ -5559,7 +5575,7 @@ msgstr "" "Anlık görüntüsü olan bir sanal makine taşınamaz: anlık görüntüleri bu " "sunucuda kalır. Önce onları silin." -#: instances/views.py:339 +#: instances/views.py:340 msgid "" "A VM with host devices cannot be migrated: the destination may not have " "them. Remove them first." @@ -5567,11 +5583,11 @@ msgstr "" "Sunucu aygıtları olan bir sanal makine taşınamaz: hedefte bu aygıtlar " "olmayabilir. Önce onları kaldırın." -#: instances/views.py:605 +#: instances/views.py:606 msgid "The VM was moved meanwhile; try again." msgstr "Sanal makine bu arada taşındı; yeniden deneyin." -#: instances/views.py:636 +#: instances/views.py:637 msgid "" "The quota cannot be checked because a host of your virtual machines cannot " "be reached. Nothing was changed." @@ -5579,7 +5595,7 @@ msgstr "" "Sanal makinelerinizin bir sunucusuna erişilemediği için kota denetlenemiyor. " "Hiçbir şey değiştirilmedi." -#: instances/views.py:717 +#: instances/views.py:718 msgid "" "The running VM has no free PCI slot for another disk on this bus: choose the " "scsi bus if the VM has a SCSI controller, or shut the VM down and add the " @@ -5592,7 +5608,7 @@ msgstr "" "yuvaları ekle, sonraki başlatmadan itibaren ona boş yuvalar verir. Yeni bir " "birim havuzunda kalır, mevcut birim olarak eklenebilir." -#: instances/views.py:721 +#: instances/views.py:722 msgid "" "The running VM has no free PCI slot for the SCSI controller the disk needs: " "shut the VM down and add the disk then; Add PCIe slots on the Disks tab " @@ -5604,82 +5620,82 @@ msgstr "" "PCIe yuvaları ekle, sonraki başlatmadan itibaren ona boş yuvalar verir. Yeni " "bir birim havuzunda kalır, mevcut birim olarak eklenebilir." -#: instances/views.py:739 +#: instances/views.py:740 #, python-format msgid "Invalid disk options: %(options)s" msgstr "Geçersiz disk seçenekleri: %(options)s" -#: instances/views.py:749 instances/views.py:764 +#: instances/views.py:750 instances/views.py:765 msgid "Templates cannot be started." msgstr "Şablon makineler başlatılamaz." -#: instances/views.py:860 +#: instances/views.py:861 #, 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/views.py:866 +#: instances/views.py:867 #, 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/views.py:875 +#: instances/views.py:876 #, 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/views.py:997 +#: instances/views.py:998 #, python-format msgid "Set the gstfsd key of %(compute)s first (Computes, edit the host)" msgstr "" "Önce %(compute)s sunucusunun gstfsd anahtarını ayarlayın (Sunucular, " "sunucuyu düzenle)" -#: instances/views.py:1003 instances/views.py:1017 instances/views.py:1026 +#: instances/views.py:1004 instances/views.py:1018 instances/views.py:1027 #, python-format msgid "gstfsd error: %(err)s" msgstr "gstfsd hatası: %(err)s" -#: instances/views.py:1027 +#: instances/views.py:1028 msgid "the reply is not signed with the key" msgstr "yanıt anahtarla imzalanmamış" -#: instances/views.py:1035 +#: instances/views.py:1036 msgid "Shut the VM down, or start its QEMU guest agent, and try again" msgstr "" "Sanal makineyi kapatın ya da QEMU konuk ajanını başlatın ve yeniden deneyin" -#: instances/views.py:1048 +#: instances/views.py:1049 msgid "The change may still be made when the agent answers again." msgstr "Ajan yeniden yanıt verdiğinde değişiklik yine de yapılabilir." -#: instances/views.py:1079 +#: instances/views.py:1080 msgid "Enter a password" msgstr "Bir parola girin" -#: instances/views.py:1093 +#: instances/views.py:1094 msgid "Select an SSH public key" msgstr "Bir SSH açık anahtarı seçin" -#: instances/views.py:1115 +#: instances/views.py:1116 #, python-format msgid "Installed new SSH public key %(keyname)s" msgstr "Yeni %(keyname)s SSH açık anahtarı yüklendi" -#: instances/views.py:1135 +#: instances/views.py:1136 msgid "Enter a whole number of vCPUs" msgstr "vCPU sayısı için bir tam sayı girin" -#: instances/views.py:1139 +#: instances/views.py:1140 msgid "Shut off the VM to change its vCPUs" msgstr "vCPU'larını değiştirmek için sanal makineyi kapatın" -#: instances/views.py:1142 +#: instances/views.py:1143 msgid "The current vCPUs cannot be more than the maximum" msgstr "Geçerli vCPU sayısı en fazla değerden büyük olamaz" -#: instances/views.py:1150 +#: instances/views.py:1151 #, python-format msgid "" "User %(quota_msg)s quota reached, cannot resize CPU of '%(instance_name)s'!" @@ -5687,20 +5703,20 @@ msgstr "" "%(quota_msg)s kullanıcısı kotası doldu, '%(instance_name)s' in CPU su " "yeniden boyutlandırılamıyor!" -#: instances/views.py:1161 +#: instances/views.py:1162 #, python-format msgid "vCPUs set to %(cur)s (maximum %(max)s)" msgstr "vCPU sayısı %(cur)s olarak ayarlandı (en fazla %(max)s)" -#: instances/views.py:1180 +#: instances/views.py:1181 msgid "Enter a whole number for memory" msgstr "Bellek için bir tam sayı girin" -#: instances/views.py:1183 +#: instances/views.py:1184 msgid "The current memory cannot be more than the maximum" msgstr "Geçerli bellek en fazla değerden büyük olamaz" -#: instances/views.py:1188 +#: instances/views.py:1189 #, python-format msgid "" "User %(quota_msg)s quota reached, cannot resize memory of " @@ -5709,25 +5725,25 @@ msgstr "" "%(quota_msg)s kullanıcı kotası doldu, '%(instance_name)s' in belleği yeniden " "boyutlandırılamıyor!" -#: instances/views.py:1197 +#: instances/views.py:1198 #, python-format msgid "Memory set to %(cur)s MiB (maximum %(max)s MiB)" msgstr "Bellek %(cur)s MiB olarak ayarlandı (en fazla %(max)s MiB)" -#: instances/views.py:1217 +#: instances/views.py:1218 msgid "Enter disk sizes as whole GiB" msgstr "Disk boyutlarını tam GiB olarak girin" -#: instances/views.py:1221 +#: instances/views.py:1222 #, python-format msgid "%(dev)s is %(size)s: a disk cannot shrink" msgstr "%(dev)s boyutu %(size)s: disk küçültülemez" -#: instances/views.py:1229 +#: instances/views.py:1230 msgid "No disks were selected or valid for resizing." msgstr "Boyutlandırma için seçilmiş ya da geçerli disk yok." -#: instances/views.py:1239 +#: instances/views.py:1240 #, python-format msgid "" "User %(quota_msg)s quota reached, cannot resize disks of '%(instance_name)s'!" @@ -5735,61 +5751,61 @@ msgstr "" "%(quota_msg)s kullanıcı kotası doldu, '%(instance_name)s' in diski yeniden " "boyutlandırılamıyor!" -#: instances/views.py:1254 +#: instances/views.py:1255 #, python-format msgid "The storage pool of %(dev)s cannot resize its volumes" msgstr "%(dev)s diskinin depolama havuzu birimlerini boyutlandıramaz" -#: instances/views.py:1256 +#: instances/views.py:1257 #, python-format msgid "Disk is resized: %(dev)s" msgstr "Disk boyutu değiştirildi: %(dev)s" -#: instances/views.py:1350 +#: instances/views.py:1351 msgid "The VM's machine has no PCIe slots to add" msgstr "Sanal makinenin makine tipinde eklenecek PCIe yuvası yok" -#: instances/views.py:1355 +#: instances/views.py:1356 #, python-format msgid "%(count)s PCIe slots added" msgstr "%(count)s PCIe yuvası eklendi" -#: instances/views.py:1357 +#: instances/views.py:1358 #, python-format msgid "%(count)s PCIe slots added: the VM gets them at its next start" msgstr "" "%(count)s PCIe yuvası eklendi: sanal makine bunları sonraki başlatmasında " "alır" -#: instances/views.py:1488 +#: instances/views.py:1489 msgid "Volume changes are applied. But it will be activated after shutdown" msgstr "" "Birim değişiklikleri uygulandı. Ama yeniden başlatıldıktan sonra uygulanacak" -#: instances/views.py:1493 +#: instances/views.py:1494 msgid "Volume is changed successfully." msgstr "Birim başarıyla değiştirildi." -#: instances/views.py:1511 +#: instances/views.py:1512 #, python-format msgid "Disk %(dev)s is not a storage volume of this instance" msgstr "%(dev)s diski bu sanal makinenin bir depolama birimi değil" -#: instances/views.py:1518 +#: instances/views.py:1519 #, python-format msgid "Disk %(dev)s has a pending change; delete it once the VM is shut down" msgstr "" "%(dev)s diskinin bekleyen bir değişikliği var; sanal makine kapatıldığında " "silin" -#: instances/views.py:1533 +#: instances/views.py:1534 #, python-format msgid "Volume %(vol)s is used by another VM; detach it instead" msgstr "" "%(vol)s birimi başka bir sanal makine tarafından kullanılıyor; onun yerine " "ayırın" -#: instances/views.py:1545 +#: instances/views.py:1546 #, python-format msgid "" "The guest has not released disk %(dev)s yet; volume %(vol)s was kept. Delete " @@ -5798,43 +5814,52 @@ msgstr "" "Konuk sistem %(dev)s diskini henüz bırakmadı; %(vol)s birimi korundu. Sanal " "makine kapatıldığında depolama havuzundan silin." -#: instances/views.py:1556 +#: instances/views.py:1557 #, python-format msgid "Disk %(dev)s was detached, but its volume was kept: %(err)s" msgstr "%(dev)s diski ayrıldı, ancak birimi korundu: %(err)s" -#: instances/views.py:1660 +#: instances/views.py:1583 +#, python-format +msgid "This VM cannot take a CD-ROM on the %(bus)s bus now" +msgstr "Bu sanal makineye şu anda %(bus)s veri yolunda CD-ROM eklenemez" + +#: instances/views.py:1586 +msgid "A USB CD-ROM needs an ISO image: libvirt has no empty USB drive" +msgstr "USB CD-ROM bir ISO imajı ister: libvirt boş USB sürücü desteklemiyor" + +#: instances/views.py:1679 #, python-format msgid "Snapshot was not deleted: %(err)s" msgstr "Anlık görüntü silinmedi: %(err)s" -#: instances/views.py:1775 +#: instances/views.py:1794 msgid "Check at least one device to boot from." msgstr "Önyükleme için en az bir aygıt işaretleyin." -#: instances/views.py:1789 +#: instances/views.py:1808 msgid "Boot order changed. It applies at the next start." msgstr "Ön yükleme sırası değiştirildi. Sonraki başlatmada uygulanır." -#: instances/views.py:1791 +#: instances/views.py:1810 msgid "Boot order changed successfully." msgstr "Ön yükleme sırası başarıyla değiştirildi." -#: instances/views.py:1811 +#: instances/views.py:1830 msgid "The XML is empty" msgstr "XML boş" -#: instances/views.py:1815 instances/views.py:2418 +#: instances/views.py:1834 instances/views.py:2437 #, python-format msgid "The XML is not well-formed: %(error)s" msgstr "XML düzgün biçimlendirilmemiş: %(error)s" -#: instances/views.py:1817 +#: instances/views.py:1836 #, python-format msgid "The XML must keep the VM's UUID %(uuid)s." msgstr "XML, sanal makinenin %(uuid)s UUID'sini korumalıdır." -#: instances/views.py:1821 +#: instances/views.py:1840 msgid "" "The definition has changed since the editor was opened. Reload the page to " "edit the current one." @@ -5842,35 +5867,35 @@ msgstr "" "Düzenleyici açıldığından beri tanım değişti. Geçerli tanımı düzenlemek için " "sayfayı yeniden yükleyin." -#: instances/views.py:1833 +#: instances/views.py:1852 msgid "The XML definition was saved." msgstr "XML tanımı kaydedildi." -#: instances/views.py:1835 +#: instances/views.py:1854 msgid "The XML definition was saved; the VM uses it from its next start." msgstr "" "XML tanımı kaydedildi; sanal makine onu sonraki başlatmasından itibaren " "kullanır." -#: instances/views.py:1902 +#: instances/views.py:1921 msgid "Select one network interface to change" msgstr "Değiştirilecek bir ağ arabirimi seçin" -#: instances/views.py:1940 +#: instances/views.py:1959 msgid "Network Device Config is changed. Please shutdown instance to activate." msgstr "" "Ağ aygıtı yapılandırması değiştirildi. Aktifleştirmek için lütfen sanal " "makineyi kapatın." -#: instances/views.py:1942 +#: instances/views.py:1961 msgid "Network device changed" msgstr "Ağ aygıtı değiştirildi" -#: instances/views.py:1954 +#: instances/views.py:1973 msgid "No network selected" msgstr "Ağ seçilmedi" -#: instances/views.py:1974 +#: instances/views.py:1993 msgid "" "The running VM has no free PCI slot for another network device: shut it down " "and add the device then; Add PCIe slots on the Disks tab gives it free ones " @@ -5880,21 +5905,21 @@ msgstr "" "aygıtı sonra ekleyin; Diskler sekmesindeki PCIe yuvaları ekle, sonraki " "başlatmadan itibaren ona boş yuvalar verir." -#: instances/views.py:2011 instances/views.py:2017 +#: instances/views.py:2030 instances/views.py:2036 msgid "The VM has it" msgstr "Sanal makinede var" -#: instances/views.py:2048 +#: instances/views.py:2067 #, python-format msgid "The host has no USB device %(device)s" msgstr "Sunucuda %(device)s USB aygıtı yok" -#: instances/views.py:2069 +#: instances/views.py:2088 #, python-format msgid "The host has no PCI device %(device)s" msgstr "Sunucuda %(device)s PCI aygıtı yok" -#: instances/views.py:2087 +#: instances/views.py:2106 #, python-format msgid "" "Removal of PCI device %(address)s requested: the guest has not let it go " @@ -5904,20 +5929,20 @@ msgstr "" "bırakmadı; bırakana ya da sanal makine durana kadar çalışan sanal makinede " "kalır." -#: instances/views.py:2132 instances/views.py:2162 +#: instances/views.py:2151 instances/views.py:2181 msgid "Select a network interface and a QoS direction" msgstr "Bir ağ arabirimi ve bir QoS yönü seçin" -#: instances/views.py:2135 +#: instances/views.py:2154 msgid "QoS rates must be whole numbers" msgstr "QoS hızları tam sayı olmalıdır" -#: instances/views.py:2142 networks/views.py:229 +#: instances/views.py:2161 networks/views.py:229 #, python-format msgid "%(qos_dir)s QoS is set" msgstr "%(qos_dir)s QoS ayarlandı" -#: instances/views.py:2147 +#: instances/views.py:2166 #, python-format msgid "" "%(qos_dir)s QoS is set. A running VM gets it after it is powered off and on." @@ -5925,12 +5950,12 @@ msgstr "" "%(qos_dir)s QoS ayarlandı. Çalışan bir sanal makine, kapatılıp açıldıktan " "sonra bunu alır." -#: instances/views.py:2169 networks/views.py:255 +#: instances/views.py:2188 networks/views.py:255 #, python-format msgid "%(qos_dir)s QoS is deleted" msgstr "%(qos_dir)s QoS silindi" -#: instances/views.py:2174 +#: instances/views.py:2193 #, python-format msgid "" "%(qos_dir)s QoS is deleted. A running VM keeps the old limit until it is " @@ -5939,21 +5964,21 @@ msgstr "" "%(qos_dir)s QoS silindi. Çalışan bir sanal makine, kapatılıp açılana kadar " "eski sınırı korur." -#: instances/views.py:2202 +#: instances/views.py:2221 msgid "Only one owner is allowed and the one already added" msgstr "Sadece bir sahibe izin verilmiştir ve bir sahip zaten eklenmiş" -#: instances/views.py:2204 +#: instances/views.py:2223 #, python-format msgid "%(user)s is already an owner" msgstr "%(user)s zaten bir sahip" -#: instances/views.py:2248 +#: instances/views.py:2267 #, python-format msgid "Console settings were not saved: %(errors)s" msgstr "Konsol ayarları kaydedilmedi: %(errors)s" -#: instances/views.py:2270 +#: instances/views.py:2289 msgid "" "Error setting console password. You should check that your instance have an " "graphic device." @@ -5961,67 +5986,67 @@ msgstr "" "Konsol parolası ayarlanırken hata oluştu. Sanal makinenin bir grafik " "aygıtına sahip olduğundan emin olunuz." -#: instances/views.py:2317 +#: instances/views.py:2336 msgid "Shut the VM down to change whether it is a template." msgstr "Şablon olup olmadığını değiştirmek için sanal makineyi kapatın." -#: instances/views.py:2324 +#: instances/views.py:2343 #, python-format msgid "User %(quota_msg)s quota reached, '%(instance_name)s' stays a template!" msgstr "" "Kullanıcının %(quota_msg)s kotası doldu, '%(instance_name)s' şablon olarak " "kalıyor!" -#: instances/views.py:2406 +#: instances/views.py:2425 msgid "The XML has no name" msgstr "XML'de ad yok" -#: instances/views.py:2420 webvirtcloud/middleware.py:75 +#: instances/views.py:2439 webvirtcloud/middleware.py:75 #, python-format msgid "libvirt Error - %(exception)s" msgstr "libvirt Hatası - %(exception)s" -#: instances/views.py:2427 +#: instances/views.py:2446 msgid "An empty disk of the size you set" msgstr "Belirlediğiniz boyutta boş bir disk" -#: instances/views.py:2428 +#: instances/views.py:2447 msgid "Volumes already in a pool" msgstr "Bir havuzda zaten bulunan birimler" -#: instances/views.py:2429 +#: instances/views.py:2448 msgid "A copy of a template volume" msgstr "Bir şablon biriminin kopyası" -#: instances/views.py:2518 +#: instances/views.py:2537 msgid "You haven't defined any storage pools" msgstr "Hiç bir depolama havuzu tanımlamadınız" -#: instances/views.py:2520 +#: instances/views.py:2539 msgid "You haven't defined any network pools" msgstr "Hiç bir ağ havuzu tanımlamadınız" -#: instances/views.py:2583 +#: instances/views.py:2602 msgid "Flavor Created" msgstr "Flavor Oluşturuldu" -#: instances/views.py:2591 +#: instances/views.py:2610 msgid "Create Flavor" msgstr "Kullanıcı Oluştur" -#: instances/views.py:2606 +#: instances/views.py:2625 msgid "Flavor Updated" msgstr "Flavor Güncellendi" -#: instances/views.py:2612 +#: instances/views.py:2631 msgid "Update Flavor" msgstr "Flavor Güncelle" -#: instances/views.py:2624 +#: instances/views.py:2643 msgid "Flavor Deleted" msgstr "Flavor Silindi" -#: instances/views.py:2630 +#: instances/views.py:2649 msgid "Only the size template goes; VMs made from it keep their size." msgstr "" "Yalnızca boyut şablonu silinir; ondan oluşturulan sanal makineler " diff --git a/vrtManager/instance.py b/vrtManager/instance.py index 00c6bebe..2983fa9f 100644 --- a/vrtManager/instance.py +++ b/vrtManager/instance.py @@ -1141,7 +1141,8 @@ class wvmInstance(wvmConnect): raise util.OperationError(f"Media of {dev} changed in the running VM only: {err}") from err raise - def mount_iso(self, dev, image): + def get_iso_path(self, image): + """The path of the ISO image of that name, in one of the active pools.""" paths = [] for storage in self.get_storages(only_actives=True): stg = self.get_storage(storage) @@ -1151,7 +1152,10 @@ class wvmInstance(wvmConnect): raise util.OperationError(f"ISO image {image} was not found") if len(paths) > 1: raise util.OperationError(f"ISO image {image} is in more than one storage pool") - self._set_cdrom_media(dev, paths[0]) + return paths[0] + + def mount_iso(self, dev, image): + self._set_cdrom_media(dev, self.get_iso_path(image)) def umount_iso(self, dev, image=None): self._set_cdrom_media(dev, None) @@ -1199,7 +1203,10 @@ class wvmInstance(wvmConnect): if detect_zeroes_mode is not None and detect_zeroes_mode != "default": additionals += f"detect_zeroes='{detect_zeroes_mode}' " - xml_disk = f"" + # a USB CD-ROM is a CD drive on usb-bot only: usb-storage, which libvirt + # takes where QEMU has no usb-bot (RHEL 9), shows the guest a hard disk + usb_cd = " model='usb-bot'" if disk_device == "cdrom" and target_bus == "usb" else "" + xml_disk = f"" if disk_device == "cdrom": xml_disk += f"" elif disk_device == "disk": diff --git a/vrtManager/tests/test_device_edits.py b/vrtManager/tests/test_device_edits.py index 0d0a9fbb..b4c3bb74 100644 --- a/vrtManager/tests/test_device_edits.py +++ b/vrtManager/tests/test_device_edits.py @@ -91,6 +91,16 @@ class DeviceEditsTestCase(unittest.TestCase): xml, _ = p.instance.attachDeviceFlags.call_args.args self.assertEqual(etree.fromstring(xml).find("source").attrib, {"dev": "/dev/vg/c"}) + def test_a_usb_cdrom_is_a_cd_drive_on_usb_bot(self): + # usb-storage, libvirt's choice where QEMU has no usb-bot, shows a hard disk + for bus, model in (("usb", "usb-bot"), ("sata", None)): + with self.subTest(bus=bus): + p = proxy(active=False) + p._definitions = lambda: [(PERSISTENT_XML, VIR_DOMAIN_AFFECT_CONFIG)] + p.attach_disk("sdb", "/iso/virtio-win.iso", disk_device="cdrom", target_bus=bus, readonly=True) + xml, _ = p.instance.attachDeviceFlags.call_args.args + self.assertEqual(etree.fromstring(xml).get("model"), model) + def test_a_bridge_nic_can_be_a_boot_device(self): p = proxy() p.set_bootorder({0: {"type": "network", "dev": "52:54:10:00:00:02"}, 1: {"type": "disk", "dev": "vdb"}})