mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
feat(instances): a new VM can have UEFI Secure Boot and a TPM 2.0
Windows 11 does not install without Secure Boot and a TPM 2.0, and the new VM form offered neither: its firmware list was BIOS and one UEFI loader guessed from file names, which on RHEL never matched the Secure Boot one. The firmware choices are now BIOS, UEFI and UEFI Secure Boot, as the host's libvirt reports them (its firmware features from libvirt 12.1, before that its loaders), and libvirt picks the firmware itself: UEFI without Secure Boot, or with Microsoft's keys enrolled. A TPM 2.0 switch, shown when the host has swtpm, adds an emulated TPM. The create job refuses a firmware or a TPM the host does not offer, and the API takes both. The path-guessing helpers are gone.
This commit is contained in:
parent
40db3dcf3d
commit
005fa59bf0
20 changed files with 399 additions and 228 deletions
|
|
@ -42,6 +42,7 @@ WebVirtCloud and `virt-manager`/`virsh` can manage the same libvirt host **concu
|
||||||
* Storage pool and volume management
|
* Storage pool and volume management
|
||||||
* Network (with DHCP leases), interface, network filter and secret management
|
* Network (with DHCP leases), interface, network filter and secret management
|
||||||
* PCI and USB devices of the host given to VMs
|
* PCI and USB devices of the host given to VMs
|
||||||
|
* UEFI Secure Boot and TPM 2.0 for new VMs, as Windows 11 needs
|
||||||
* Browser-based VNC console (noVNC) and serial console
|
* Browser-based VNC console (noVNC) and serial console
|
||||||
* Role-based access control: users only see the VMs assigned to them, within their quota
|
* Role-based access control: users only see the VMs assigned to them, within their quota
|
||||||
* 2FA (OTP / TOTP) and failed-login lockout
|
* 2FA (OTP / TOTP) and failed-login lockout
|
||||||
|
|
@ -238,6 +239,8 @@ A compute node is a KVM host the panel manages over libvirt.
|
||||||
# or, from a cloned repository: sudo sh ./dev/libvirt-bootstrap.sh
|
# or, from a cloned repository: sudo sh ./dev/libvirt-bootstrap.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The new VM form offers UEFI, UEFI Secure Boot (with Microsoft's keys, for Windows 11) and a TPM 2.0 only when the host's libvirt finds the UEFI firmware and swtpm: install `ovmf swtpm-tools` (Debian / Ubuntu) or `edk2-ovmf swtpm swtpm-tools` (RHEL family) if they are missing.
|
||||||
|
|
||||||
2. For SSH connections, give the user the panel runs as an SSH key and copy it to the compute node. **Use only the block for your installation type:** a key set up with the host commands is not seen by the panel in Docker. `ssh-copy-id` asks for the compute node's root password once. `accept-new` accepts a new host's key on first connect and refuses a changed one.
|
2. For SSH connections, give the user the panel runs as an SSH key and copy it to the compute node. **Use only the block for your installation type:** a key set up with the host commands is not seen by the panel in Docker. `ssh-copy-id` asks for the compute node's root password once. `accept-new` accepts a new host's key on first connect and refuses a changed one.
|
||||||
|
|
||||||
**Installer or manual installation**, on the panel host:
|
**Installer or manual installation**, on the panel host:
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,8 @@ class DestroyOptionsSerializer(serializers.Serializer):
|
||||||
|
|
||||||
|
|
||||||
class CreateInstanceSerializer(serializers.ModelSerializer):
|
class CreateInstanceSerializer(serializers.ModelSerializer):
|
||||||
|
# BIOS, UEFI or UEFI Secure Boot, as the host offers; left out: the machine's default
|
||||||
|
firmware = serializers.CharField(required=False)
|
||||||
video = serializers.CharField(initial="vga")
|
video = serializers.CharField(initial="vga")
|
||||||
storage = serializers.CharField(initial="default")
|
storage = serializers.CharField(initial="default")
|
||||||
cache_mode = serializers.CharField(initial="none")
|
cache_mode = serializers.CharField(initial="none")
|
||||||
|
|
@ -90,6 +92,8 @@ class CreateInstanceSerializer(serializers.ModelSerializer):
|
||||||
model = CreateInstance
|
model = CreateInstance
|
||||||
fields = [
|
fields = [
|
||||||
"name",
|
"name",
|
||||||
|
"firmware",
|
||||||
|
"tpm",
|
||||||
"vcpu",
|
"vcpu",
|
||||||
"vcpu_mode",
|
"vcpu_mode",
|
||||||
"memory",
|
"memory",
|
||||||
|
|
|
||||||
|
|
@ -665,11 +665,11 @@ class CreateInstance(Kind):
|
||||||
def normalize(self, raw):
|
def normalize(self, raw):
|
||||||
# what the API leaves out means what create_instance did by default
|
# what the API leaves out means what create_instance did by default
|
||||||
params = {"template": "", "images": "", "firmware": "", "net_model": "virtio", "add_cdrom": "sata",
|
params = {"template": "", "images": "", "firmware": "", "net_model": "virtio", "add_cdrom": "sata",
|
||||||
"add_input": "default", "meta_prealloc": False, "hdd_size": 0, "cloud_init": False}
|
"add_input": "default", "meta_prealloc": False, "hdd_size": 0, "cloud_init": False, "tpm": False}
|
||||||
params.update({k: v for k, v in raw.items() if k not in ("submission_key", "create", "csrfmiddlewaretoken")})
|
params.update({k: v for k, v in raw.items() if k not in ("submission_key", "create", "csrfmiddlewaretoken")})
|
||||||
params.update(compute=int(raw["compute"]), devices=[list(d) for d in raw.get("devices", [])])
|
params.update(compute=int(raw["compute"]), devices=[list(d) for d in raw.get("devices", [])])
|
||||||
if "UEFI" in params["firmware"] and "secboot" in params["firmware"].partition(":")[2]:
|
if params["firmware"] == "UEFI Secure Boot" and "q35" not in params["machine"]:
|
||||||
params["machine"] = "q35" # a secure boot loader needs SMM, on q35 only
|
params["machine"] = "q35" # Secure Boot needs SMM, on q35 only
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def validate(self, user, params):
|
def validate(self, user, params):
|
||||||
|
|
@ -708,8 +708,10 @@ class CreateInstance(Kind):
|
||||||
utils.nic_macs(data["mac"], data["networks"])
|
utils.nic_macs(data["mac"], data["networks"])
|
||||||
except ValueError as err:
|
except ValueError as err:
|
||||||
raise util.OperationError(str(err))
|
raise util.OperationError(str(err))
|
||||||
if "UEFI" in data["firmware"] and ":" not in data["firmware"]:
|
if data["firmware"] and data["firmware"] not in conn.get_firmwares(data["arch"], data["machine"]):
|
||||||
raise util.OperationError(_("Invalid firmware"))
|
raise util.OperationError(_("This host offers no firmware %(firmware)s") % {"firmware": data["firmware"]})
|
||||||
|
if data["tpm"] and not conn.get_tpm_model(data["arch"], data["machine"]):
|
||||||
|
raise util.OperationError(_("This host offers no TPM 2.0: install swtpm on it"))
|
||||||
|
|
||||||
def plan(self, user, params):
|
def plan(self, user, params):
|
||||||
compute = Compute.objects.get(pk=params["compute"])
|
compute = Compute.objects.get(pk=params["compute"])
|
||||||
|
|
@ -727,13 +729,6 @@ class CreateInstance(Kind):
|
||||||
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
default_disk_owner_uid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_UID)
|
||||||
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
default_disk_owner_gid = int(app_settings.INSTANCE_VOLUME_DEFAULT_OWNER_GID)
|
||||||
|
|
||||||
firmware = dict()
|
|
||||||
if "UEFI" in data["firmware"]:
|
|
||||||
firmware["loader"] = data["firmware"].split(":")[1].strip()
|
|
||||||
firmware["secure"] = "yes" if "secboot" in firmware["loader"] else "no"
|
|
||||||
firmware["readonly"] = "yes"
|
|
||||||
firmware["type"] = "pflash"
|
|
||||||
|
|
||||||
def volume(path, device="disk", bus=default_bus):
|
def volume(path, device="disk", bus=default_bus):
|
||||||
return {
|
return {
|
||||||
"path": path,
|
"path": path,
|
||||||
|
|
@ -783,7 +778,7 @@ class CreateInstance(Kind):
|
||||||
uuid=op.intent["uuid"],
|
uuid=op.intent["uuid"],
|
||||||
arch=data["arch"],
|
arch=data["arch"],
|
||||||
machine=data["machine"],
|
machine=data["machine"],
|
||||||
firmware=firmware,
|
firmware=data["firmware"],
|
||||||
volumes=volume_list,
|
volumes=volume_list,
|
||||||
networks=data["networks"],
|
networks=data["networks"],
|
||||||
virtio=data["virtio"],
|
virtio=data["virtio"],
|
||||||
|
|
@ -796,6 +791,7 @@ class CreateInstance(Kind):
|
||||||
qemu_ga=data["qemu_ga"],
|
qemu_ga=data["qemu_ga"],
|
||||||
add_cdrom=data["add_cdrom"],
|
add_cdrom=data["add_cdrom"],
|
||||||
add_input=data["add_input"],
|
add_input=data["add_input"],
|
||||||
|
tpm_model=conn.get_tpm_model(data["arch"], data["machine"]) if data["tpm"] else None,
|
||||||
)
|
)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
# the VM is not defined: its new volumes go
|
# the VM is not defined: its new volumes go
|
||||||
|
|
|
||||||
|
|
@ -402,6 +402,8 @@ class CreateInstance(models.Model):
|
||||||
error_messages={"required": _("No Virtual Machine name has been entered")},
|
error_messages={"required": _("No Virtual Machine name has been entered")},
|
||||||
)
|
)
|
||||||
firmware = models.CharField(max_length=64)
|
firmware = models.CharField(max_length=64)
|
||||||
|
# an emulated TPM 2.0, as Windows 11 needs
|
||||||
|
tpm = models.BooleanField(default=False)
|
||||||
vcpu = models.IntegerField(
|
vcpu = models.IntegerField(
|
||||||
error_messages={"required": _("No VCPU has been entered")}, validators=[MinValueValidator(1)]
|
error_messages={"required": _("No VCPU has been entered")}, validators=[MinValueValidator(1)]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,13 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
{% field_error vm_form.firmware "vm_firmware" %}
|
{% field_error vm_form.firmware "vm_firmware" %}
|
||||||
|
{% if tpm_support %}
|
||||||
|
<div class="form-check mt-2">
|
||||||
|
<input id="vm_tpm" class="form-check-input" type="checkbox" name="tpm" value="true"{% if vm_form.tpm.value %} checked{% endif %} aria-describedby="vm_tpm_help">
|
||||||
|
<label for="vm_tpm" class="form-check-label">{% trans "TPM 2.0" %}</label>
|
||||||
|
<div id="vm_tpm_help" class="form-text">{% trans "Windows 11 needs it, with UEFI Secure Boot" %}</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if dom_caps.cpu_modes %}
|
{% if dom_caps.cpu_modes %}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ class CloudInitCreateTestCase(TransactionTestCase):
|
||||||
self.addCleanup(patcher.stop)
|
self.addCleanup(patcher.stop)
|
||||||
self.conn.get_instances.return_value = []
|
self.conn.get_instances.return_value = []
|
||||||
self.conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
self.conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
||||||
|
self.conn.get_firmwares.return_value = ["BIOS", "UEFI", "UEFI Secure Boot"]
|
||||||
self.conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model"]}
|
self.conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model"]}
|
||||||
self.conn.get_disk_bus_types.return_value = ["virtio", "sata"]
|
self.conn.get_disk_bus_types.return_value = ["virtio", "sata"]
|
||||||
self.conn.create_volume.return_value = "/srv/default/api-vm.qcow2"
|
self.conn.create_volume.return_value = "/srv/default/api-vm.qcow2"
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ def defined_cpu(vcpu_mode):
|
||||||
patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS):
|
patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS):
|
||||||
conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode=vcpu_mode,
|
conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode=vcpu_mode,
|
||||||
uuid="00000000-0000-4000-8000-0000000000c1", arch="x86_64", machine="q35",
|
uuid="00000000-0000-4000-8000-0000000000c1", arch="x86_64", machine="q35",
|
||||||
firmware={}, volumes=[], networks="", nwfilter="", virtio=True, listener_addr="0.0.0.0",
|
firmware="BIOS", volumes=[], networks="", nwfilter="", virtio=True, listener_addr="0.0.0.0",
|
||||||
add_cdrom="None", add_input="None")
|
add_cdrom="None", add_input="None")
|
||||||
return etree.fromstring(conn.wvm.defineXML.call_args.args[0]).find("cpu")
|
return etree.fromstring(conn.wvm.defineXML.call_args.args[0]).find("cpu")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -182,8 +182,8 @@ class CreateFormValuesTests(TestCase):
|
||||||
self.assertIn("Networks: This field is required.", self.summary(page))
|
self.assertIn("Networks: This field is required.", self.summary(page))
|
||||||
|
|
||||||
def test_a_firmware_error_without_a_firmware_choice_is_in_the_summary(self):
|
def test_a_firmware_error_without_a_firmware_choice_is_in_the_summary(self):
|
||||||
# an architecture with no firmware label renders no firmware select
|
# an architecture with no firmware choice renders no firmware select
|
||||||
with patch("vrtManager.connection.wvmConnect.label_for_firmware_path", return_value=None):
|
with patch("vrtManager.connection.wvmConnect.get_firmwares", return_value=[]):
|
||||||
fields = valid()
|
fields = valid()
|
||||||
del fields["firmware"]
|
del fields["firmware"]
|
||||||
_, response = render_create_page(self, fields)
|
_, response = render_create_page(self, fields)
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,24 @@ class CreateInstanceViewTests(TestCase):
|
||||||
|
|
||||||
@patch("instances.views.wvmCreate", new=wvm_create)
|
@patch("instances.views.wvmCreate", new=wvm_create)
|
||||||
@patch("instances.views.NewVMForm")
|
@patch("instances.views.NewVMForm")
|
||||||
def test_secure_boot_loader_on_q35_requests_secure_boot(self, form_class):
|
def test_secure_boot_and_a_tpm_reach_the_definition(self, form_class):
|
||||||
_, conn = self.create(form_class, firmware="UEFI x86_64: /usr/share/edk2/ovmf/OVMF_CODE.secboot.fd")
|
_, conn = self.create(form_class, firmware="UEFI Secure Boot", tpm=True)
|
||||||
run(Operation.objects.get())
|
run(Operation.objects.get())
|
||||||
firmware = conn.create_instance.call_args.kwargs["firmware"]
|
kwargs = conn.create_instance.call_args.kwargs
|
||||||
self.assertEqual(firmware["secure"], "yes")
|
self.assertEqual((kwargs["firmware"], kwargs["machine"], kwargs["tpm_model"]),
|
||||||
|
("UEFI Secure Boot", "q35", "tpm-crb"))
|
||||||
|
|
||||||
|
@patch("instances.views.wvmCreate", new=wvm_create)
|
||||||
|
@patch("instances.views.NewVMForm")
|
||||||
|
def test_a_firmware_or_a_tpm_the_host_does_not_offer_is_refused(self, form_class):
|
||||||
|
for data, host in (({"firmware": "UEFI Secure Boot"}, {"get_firmwares": ["BIOS", "UEFI"]}),
|
||||||
|
({"tpm": True}, {"get_tpm_model": None})):
|
||||||
|
with self.subTest(data):
|
||||||
|
Instance.objects.all().delete()
|
||||||
|
response, conn = self.create(form_class, host=host, **data)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertFalse(Operation.objects.exists())
|
||||||
|
conn.create_instance.assert_not_called()
|
||||||
|
|
||||||
@patch("instances.views.wvmCreate", new=wvm_create)
|
@patch("instances.views.wvmCreate", new=wvm_create)
|
||||||
@patch("instances.views.NewVMForm")
|
@patch("instances.views.NewVMForm")
|
||||||
|
|
@ -71,7 +84,9 @@ class CreateInstanceViewTests(TestCase):
|
||||||
conn.create_volume.assert_not_called()
|
conn.create_volume.assert_not_called()
|
||||||
conn.create_instance.assert_not_called()
|
conn.create_instance.assert_not_called()
|
||||||
|
|
||||||
def create(self, form_class, **data):
|
def create(self, form_class, host=None, **data):
|
||||||
|
"""POST the create form with data over the form's defaults; host: what
|
||||||
|
the mocked host answers, over its defaults."""
|
||||||
user = get_user_model().objects.filter(username="create-test-admin").first() or (
|
user = get_user_model().objects.filter(username="create-test-admin").first() or (
|
||||||
get_user_model().objects.create_superuser(
|
get_user_model().objects.create_superuser(
|
||||||
username="create-test-admin", password="password", email="create@example.com"
|
username="create-test-admin", password="password", email="create@example.com"
|
||||||
|
|
@ -95,8 +110,10 @@ class CreateInstanceViewTests(TestCase):
|
||||||
conn.get_storages.return_value = ["default"]
|
conn.get_storages.return_value = ["default"]
|
||||||
conn.get_dom_capabilities.return_value = {"loader_enums": [], "loaders": [], "cpu_modes": ["host-model"]}
|
conn.get_dom_capabilities.return_value = {"loader_enums": [], "loaders": [], "cpu_modes": ["host-model"]}
|
||||||
conn.get_capabilities.return_value = {}
|
conn.get_capabilities.return_value = {}
|
||||||
conn.label_for_firmware_path.return_value = None
|
conn.get_firmwares.return_value = ["BIOS", "UEFI", "UEFI Secure Boot"]
|
||||||
conn.find_uefi_path_for_arch.return_value = None
|
conn.get_tpm_model.return_value = "tpm-crb"
|
||||||
|
for method, value in (host or {}).items():
|
||||||
|
getattr(conn, method).return_value = value
|
||||||
conn.get_volume_path.return_value = "/var/lib/libvirt/images/base.qcow2"
|
conn.get_volume_path.return_value = "/var/lib/libvirt/images/base.qcow2"
|
||||||
conn.get_volume_format_type.return_value = "qcow2"
|
conn.get_volume_format_type.return_value = "qcow2"
|
||||||
|
|
||||||
|
|
@ -110,7 +127,7 @@ class CreateInstanceViewTests(TestCase):
|
||||||
"virtio": True, "listener_addr": "0.0.0.0", "nwfilter": "",
|
"virtio": True, "listener_addr": "0.0.0.0", "nwfilter": "",
|
||||||
"graphics": "vnc", "video": "vga",
|
"graphics": "vnc", "video": "vga",
|
||||||
"mac": "52:54:00:00:00:01", "qemu_ga": False,
|
"mac": "52:54:00:00:00:01", "qemu_ga": False,
|
||||||
"add_cdrom": "sata", "add_input": "default",
|
"add_cdrom": "sata", "add_input": "default", "tpm": False,
|
||||||
**data,
|
**data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,13 +155,13 @@ class CreateInstanceViewTests(TestCase):
|
||||||
# A host without loader support has no "loaders"/"loader_enums" keys
|
# A host without loader support has no "loaders"/"loader_enums" keys
|
||||||
conn.get_dom_capabilities.return_value = {"loader_support": "no", "cpu_modes": ["host-model"]}
|
conn.get_dom_capabilities.return_value = {"loader_support": "no", "cpu_modes": ["host-model"]}
|
||||||
conn.get_capabilities.return_value = {}
|
conn.get_capabilities.return_value = {}
|
||||||
conn.label_for_firmware_path.return_value = None
|
conn.get_firmwares.return_value = ["BIOS"]
|
||||||
conn.find_uefi_path_for_arch.return_value = None
|
conn.get_tpm_model.return_value = None
|
||||||
|
|
||||||
response = self.client.get(reverse("instances:create_instance", args=[compute.id, "x86_64", "q35"]))
|
response = self.client.get(reverse("instances:create_instance", args=[compute.id, "x86_64", "q35"]))
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
conn.find_uefi_path_for_arch.assert_called_once_with("x86_64", "q35")
|
conn.get_firmwares.assert_called_once_with("x86_64", "q35")
|
||||||
|
|
||||||
|
|
||||||
class NicMacsTestCase(TestCase):
|
class NicMacsTestCase(TestCase):
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ class CreateJobTestCase(TransactionTestCase):
|
||||||
self.addCleanup(patcher.stop)
|
self.addCleanup(patcher.stop)
|
||||||
self.conn.get_instances.return_value = []
|
self.conn.get_instances.return_value = []
|
||||||
self.conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
self.conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
||||||
|
self.conn.get_firmwares.return_value = ["BIOS", "UEFI", "UEFI Secure Boot"]
|
||||||
self.conn.get_disk_bus_types.return_value = ["virtio", "sata"]
|
self.conn.get_disk_bus_types.return_value = ["virtio", "sata"]
|
||||||
self.conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model", "custom"],
|
self.conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model", "custom"],
|
||||||
"cpu_custom_models": ["Skylake-Server-noTSX-IBRS"]}
|
"cpu_custom_models": ["Skylake-Server-noTSX-IBRS"]}
|
||||||
|
|
@ -51,6 +52,19 @@ class CreateJobTestCase(TransactionTestCase):
|
||||||
self.assertEqual(self.conn.create_instance.call_args.kwargs["uuid"], op.intent["uuid"])
|
self.assertEqual(self.conn.create_instance.call_args.kwargs["uuid"], op.intent["uuid"])
|
||||||
self.assertTrue(Instance.objects.filter(uuid=op.intent["uuid"], name="api-vm").exists())
|
self.assertTrue(Instance.objects.filter(uuid=op.intent["uuid"], name="api-vm").exists())
|
||||||
|
|
||||||
|
def test_the_api_takes_secure_boot_and_a_tpm(self):
|
||||||
|
url = f"/api/v1/computes/{self.compute.pk}/instances/create/x86_64/pc/"
|
||||||
|
body = {**BODY, "firmware": "UEFI Secure Boot", "tpm": True}
|
||||||
|
self.assertEqual(self.client.post(url, body, content_type="application/json").status_code, 202)
|
||||||
|
params = Operation.objects.get().params
|
||||||
|
self.assertEqual((params["firmware"], params["tpm"], params["machine"]), ("UEFI Secure Boot", True, "q35"))
|
||||||
|
|
||||||
|
def test_secure_boot_keeps_a_versioned_q35_machine(self):
|
||||||
|
url = f"/api/v1/computes/{self.compute.pk}/instances/create/x86_64/pc-q35-9.2/"
|
||||||
|
body = {**BODY, "firmware": "UEFI Secure Boot"}
|
||||||
|
self.assertEqual(self.client.post(url, body, content_type="application/json").status_code, 202)
|
||||||
|
self.assertEqual(Operation.objects.get().params["machine"], "pc-q35-9.2")
|
||||||
|
|
||||||
def test_a_refused_create_queues_nothing(self):
|
def test_a_refused_create_queues_nothing(self):
|
||||||
url = f"/api/v1/computes/{self.compute.pk}/instances/create/x86_64/q35/"
|
url = f"/api/v1/computes/{self.compute.pk}/instances/create/x86_64/q35/"
|
||||||
response = self.client.post(url, {**BODY, "cache_mode": "writeback"}, content_type="application/json")
|
response = self.client.post(url, {**BODY, "cache_mode": "writeback"}, content_type="application/json")
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ class CreateRollbackTestCase(TestCase):
|
||||||
with patch("instances.jobs.wvmCreate") as wvm:
|
with patch("instances.jobs.wvmCreate") as wvm:
|
||||||
conn = wvm.return_value
|
conn = wvm.return_value
|
||||||
conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
conn.get_cache_modes.return_value = {"none": "Disable cache"}
|
||||||
|
conn.get_firmwares.return_value = ["BIOS", "UEFI", "UEFI Secure Boot"]
|
||||||
conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model"]}
|
conn.get_dom_capabilities.return_value = {"cpu_modes": ["host-model"]}
|
||||||
conn.get_instances.return_value = []
|
conn.get_instances.return_value = []
|
||||||
conn.create_volume.return_value = "/pool/rb-vm.qcow2"
|
conn.create_volume.return_value = "/pool/rb-vm.qcow2"
|
||||||
|
|
|
||||||
126
instances/tests/test_firmware_tpm.py
Normal file
126
instances/tests/test_firmware_tpm.py
Normal file
|
|
@ -0,0 +1,126 @@
|
||||||
|
"""A new VM's firmware and TPM: the choices come from the host's domain
|
||||||
|
capabilities, UEFI is libvirt's firmware autoselection (Secure Boot with
|
||||||
|
Microsoft's keys enrolled, as Windows 11 needs) and a TPM is an emulated
|
||||||
|
TPM 2.0 (swtpm)."""
|
||||||
|
|
||||||
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
|
from django.test import SimpleTestCase
|
||||||
|
from lxml import etree
|
||||||
|
from vrtManager.connection import wvmConnect
|
||||||
|
from vrtManager.create import wvmCreate
|
||||||
|
|
||||||
|
FULL = """<domainCapabilities><os supported='yes'>
|
||||||
|
<enum name='firmware'><value>efi</value></enum>
|
||||||
|
<firmwareFeatures supported='yes'>
|
||||||
|
<enum name='secureBoot'><value>yes</value><value>no</value></enum>
|
||||||
|
<enum name='enrolledKeys'><value>yes</value><value>no</value></enum>
|
||||||
|
</firmwareFeatures>
|
||||||
|
</os><devices><tpm supported='yes'>
|
||||||
|
<enum name='model'><value>tpm-tis</value><value>tpm-crb</value></enum>
|
||||||
|
<enum name='backendModel'><value>emulator</value><value>external</value></enum>
|
||||||
|
<enum name='backendVersion'><value>2.0</value></enum>
|
||||||
|
</tpm></devices></domainCapabilities>"""
|
||||||
|
|
||||||
|
# Secure Boot firmware without Microsoft's keys, and no swtpm
|
||||||
|
BARE = """<domainCapabilities><os supported='yes'>
|
||||||
|
<enum name='firmware'><value>efi</value></enum>
|
||||||
|
<firmwareFeatures supported='yes'>
|
||||||
|
<enum name='secureBoot'><value>yes</value><value>no</value></enum>
|
||||||
|
<enum name='enrolledKeys'><value>no</value></enum>
|
||||||
|
</firmwareFeatures>
|
||||||
|
</os><devices><tpm supported='no'/></devices></domainCapabilities>"""
|
||||||
|
|
||||||
|
# only Secure Boot firmware, and a TPM of another machine
|
||||||
|
SECURE_ONLY = """<domainCapabilities><os supported='yes'>
|
||||||
|
<enum name='firmware'><value>efi</value></enum>
|
||||||
|
<firmwareFeatures supported='yes'>
|
||||||
|
<enum name='secureBoot'><value>yes</value></enum>
|
||||||
|
<enum name='enrolledKeys'><value>yes</value></enum>
|
||||||
|
</firmwareFeatures>
|
||||||
|
</os><devices><tpm supported='yes'>
|
||||||
|
<enum name='model'><value>tpm-spapr</value></enum>
|
||||||
|
<enum name='backendModel'><value>emulator</value></enum>
|
||||||
|
<enum name='backendVersion'><value>2.0</value></enum>
|
||||||
|
</tpm></devices></domainCapabilities>"""
|
||||||
|
|
||||||
|
# libvirt before 12.1: no firmwareFeatures, the loaders say whether they do Secure Boot
|
||||||
|
EARLIER = """<domainCapabilities><os supported='yes'>
|
||||||
|
<enum name='firmware'><value>efi</value></enum>
|
||||||
|
<loader supported='yes'><enum name='secure'><value>yes</value><value>no</value></enum></loader>
|
||||||
|
</os></domainCapabilities>"""
|
||||||
|
EARLIER_PLAIN = """<domainCapabilities><os supported='yes'>
|
||||||
|
<enum name='firmware'><value>efi</value></enum>
|
||||||
|
<loader supported='yes'/>
|
||||||
|
</os></domainCapabilities>"""
|
||||||
|
|
||||||
|
DOM_CAPS = {"domain": "kvm", "os_support": "yes", "cpu_modes": ["host-model"]}
|
||||||
|
|
||||||
|
|
||||||
|
def host(dom_cap_xml):
|
||||||
|
conn = wvmConnect.__new__(wvmConnect)
|
||||||
|
conn.get_dom_cap_xml = lambda arch, machine: dom_cap_xml
|
||||||
|
return conn
|
||||||
|
|
||||||
|
|
||||||
|
def defined(firmware, tpm_model=None):
|
||||||
|
conn = wvmCreate.__new__(wvmCreate)
|
||||||
|
conn.wvm = MagicMock()
|
||||||
|
with patch.object(wvmCreate, "get_capabilities", return_value={"features": ["acpi"], "os_type": "hvm"}), \
|
||||||
|
patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS):
|
||||||
|
conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode="host-model",
|
||||||
|
uuid="00000000-0000-4000-8000-0000000000c2", arch="x86_64", machine="q35",
|
||||||
|
firmware=firmware, volumes=[], networks="", nwfilter="", virtio=True,
|
||||||
|
listener_addr="0.0.0.0", add_cdrom="None", add_input="None", tpm_model=tpm_model)
|
||||||
|
return etree.fromstring(conn.wvm.defineXML.call_args.args[0])
|
||||||
|
|
||||||
|
|
||||||
|
class HostOffersTests(SimpleTestCase):
|
||||||
|
def test_a_host_with_enrolled_keys_and_swtpm_offers_everything(self):
|
||||||
|
self.assertEqual(host(FULL).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI", "UEFI Secure Boot"])
|
||||||
|
self.assertEqual(host(FULL).get_tpm_model("x86_64", "q35"), "tpm-crb")
|
||||||
|
|
||||||
|
def test_secure_boot_without_microsofts_keys_and_no_swtpm_are_not_offered(self):
|
||||||
|
self.assertEqual(host(BARE).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI"])
|
||||||
|
self.assertIsNone(host(BARE).get_tpm_model("x86_64", "q35"))
|
||||||
|
|
||||||
|
def test_bios_is_x86_only_and_secure_boot_x86_64_only(self):
|
||||||
|
self.assertEqual(host(FULL).get_firmwares("aarch64", "virt"), ["UEFI"])
|
||||||
|
|
||||||
|
def test_uefi_is_offered_only_where_secure_boot_can_be_off(self):
|
||||||
|
self.assertEqual(host(SECURE_ONLY).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI Secure Boot"])
|
||||||
|
|
||||||
|
def test_an_earlier_libvirt_offers_what_its_loaders_do(self):
|
||||||
|
self.assertEqual(host(EARLIER).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI", "UEFI Secure Boot"])
|
||||||
|
self.assertEqual(host(EARLIER_PLAIN).get_firmwares("x86_64", "q35"), ["BIOS", "UEFI"])
|
||||||
|
|
||||||
|
def test_a_machine_without_crb_or_tis_takes_its_own_tpm_model(self):
|
||||||
|
self.assertEqual(host(SECURE_ONLY).get_tpm_model("ppc64le", "pseries"), "tpm-spapr")
|
||||||
|
|
||||||
|
|
||||||
|
class DefinitionTests(SimpleTestCase):
|
||||||
|
def features(self, tree):
|
||||||
|
return {f.get("name"): f.get("enabled") for f in tree.findall("os/firmware/feature")}
|
||||||
|
|
||||||
|
def test_bios_leaves_the_firmware_to_the_machine(self):
|
||||||
|
tree = defined("BIOS")
|
||||||
|
self.assertIsNone(tree.find("os").get("firmware"))
|
||||||
|
self.assertEqual((self.features(tree), tree.find("features/smm")), ({}, None))
|
||||||
|
|
||||||
|
def test_uefi_is_picked_by_libvirt_without_secure_boot(self):
|
||||||
|
tree = defined("UEFI")
|
||||||
|
self.assertEqual(tree.find("os").get("firmware"), "efi")
|
||||||
|
self.assertEqual(self.features(tree), {"secure-boot": "no"})
|
||||||
|
self.assertIsNone(tree.find("features/smm"))
|
||||||
|
|
||||||
|
def test_secure_boot_has_microsofts_keys_and_smm(self):
|
||||||
|
tree = defined("UEFI Secure Boot")
|
||||||
|
self.assertEqual(tree.find("os").get("firmware"), "efi")
|
||||||
|
self.assertEqual(self.features(tree), {"secure-boot": "yes", "enrolled-keys": "yes"})
|
||||||
|
self.assertEqual(tree.find("features/smm").get("state"), "on")
|
||||||
|
|
||||||
|
def test_a_tpm_is_an_emulated_tpm_2(self):
|
||||||
|
self.assertEqual(defined("UEFI").findall("devices/tpm"), [])
|
||||||
|
tpm = defined("UEFI Secure Boot", tpm_model="tpm-crb").find("devices/tpm")
|
||||||
|
self.assertEqual((tpm.get("model"), tpm.find("backend").get("type"), tpm.find("backend").get("version")),
|
||||||
|
("tpm-crb", "emulator", "2.0"))
|
||||||
|
|
@ -117,8 +117,8 @@ class LiveCreateTestCase(TransactionTestCase):
|
||||||
def test_invalid_cache_mode_allocates_nothing(self):
|
def test_invalid_cache_mode_allocates_nothing(self):
|
||||||
self.assert_nothing_left("cr-cache", self.create("cr-cache", cache_mode="bogus"))
|
self.assert_nothing_left("cr-cache", self.create("cr-cache", cache_mode="bogus"))
|
||||||
|
|
||||||
def test_malformed_uefi_firmware_allocates_nothing(self):
|
def test_a_firmware_the_host_does_not_offer_allocates_nothing(self):
|
||||||
self.assert_nothing_left("cr-uefi", self.create("cr-uefi", firmware="UEFI"))
|
self.assert_nothing_left("cr-uefi", self.create("cr-uefi", firmware="Coreboot"))
|
||||||
|
|
||||||
def test_rejected_definition_removes_the_new_disk(self):
|
def test_rejected_definition_removes_the_new_disk(self):
|
||||||
self.assert_nothing_left("cr-define", self.create("cr-define", video="no-such-model"))
|
self.assert_nothing_left("cr-define", self.create("cr-define", video="no-such-model"))
|
||||||
|
|
@ -129,36 +129,37 @@ class LiveCreateTestCase(TransactionTestCase):
|
||||||
self.assertTrue(self.domain_exists(P + "cr-ok"))
|
self.assertTrue(self.domain_exists(P + "cr-ok"))
|
||||||
self.assertEqual(len(self.volumes()), 1)
|
self.assertEqual(len(self.volumes()), 1)
|
||||||
|
|
||||||
def test_offered_uefi_firmware_boots_on_q35(self):
|
def offered(self):
|
||||||
page = self.client.get(reverse("instances:create_instance", args=[self.compute.id, "x86_64", "q35"]))
|
page = self.client.get(reverse("instances:create_instance", args=[self.compute.id, "x86_64", "q35"]))
|
||||||
uefi = [f for f in page.context["firmwares"] if f.startswith("UEFI")]
|
return page.context["firmwares"], page.context["tpm_support"]
|
||||||
self.assertTrue(uefi, "no UEFI firmware offered for q35")
|
|
||||||
|
|
||||||
self.create("cr-uefi-ok", firmware=uefi[0])
|
def test_uefi_boots_without_secure_boot(self):
|
||||||
|
self.assertIn("UEFI", self.offered()[0], "no UEFI offered for q35")
|
||||||
|
|
||||||
|
self.create("cr-uefi-ok", firmware="UEFI")
|
||||||
|
|
||||||
dom = self.conn.lookupByName(P + "cr-uefi-ok")
|
dom = self.conn.lookupByName(P + "cr-uefi-ok")
|
||||||
loader = etree.fromstring(dom.XMLDesc(0)).findtext("os/loader")
|
|
||||||
self.assertEqual(loader, uefi[0].split(":", 1)[1].strip())
|
|
||||||
# Listed by libvirt for q35, so the domain starts with it
|
|
||||||
caps = etree.fromstring(self.conn.getDomainCapabilities(None, "x86_64", "q35", "kvm"))
|
|
||||||
self.assertIn(loader, caps.xpath("os/loader/value/text()"))
|
|
||||||
dom.create()
|
dom.create()
|
||||||
self.assertTrue(dom.isActive())
|
tree = etree.fromstring(dom.XMLDesc(0))
|
||||||
|
self.assertEqual(tree.find("os").get("firmware"), "efi")
|
||||||
|
self.assertNotEqual(tree.find("os/loader").get("secure"), "yes")
|
||||||
|
self.assertTrue(tree.findtext("os/nvram"))
|
||||||
|
|
||||||
def test_secure_boot_loader_on_q35_gets_secure_boot(self):
|
def test_secure_boot_with_a_tpm_boots_as_windows_11_needs(self):
|
||||||
caps = etree.fromstring(self.conn.getDomainCapabilities(None, "x86_64", "q35", "kvm"))
|
firmwares, tpm_support = self.offered()
|
||||||
secboot = [p for p in caps.xpath("os/loader/value/text()") if "secboot" in p and "OVMF_CODE" in p]
|
if "UEFI Secure Boot" not in firmwares or not tpm_support:
|
||||||
if not secboot:
|
self.skipTest("the host offers no Secure Boot with Microsoft's keys, or no swtpm")
|
||||||
self.skipTest("the host has no secure boot loader for q35")
|
|
||||||
|
|
||||||
self.create("cr-secboot", firmware=f"UEFI x86_64: {secboot[0]}")
|
self.create("cr-secboot", firmware="UEFI Secure Boot", tpm="true")
|
||||||
|
|
||||||
dom = self.conn.lookupByName(P + "cr-secboot")
|
dom = self.conn.lookupByName(P + "cr-secboot")
|
||||||
|
dom.create()
|
||||||
tree = etree.fromstring(dom.XMLDesc(0))
|
tree = etree.fromstring(dom.XMLDesc(0))
|
||||||
self.assertEqual(tree.find("os/loader").get("secure"), "yes")
|
self.assertEqual(tree.find("os/loader").get("secure"), "yes")
|
||||||
self.assertEqual(tree.find("features/smm").get("state"), "on")
|
self.assertEqual(tree.find("features/smm").get("state"), "on")
|
||||||
dom.create()
|
# the variables libvirt chose come with Microsoft's keys enrolled
|
||||||
self.assertTrue(dom.isActive())
|
self.assertEqual(tree.xpath("os/firmware/feature[@name='enrolled-keys']/@enabled"), ["yes"])
|
||||||
|
self.assertEqual(tree.xpath("devices/tpm/backend/@version"), ["2.0"])
|
||||||
|
|
||||||
def test_every_offered_cdrom_bus_creates_a_vm_that_starts(self):
|
def test_every_offered_cdrom_bus_creates_a_vm_that_starts(self):
|
||||||
page = self.client.get(reverse("instances:create_instance", args=[self.compute.id, "x86_64", "q35"]))
|
page = self.client.get(reverse("instances:create_instance", args=[self.compute.id, "x86_64", "q35"]))
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ def defined_controllers(machine):
|
||||||
patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS):
|
patch.object(wvmCreate, "get_dom_capabilities", return_value=DOM_CAPS):
|
||||||
conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode="host-model",
|
conn.create_instance(name="vm-1", memory=512, vcpu=1, vcpu_mode="host-model",
|
||||||
uuid="00000000-0000-4000-8000-0000000000c2", arch="x86_64", machine=machine,
|
uuid="00000000-0000-4000-8000-0000000000c2", arch="x86_64", machine=machine,
|
||||||
firmware={}, volumes=[], networks="default", nwfilter="", virtio=True,
|
firmware="BIOS", volumes=[], networks="default", nwfilter="", virtio=True,
|
||||||
listener_addr="0.0.0.0", add_cdrom="None", add_input="None")
|
listener_addr="0.0.0.0", add_cdrom="None", add_input="None")
|
||||||
domain = etree.fromstring(conn.wvm.defineXML.call_args.args[0])
|
domain = etree.fromstring(conn.wvm.defineXML.call_args.args[0])
|
||||||
return [c.get("model") for c in domain.findall("devices/controller[@type='pci']")]
|
return [c.get("model") for c in domain.findall("devices/controller[@type='pci']")]
|
||||||
|
|
|
||||||
|
|
@ -2459,7 +2459,6 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
storages = list()
|
storages = list()
|
||||||
networks = list()
|
networks = list()
|
||||||
hypervisors = list()
|
hypervisors = list()
|
||||||
firmwares = list()
|
|
||||||
meta_prealloc = False
|
meta_prealloc = False
|
||||||
compute = get_object_or_404(Compute, pk=compute_id)
|
compute = get_object_or_404(Compute, pk=compute_id)
|
||||||
flavors = Flavor.objects.filter().order_by("id")
|
flavors = Flavor.objects.filter().order_by("id")
|
||||||
|
|
@ -2512,16 +2511,8 @@ def create_instance(request, compute_id, arch, machine):
|
||||||
caps = conn.get_capabilities(arch)
|
caps = conn.get_capabilities(arch)
|
||||||
|
|
||||||
virtio_support = conn.is_supports_virtio(arch, machine)
|
virtio_support = conn.is_supports_virtio(arch, machine)
|
||||||
# Add BIOS
|
firmwares = conn.get_firmwares(arch, machine)
|
||||||
label = conn.label_for_firmware_path(arch, None)
|
tpm_support = bool(conn.get_tpm_model(arch, machine))
|
||||||
if label:
|
|
||||||
firmwares.append(label)
|
|
||||||
# Add UEFI
|
|
||||||
loader_path = conn.find_uefi_path_for_arch(arch, machine)
|
|
||||||
label = conn.label_for_firmware_path(arch, loader_path)
|
|
||||||
if label:
|
|
||||||
firmwares.append(label)
|
|
||||||
firmwares = list(set(firmwares))
|
|
||||||
|
|
||||||
if not storages:
|
if not storages:
|
||||||
raise util.OperationError(_("You haven't defined any storage pools"))
|
raise util.OperationError(_("You haven't defined any storage pools"))
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -11,7 +11,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2026-10-31 18:13+0000\n"
|
"POT-Creation-Date: 2026-11-01 10:56+0000\n"
|
||||||
"PO-Revision-Date: 2020-11-04 12:00+0000\n"
|
"PO-Revision-Date: 2020-11-04 12:00+0000\n"
|
||||||
"Last-Translator: catborise <muhammetalisag@gmail.com>, 2020\n"
|
"Last-Translator: catborise <muhammetalisag@gmail.com>, 2020\n"
|
||||||
"Language-Team: Turkish (https://www.transifex.com/catborise/teams/110663/"
|
"Language-Team: Turkish (https://www.transifex.com/catborise/teams/110663/"
|
||||||
|
|
@ -275,7 +275,7 @@ msgstr "Parola Değiştir"
|
||||||
#: instances/templates/add_instance_owner_block.html:29
|
#: instances/templates/add_instance_owner_block.html:29
|
||||||
#: instances/templates/add_instance_volume.html:108
|
#: instances/templates/add_instance_volume.html:108
|
||||||
#: instances/templates/create_instance_w1.html:39
|
#: instances/templates/create_instance_w1.html:39
|
||||||
#: instances/templates/create_instance_w2.html:345
|
#: instances/templates/create_instance_w2.html:352
|
||||||
#: instances/templates/instances/destroy_instance_form.html:48
|
#: instances/templates/instances/destroy_instance_form.html:48
|
||||||
#: instances/templates/instances/edit_instance_volume.html:111
|
#: instances/templates/instances/edit_instance_volume.html:111
|
||||||
#: instances/templates/instances/move_disk.html:27
|
#: instances/templates/instances/move_disk.html:27
|
||||||
|
|
@ -1395,7 +1395,9 @@ msgstr "Oturum açma hata sınırı"
|
||||||
|
|
||||||
#: appsettings/migrations/0014_login_lockout_settings.py:8
|
#: appsettings/migrations/0014_login_lockout_settings.py:8
|
||||||
msgid "Failed logins of a user from one address before it is locked"
|
msgid "Failed logins of a user from one address before it is locked"
|
||||||
msgstr "Bir kullanıcının bir adresten kilitlenmeden önceki başarısız oturum açma sayısı"
|
msgstr ""
|
||||||
|
"Bir kullanıcının bir adresten kilitlenmeden önceki başarısız oturum açma "
|
||||||
|
"sayısı"
|
||||||
|
|
||||||
#: appsettings/migrations/0014_login_lockout_settings.py:9
|
#: appsettings/migrations/0014_login_lockout_settings.py:9
|
||||||
msgid "Login Lockout Minutes"
|
msgid "Login Lockout Minutes"
|
||||||
|
|
@ -1413,7 +1415,9 @@ msgstr "Anlık görüntü boş alan yüzdesi"
|
||||||
msgid ""
|
msgid ""
|
||||||
"Free space a storage pool keeps for a snapshot, in percent of the VM's disks "
|
"Free space a storage pool keeps for a snapshot, in percent of the VM's disks "
|
||||||
"in it (0: none)"
|
"in it (0: none)"
|
||||||
msgstr "Bir depolama havuzunun anlık görüntü için ayırdığı boş alan; sanal makinenin bu havuzdaki disklerinin yüzdesi olarak (0: yok)"
|
msgstr ""
|
||||||
|
"Bir depolama havuzunun anlık görüntü için ayırdığı boş alan; sanal makinenin "
|
||||||
|
"bu havuzdaki disklerinin yüzdesi olarak (0: yok)"
|
||||||
|
|
||||||
#: appsettings/migrations/0017_snapshot_free_space_settings.py:9
|
#: appsettings/migrations/0017_snapshot_free_space_settings.py:9
|
||||||
msgid "Snapshot Free Min GiB"
|
msgid "Snapshot Free Min GiB"
|
||||||
|
|
@ -1422,7 +1426,9 @@ msgstr "Anlık görüntü en az boş alan (GiB)"
|
||||||
#: appsettings/migrations/0017_snapshot_free_space_settings.py:10
|
#: appsettings/migrations/0017_snapshot_free_space_settings.py:10
|
||||||
msgid ""
|
msgid ""
|
||||||
"Free space a storage pool keeps for a snapshot at least, in GiB (0: none)"
|
"Free space a storage pool keeps for a snapshot at least, in GiB (0: none)"
|
||||||
msgstr "Bir depolama havuzunun anlık görüntü için en az ayırdığı boş alan, GiB olarak (0: yok)"
|
msgstr ""
|
||||||
|
"Bir depolama havuzunun anlık görüntü için en az ayırdığı boş alan, GiB "
|
||||||
|
"olarak (0: yok)"
|
||||||
|
|
||||||
#: appsettings/migrations/0018_worker_settings.py:7
|
#: appsettings/migrations/0018_worker_settings.py:7
|
||||||
msgid "Worker Concurrency"
|
msgid "Worker Concurrency"
|
||||||
|
|
@ -1459,7 +1465,9 @@ msgstr "Yeni bir sanal makine için önce seçilen CPU modu"
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:10
|
#: appsettings/migrations/0021_setting_descriptions.py:10
|
||||||
msgid ""
|
msgid ""
|
||||||
"Network card model chosen first for a new VM (default: libvirt's choice)"
|
"Network card model chosen first for a new VM (default: libvirt's choice)"
|
||||||
msgstr "Yeni bir sanal makine için önce seçilen ağ kartı modeli (default: libvirt'in seçimi)"
|
msgstr ""
|
||||||
|
"Yeni bir sanal makine için önce seçilen ağ kartı modeli (default: libvirt'in "
|
||||||
|
"seçimi)"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:11
|
#: appsettings/migrations/0021_setting_descriptions.py:11
|
||||||
msgid "Video device chosen first for a new VM"
|
msgid "Video device chosen first for a new VM"
|
||||||
|
|
@ -1467,7 +1475,8 @@ msgstr "Yeni bir sanal makine için önce seçilen video aygıtı"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:12
|
#: appsettings/migrations/0021_setting_descriptions.py:12
|
||||||
msgid "Input device chosen first for a new VM (None: no input device)"
|
msgid "Input device chosen first for a new VM (None: no input device)"
|
||||||
msgstr "Yeni bir sanal makine için önce seçilen girdi aygıtı (None: girdi aygıtı yok)"
|
msgstr ""
|
||||||
|
"Yeni bir sanal makine için önce seçilen girdi aygıtı (None: girdi aygıtı yok)"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:13
|
#: appsettings/migrations/0021_setting_descriptions.py:13
|
||||||
msgid "Bus of the CD-ROM a new VM gets (None: no CD-ROM)"
|
msgid "Bus of the CD-ROM a new VM gets (None: no CD-ROM)"
|
||||||
|
|
@ -1487,16 +1496,21 @@ msgstr "Yeni disklerin önbellek modu"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:17
|
#: appsettings/migrations/0021_setting_descriptions.py:17
|
||||||
msgid "I/O mode of new disks (native needs cache none or directsync)"
|
msgid "I/O mode of new disks (native needs cache none or directsync)"
|
||||||
msgstr "Yeni disklerin G/Ç modu (native, önbellek modu none veya directsync gerektirir)"
|
msgstr ""
|
||||||
|
"Yeni disklerin G/Ç modu (native, önbellek modu none veya directsync "
|
||||||
|
"gerektirir)"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:18
|
#: appsettings/migrations/0021_setting_descriptions.py:18
|
||||||
msgid "Discard of new disks (unmap: the guest's TRIM frees space in the image)"
|
msgid "Discard of new disks (unmap: the guest's TRIM frees space in the image)"
|
||||||
msgstr "Yeni disklerin discard ayarı (unmap: konuğun TRIM komutu imajda yer açar)"
|
msgstr ""
|
||||||
|
"Yeni disklerin discard ayarı (unmap: konuğun TRIM komutu imajda yer açar)"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:19
|
#: appsettings/migrations/0021_setting_descriptions.py:19
|
||||||
msgid ""
|
msgid ""
|
||||||
"Detect zeroes of new disks: how blocks of zeros the guest writes are stored"
|
"Detect zeroes of new disks: how blocks of zeros the guest writes are stored"
|
||||||
msgstr "Yeni disklerin sıfır algılama ayarı: konuğun yazdığı sıfır bloklarının nasıl saklanacağı"
|
msgstr ""
|
||||||
|
"Yeni disklerin sıfır algılama ayarı: konuğun yazdığı sıfır bloklarının nasıl "
|
||||||
|
"saklanacağı"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:20
|
#: appsettings/migrations/0021_setting_descriptions.py:20
|
||||||
msgid "Controller model for disks on the SCSI bus"
|
msgid "Controller model for disks on the SCSI bus"
|
||||||
|
|
@ -1549,15 +1563,21 @@ msgstr "Bir sanal makine birden fazla kullanıcıya verilebilir"
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:32
|
#: appsettings/migrations/0021_setting_descriptions.py:32
|
||||||
msgid ""
|
msgid ""
|
||||||
"A quota refusal shows the numbers: what would be used and what is allowed"
|
"A quota refusal shows the numbers: what would be used and what is allowed"
|
||||||
msgstr "Kota reddi sayıları gösterir: ne kadar kullanılacağını ve ne kadarına izin verildiğini"
|
msgstr ""
|
||||||
|
"Kota reddi sayıları gösterir: ne kadar kullanılacağını ve ne kadarına izin "
|
||||||
|
"verildiğini"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:33
|
#: appsettings/migrations/0021_setting_descriptions.py:33
|
||||||
msgid "Instances page for administrators: VMs under their host, or one list"
|
msgid "Instances page for administrators: VMs under their host, or one list"
|
||||||
msgstr "Yöneticiler için Sanal Makineler sayfası: sanal makineler sunucularının altında ya da tek bir listede"
|
msgstr ""
|
||||||
|
"Yöneticiler için Sanal Makineler sayfası: sanal makineler sunucularının "
|
||||||
|
"altında ya da tek bir listede"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:34
|
#: appsettings/migrations/0021_setting_descriptions.py:34
|
||||||
msgid "A VM's page shows a bar with its host's links at the bottom"
|
msgid "A VM's page shows a bar with its host's links at the bottom"
|
||||||
msgstr "Sanal makinenin sayfası, altta sunucusunun bağlantılarını içeren bir çubuk gösterir"
|
msgstr ""
|
||||||
|
"Sanal makinenin sayfası, altta sunucusunun bağlantılarını içeren bir çubuk "
|
||||||
|
"gösterir"
|
||||||
|
|
||||||
#: appsettings/migrations/0021_setting_descriptions.py:35
|
#: appsettings/migrations/0021_setting_descriptions.py:35
|
||||||
msgid "The Instances page shows each VM's DRBD state"
|
msgid "The Instances page shows each VM's DRBD state"
|
||||||
|
|
@ -1921,7 +1941,7 @@ msgid "Connection"
|
||||||
msgstr "Bağlantı"
|
msgstr "Bağlantı"
|
||||||
|
|
||||||
#: computes/templates/computes/form.html:31
|
#: computes/templates/computes/form.html:31
|
||||||
#: instances/templates/create_instance_w2.html:248
|
#: instances/templates/create_instance_w2.html:255
|
||||||
msgid "Advanced"
|
msgid "Advanced"
|
||||||
msgstr "Gelişmiş"
|
msgstr "Gelişmiş"
|
||||||
|
|
||||||
|
|
@ -1978,8 +1998,8 @@ msgstr "Depolama"
|
||||||
#: computes/templates/computes/host_nav.html:19
|
#: computes/templates/computes/host_nav.html:19
|
||||||
#: computes/templates/computes/host_nav.html:20
|
#: computes/templates/computes/host_nav.html:20
|
||||||
#: instances/templates/add_instance_network_block.html:18
|
#: instances/templates/add_instance_network_block.html:18
|
||||||
#: instances/templates/create_instance_w2.html:220
|
#: instances/templates/create_instance_w2.html:227
|
||||||
#: instances/templates/create_instance_w2.html:222
|
#: instances/templates/create_instance_w2.html:229
|
||||||
#: instances/templates/instances/settings_tab.html:379
|
#: instances/templates/instances/settings_tab.html:379
|
||||||
#: networks/templates/networks.html:5 networks/templates/networks.html:7
|
#: networks/templates/networks.html:5 networks/templates/networks.html:7
|
||||||
msgid "Networks"
|
msgid "Networks"
|
||||||
|
|
@ -2376,9 +2396,9 @@ msgstr "Ölçekleme"
|
||||||
|
|
||||||
#: console/templates/console-vnc.html:29
|
#: console/templates/console-vnc.html:29
|
||||||
#: instances/templates/add_instance_network_block.html:41
|
#: instances/templates/add_instance_network_block.html:41
|
||||||
#: instances/templates/create_instance_w2.html:275
|
#: instances/templates/create_instance_w2.html:282
|
||||||
#: instances/templates/create_instance_w2.html:304
|
#: instances/templates/create_instance_w2.html:311
|
||||||
#: instances/templates/create_instance_w2.html:314
|
#: instances/templates/create_instance_w2.html:321
|
||||||
#: instances/templates/instance.html:55
|
#: instances/templates/instance.html:55
|
||||||
#: instances/templates/instances/settings_tab.html:186
|
#: instances/templates/instances/settings_tab.html:186
|
||||||
#: instances/templates/instances/settings_tab.html:342
|
#: instances/templates/instances/settings_tab.html:342
|
||||||
|
|
@ -2451,7 +2471,7 @@ msgstr "Bağlan"
|
||||||
#: instances/templates/allinstances.html:32
|
#: instances/templates/allinstances.html:32
|
||||||
#: instances/templates/allinstances.html:88
|
#: instances/templates/allinstances.html:88
|
||||||
#: instances/templates/create_inst_block.html:12
|
#: instances/templates/create_inst_block.html:12
|
||||||
#: instances/templates/create_instance_w2.html:357
|
#: instances/templates/create_instance_w2.html:364
|
||||||
#: instances/templates/instances/edit_instance_volume.html:13
|
#: instances/templates/instances/edit_instance_volume.html:13
|
||||||
#: instances/templates/instances/move_disk.html:13
|
#: instances/templates/instances/move_disk.html:13
|
||||||
#: instances/templates/instances/settings_tab.html:366
|
#: instances/templates/instances/settings_tab.html:366
|
||||||
|
|
@ -2695,7 +2715,7 @@ msgstr "libvirt sanal makineyi hâlâ geri döndürüyor"
|
||||||
msgid "Clone VM"
|
msgid "Clone VM"
|
||||||
msgstr "Sanal makineyi klonla"
|
msgstr "Sanal makineyi klonla"
|
||||||
|
|
||||||
#: instances/jobs.py:495 instances/jobs.py:503 instances/jobs.py:751
|
#: instances/jobs.py:495 instances/jobs.py:503 instances/jobs.py:746
|
||||||
msgid "Copying disks"
|
msgid "Copying disks"
|
||||||
msgstr "Diskler kopyalanıyor"
|
msgstr "Diskler kopyalanıyor"
|
||||||
|
|
||||||
|
|
@ -2704,7 +2724,7 @@ msgid "libvirt is still copying the disks"
|
||||||
msgstr "libvirt diskleri hâlâ kopyalıyor"
|
msgstr "libvirt diskleri hâlâ kopyalıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:662 instances/templates/create_instance_w1.html:58
|
#: instances/jobs.py:662 instances/templates/create_instance_w1.html:58
|
||||||
#: instances/templates/create_instance_w2.html:346
|
#: instances/templates/create_instance_w2.html:353
|
||||||
msgid "Create VM"
|
msgid "Create VM"
|
||||||
msgstr "Sanal makine oluştur"
|
msgstr "Sanal makine oluştur"
|
||||||
|
|
||||||
|
|
@ -2754,49 +2774,54 @@ 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"
|
msgstr "cloud-init tohumu, disk için bir dizin depolama havuzu gerektirir"
|
||||||
|
|
||||||
#: instances/jobs.py:712
|
#: instances/jobs.py:712
|
||||||
msgid "Invalid firmware"
|
#, python-format
|
||||||
msgstr "Geçersiz donanım yazılımı"
|
msgid "This host offers no firmware %(firmware)s"
|
||||||
|
msgstr "Bu sunucu %(firmware)s firmware'ini sunmuyor"
|
||||||
|
|
||||||
#: instances/jobs.py:770
|
#: instances/jobs.py:714
|
||||||
|
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:765
|
||||||
msgid "Writing the cloud-init seed"
|
msgid "Writing the cloud-init seed"
|
||||||
msgstr "cloud-init tohumu yazılıyor"
|
msgstr "cloud-init tohumu yazılıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:777
|
#: instances/jobs.py:772
|
||||||
msgid "Defining the VM"
|
msgid "Defining the VM"
|
||||||
msgstr "Sanal makine tanımlanıyor"
|
msgstr "Sanal makine tanımlanıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:833
|
#: instances/jobs.py:829
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "The instance is already on %(compute)s"
|
msgid "The instance is already on %(compute)s"
|
||||||
msgstr "Sanal makine zaten %(compute)s üzerinde"
|
msgstr "Sanal makine zaten %(compute)s üzerinde"
|
||||||
|
|
||||||
#: instances/jobs.py:835
|
#: instances/jobs.py:831
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(compute)s is not reachable"
|
msgid "%(compute)s is not reachable"
|
||||||
msgstr "%(compute)s sunucusuna ulaşılamıyor"
|
msgstr "%(compute)s sunucusuna ulaşılamıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:837
|
#: instances/jobs.py:833
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%(compute)s is the VM's own host under another name"
|
msgid "%(compute)s is the VM's own host under another name"
|
||||||
msgstr "%(compute)s, sanal makinenin kendi sunucusunun başka bir adı"
|
msgstr "%(compute)s, sanal makinenin kendi sunucusunun başka bir adı"
|
||||||
|
|
||||||
#: instances/jobs.py:855
|
#: instances/jobs.py:851
|
||||||
msgid "Migrate VM"
|
msgid "Migrate VM"
|
||||||
msgstr "Sanal makineyi taşı"
|
msgstr "Sanal makineyi taşı"
|
||||||
|
|
||||||
#: instances/jobs.py:899
|
#: instances/jobs.py:895
|
||||||
msgid "Migrating the VM"
|
msgid "Migrating the VM"
|
||||||
msgstr "Sanal makine taşınıyor"
|
msgstr "Sanal makine taşınıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:931
|
#: instances/jobs.py:927
|
||||||
msgid "libvirt is still migrating the VM"
|
msgid "libvirt is still migrating the VM"
|
||||||
msgstr "libvirt sanal makineyi hâlâ taşıyor"
|
msgstr "libvirt sanal makineyi hâlâ taşıyor"
|
||||||
|
|
||||||
#: instances/jobs.py:972
|
#: instances/jobs.py:968
|
||||||
msgid "Move disk"
|
msgid "Move disk"
|
||||||
msgstr "Diski taşı"
|
msgstr "Diski taşı"
|
||||||
|
|
||||||
#: instances/jobs.py:1003
|
#: instances/jobs.py:999
|
||||||
msgid "Copying the disk"
|
msgid "Copying the disk"
|
||||||
msgstr "Disk kopyalanıyor"
|
msgstr "Disk kopyalanıyor"
|
||||||
|
|
||||||
|
|
@ -2832,14 +2857,14 @@ msgstr "oluşturuldu"
|
||||||
msgid "drbd"
|
msgid "drbd"
|
||||||
msgstr "drbd"
|
msgstr "drbd"
|
||||||
|
|
||||||
#: instances/models.py:221 instances/templates/create_instance_w2.html:108
|
#: instances/models.py:221 instances/templates/create_instance_w2.html:115
|
||||||
#: instances/templates/instances/resize_tab.html:20
|
#: instances/templates/instances/resize_tab.html:20
|
||||||
#: instances/templates/instances/settings_tab.html:25
|
#: instances/templates/instances/settings_tab.html:25
|
||||||
#: instances/templates/instances/stats_tab.html:49
|
#: instances/templates/instances/stats_tab.html:49
|
||||||
msgid "Disk"
|
msgid "Disk"
|
||||||
msgstr "Disk"
|
msgstr "Disk"
|
||||||
|
|
||||||
#: instances/models.py:223 instances/templates/create_instance_w2.html:302
|
#: instances/models.py:223 instances/templates/create_instance_w2.html:309
|
||||||
#: instances/templates/instances/settings_tab.html:167
|
#: instances/templates/instances/settings_tab.html:167
|
||||||
msgid "CD-ROM"
|
msgid "CD-ROM"
|
||||||
msgstr "CD-ROM"
|
msgstr "CD-ROM"
|
||||||
|
|
@ -2872,27 +2897,27 @@ msgstr "Güvenli olmayan"
|
||||||
msgid "No Virtual Machine name has been entered"
|
msgid "No Virtual Machine name has been entered"
|
||||||
msgstr "Sanal makine adı girilmedi"
|
msgstr "Sanal makine adı girilmedi"
|
||||||
|
|
||||||
#: instances/models.py:406
|
#: instances/models.py:408
|
||||||
msgid "No VCPU has been entered"
|
msgid "No VCPU has been entered"
|
||||||
msgstr "VCPU belirtilmedi"
|
msgstr "VCPU belirtilmedi"
|
||||||
|
|
||||||
#: instances/models.py:412
|
#: instances/models.py:414
|
||||||
msgid "No RAM size has been entered"
|
msgid "No RAM size has been entered"
|
||||||
msgstr "RAM boyutu belirtilmedi"
|
msgstr "RAM boyutu belirtilmedi"
|
||||||
|
|
||||||
#: instances/models.py:416
|
#: instances/models.py:418
|
||||||
msgid "No Network pool has been choosen"
|
msgid "No Network pool has been choosen"
|
||||||
msgstr "Ağ havuzu seçilmedi"
|
msgstr "Ağ havuzu seçilmedi"
|
||||||
|
|
||||||
#: instances/models.py:424
|
#: instances/models.py:426
|
||||||
msgid "Please select HDD cache mode"
|
msgid "Please select HDD cache mode"
|
||||||
msgstr "Lütfen HDD ön bellek modunu seçin"
|
msgstr "Lütfen HDD ön bellek modunu seçin"
|
||||||
|
|
||||||
#: instances/models.py:436
|
#: instances/models.py:438
|
||||||
msgid "Please select a video driver"
|
msgid "Please select a video driver"
|
||||||
msgstr "Lütfen bir video sürücüsü seçin"
|
msgstr "Lütfen bir video sürücüsü seçin"
|
||||||
|
|
||||||
#: instances/models.py:473
|
#: instances/models.py:475
|
||||||
msgid "Can access console without password"
|
msgid "Can access console without password"
|
||||||
msgstr "Parolasız konsol erişimi yapabilir"
|
msgstr "Parolasız konsol erişimi yapabilir"
|
||||||
|
|
||||||
|
|
@ -2986,12 +3011,12 @@ msgid "Add disk form"
|
||||||
msgstr "Disk ekleme formu"
|
msgstr "Disk ekleme formu"
|
||||||
|
|
||||||
#: instances/templates/add_instance_volume.html:19
|
#: instances/templates/add_instance_volume.html:19
|
||||||
#: instances/templates/create_instance_w2.html:110
|
#: instances/templates/create_instance_w2.html:117
|
||||||
msgid "Disk source"
|
msgid "Disk source"
|
||||||
msgstr "Disk kaynağı"
|
msgstr "Disk kaynağı"
|
||||||
|
|
||||||
#: instances/templates/add_instance_volume.html:21
|
#: instances/templates/add_instance_volume.html:21
|
||||||
#: instances/templates/create_instance_w2.html:124 instances/views.py:2427
|
#: instances/templates/create_instance_w2.html:131 instances/views.py:2427
|
||||||
msgid "New disk"
|
msgid "New disk"
|
||||||
msgstr "Yeni disk"
|
msgstr "Yeni disk"
|
||||||
|
|
||||||
|
|
@ -3009,8 +3034,8 @@ msgstr "Sunucunun bir havuzunda zaten bulunan bir birim."
|
||||||
|
|
||||||
#: instances/templates/add_instance_volume.html:31
|
#: instances/templates/add_instance_volume.html:31
|
||||||
#: instances/templates/add_instance_volume.html:70
|
#: instances/templates/add_instance_volume.html:70
|
||||||
#: instances/templates/create_instance_w2.html:127
|
#: instances/templates/create_instance_w2.html:134
|
||||||
#: instances/templates/create_instance_w2.html:155
|
#: instances/templates/create_instance_w2.html:162
|
||||||
#: instances/templates/instances/move_disk.html:16
|
#: instances/templates/instances/move_disk.html:16
|
||||||
#: instances/templates/instances/settings_tab.html:229
|
#: instances/templates/instances/settings_tab.html:229
|
||||||
msgid "Storage pool"
|
msgid "Storage pool"
|
||||||
|
|
@ -3033,8 +3058,8 @@ msgid "Format"
|
||||||
msgstr "Biçem"
|
msgstr "Biçem"
|
||||||
|
|
||||||
#: instances/templates/add_instance_volume.html:61
|
#: instances/templates/add_instance_volume.html:61
|
||||||
#: instances/templates/create_instance_w2.html:144
|
#: instances/templates/create_instance_w2.html:151
|
||||||
#: instances/templates/create_instance_w2.html:210 storages/forms.py:95
|
#: instances/templates/create_instance_w2.html:217 storages/forms.py:95
|
||||||
#: storages/forms.py:115 storages/templates/storage.html:198
|
#: storages/forms.py:115 storages/templates/storage.html:198
|
||||||
msgid "Preallocate metadata (qcow2)"
|
msgid "Preallocate metadata (qcow2)"
|
||||||
msgstr "Meta veriyi önceden ayır (qcow2)"
|
msgstr "Meta veriyi önceden ayır (qcow2)"
|
||||||
|
|
@ -3052,7 +3077,7 @@ msgid "Choose a pool first"
|
||||||
msgstr "Önce bir havuz seçin"
|
msgstr "Önce bir havuz seçin"
|
||||||
|
|
||||||
#: instances/templates/add_instance_volume.html:90
|
#: instances/templates/add_instance_volume.html:90
|
||||||
#: instances/templates/create_instance_w2.html:174
|
#: instances/templates/create_instance_w2.html:181
|
||||||
#: instances/templates/instances/edit_instance_volume.html:82
|
#: instances/templates/instances/edit_instance_volume.html:82
|
||||||
#: instances/templates/instances/edit_instance_volume.html:87
|
#: instances/templates/instances/edit_instance_volume.html:87
|
||||||
msgid "Bus"
|
msgid "Bus"
|
||||||
|
|
@ -3335,43 +3360,51 @@ msgstr "Hazır boyutları yönet"
|
||||||
msgid "Firmware"
|
msgid "Firmware"
|
||||||
msgstr "Üretici yazılımı"
|
msgstr "Üretici yazılımı"
|
||||||
|
|
||||||
|
#: instances/templates/create_instance_w2.html:83
|
||||||
|
msgid "TPM 2.0"
|
||||||
|
msgstr "TPM 2.0"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:84
|
#: instances/templates/create_instance_w2.html:84
|
||||||
|
msgid "Windows 11 needs it, with UEFI Secure Boot"
|
||||||
|
msgstr "Windows 11, UEFI Secure Boot ile birlikte bunu ister"
|
||||||
|
|
||||||
|
#: instances/templates/create_instance_w2.html:91
|
||||||
msgid "CPU mode"
|
msgid "CPU mode"
|
||||||
msgstr "CPU modu"
|
msgstr "CPU modu"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:86
|
#: instances/templates/create_instance_w2.html:93
|
||||||
#: storages/templates/create_stg_block.html:118
|
#: storages/templates/create_stg_block.html:118
|
||||||
msgid "Choose…"
|
msgid "Choose…"
|
||||||
msgstr "Seçin…"
|
msgstr "Seçin…"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:89
|
#: instances/templates/create_instance_w2.html:96
|
||||||
msgid "Custom CPU Models"
|
msgid "Custom CPU Models"
|
||||||
msgstr "Özel CPU modelleri"
|
msgstr "Özel CPU modelleri"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:136 storages/forms.py:94
|
#: instances/templates/create_instance_w2.html:143 storages/forms.py:94
|
||||||
msgid "Size (GiB)"
|
msgid "Size (GiB)"
|
||||||
msgstr "Boyut (GiB)"
|
msgstr "Boyut (GiB)"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:151 instances/views.py:2428
|
#: instances/templates/create_instance_w2.html:158 instances/views.py:2428
|
||||||
msgid "Existing volumes"
|
msgid "Existing volumes"
|
||||||
msgstr "Mevcut birimler"
|
msgstr "Mevcut birimler"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:157
|
#: instances/templates/create_instance_w2.html:164
|
||||||
#: instances/templates/create_instance_w2.html:189
|
#: instances/templates/create_instance_w2.html:196
|
||||||
msgid "Select pool"
|
msgid "Select pool"
|
||||||
msgstr "Havuz seçin"
|
msgstr "Havuz seçin"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:162
|
#: instances/templates/create_instance_w2.html:169
|
||||||
#: instances/templates/create_instance_w2.html:163
|
#: instances/templates/create_instance_w2.html:170
|
||||||
#: storages/templates/storage.html:116 storages/templates/storages.html:47
|
#: storages/templates/storage.html:116 storages/templates/storages.html:47
|
||||||
msgid "Volumes"
|
msgid "Volumes"
|
||||||
msgstr "Birimler"
|
msgstr "Birimler"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:167
|
#: instances/templates/create_instance_w2.html:174
|
||||||
msgid "Chosen volumes"
|
msgid "Chosen volumes"
|
||||||
msgstr "Seçilen birimler"
|
msgstr "Seçilen birimler"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:171
|
#: instances/templates/create_instance_w2.html:178
|
||||||
#: instances/templates/instances/edit_instance_volume.html:81
|
#: instances/templates/instances/edit_instance_volume.html:81
|
||||||
#: interfaces/templates/create_iface_block.html:78
|
#: interfaces/templates/create_iface_block.html:78
|
||||||
#: interfaces/templates/interface.html:113 networks/templates/networks.html:39
|
#: interfaces/templates/interface.html:113 networks/templates/networks.html:39
|
||||||
|
|
@ -3380,91 +3413,91 @@ msgstr "Seçilen birimler"
|
||||||
msgid "Device"
|
msgid "Device"
|
||||||
msgstr "Aygıt"
|
msgstr "Aygıt"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:178
|
#: instances/templates/create_instance_w2.html:185
|
||||||
#: instances/templates/create_instance_w2.html:236
|
#: instances/templates/create_instance_w2.html:243
|
||||||
#: instances/templates/instances/settings_tab.html:678
|
#: instances/templates/instances/settings_tab.html:678
|
||||||
#: networks/templates/network.html:303
|
#: networks/templates/network.html:303
|
||||||
msgid "Remove"
|
msgid "Remove"
|
||||||
msgstr "Kaldır"
|
msgstr "Kaldır"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:184 instances/views.py:2429
|
#: instances/templates/create_instance_w2.html:191 instances/views.py:2429
|
||||||
msgid "Clone from template"
|
msgid "Clone from template"
|
||||||
msgstr "Şablondan klonla"
|
msgstr "Şablondan klonla"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:187
|
#: instances/templates/create_instance_w2.html:194
|
||||||
msgid "Template pool"
|
msgid "Template pool"
|
||||||
msgstr "Şablon havuzu"
|
msgstr "Şablon havuzu"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:194
|
#: instances/templates/create_instance_w2.html:201
|
||||||
msgid "Template volume"
|
msgid "Template volume"
|
||||||
msgstr "Şablon birimi"
|
msgstr "Şablon birimi"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:199
|
#: instances/templates/create_instance_w2.html:206
|
||||||
msgid "Copy into pool"
|
msgid "Copy into pool"
|
||||||
msgstr "Havuza kopyala"
|
msgstr "Havuza kopyala"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:221
|
#: instances/templates/create_instance_w2.html:228
|
||||||
msgid "Add networks"
|
msgid "Add networks"
|
||||||
msgstr "Ağ ekle"
|
msgstr "Ağ ekle"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:228
|
#: instances/templates/create_instance_w2.html:235
|
||||||
msgid "Network interfaces"
|
msgid "Network interfaces"
|
||||||
msgstr "Ağ arabirimleri"
|
msgstr "Ağ arabirimleri"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:235
|
#: instances/templates/create_instance_w2.html:242
|
||||||
msgid "MAC address"
|
msgid "MAC address"
|
||||||
msgstr "MAC adresi"
|
msgstr "MAC adresi"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:255
|
#: instances/templates/create_instance_w2.html:262
|
||||||
msgid "Disk cache mode"
|
msgid "Disk cache mode"
|
||||||
msgstr "Disk önbellek modu"
|
msgstr "Disk önbellek modu"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:264
|
#: instances/templates/create_instance_w2.html:271
|
||||||
msgid "NIC type"
|
msgid "NIC type"
|
||||||
msgstr "NIC tipi"
|
msgstr "NIC tipi"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:273
|
#: instances/templates/create_instance_w2.html:280
|
||||||
#: nwfilters/templates/nwfilter.html:6 nwfilters/templates/nwfilter.html:8
|
#: nwfilters/templates/nwfilter.html:6 nwfilters/templates/nwfilter.html:8
|
||||||
msgid "Network filter"
|
msgid "Network filter"
|
||||||
msgstr "Ağ filtresi"
|
msgstr "Ağ filtresi"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:283 vrtManager/nodedevs.py:29
|
#: instances/templates/create_instance_w2.html:290 vrtManager/nodedevs.py:29
|
||||||
msgid "Video"
|
msgid "Video"
|
||||||
msgstr "Görüntü"
|
msgstr "Görüntü"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:293
|
#: instances/templates/create_instance_w2.html:300
|
||||||
msgid "Console access"
|
msgid "Console access"
|
||||||
msgstr "Konsol erişimi"
|
msgstr "Konsol erişimi"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:312
|
#: instances/templates/create_instance_w2.html:319
|
||||||
msgid "Input device"
|
msgid "Input device"
|
||||||
msgstr "Girdi aygıtı"
|
msgstr "Girdi aygıtı"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:324
|
#: instances/templates/create_instance_w2.html:331
|
||||||
msgid "Guest agent"
|
msgid "Guest agent"
|
||||||
msgstr "Misafir aracı"
|
msgstr "Misafir aracı"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:328
|
#: instances/templates/create_instance_w2.html:335
|
||||||
msgid "Cloud-init seed"
|
msgid "Cloud-init seed"
|
||||||
msgstr "Cloud-init tohumu"
|
msgstr "Cloud-init tohumu"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:329
|
#: instances/templates/create_instance_w2.html:336
|
||||||
msgid "The hostname and your SSH keys on the CD-ROM, for cloud images"
|
msgid "The hostname and your SSH keys on the CD-ROM, for cloud images"
|
||||||
msgstr "Bulut imajları için CD-ROM'da sunucuadı ve SSH anahtarlarınız"
|
msgstr "Bulut imajları için CD-ROM'da sunucuadı ve SSH anahtarlarınız"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:334
|
#: instances/templates/create_instance_w2.html:341
|
||||||
msgid "VirtIO devices"
|
msgid "VirtIO devices"
|
||||||
msgstr "VirtIO aygıtları"
|
msgstr "VirtIO aygıtları"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:356
|
#: instances/templates/create_instance_w2.html:363
|
||||||
msgid "Flavors"
|
msgid "Flavors"
|
||||||
msgstr "Hazır boyutlar"
|
msgstr "Hazır boyutlar"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:375
|
#: instances/templates/create_instance_w2.html:382
|
||||||
msgid "Add volume"
|
msgid "Add volume"
|
||||||
msgstr "Birim ekle"
|
msgstr "Birim ekle"
|
||||||
|
|
||||||
#: instances/templates/create_instance_w2.html:382
|
#: instances/templates/create_instance_w2.html:389
|
||||||
msgid "Add network"
|
msgid "Add network"
|
||||||
msgstr "Ağ ekle"
|
msgstr "Ağ ekle"
|
||||||
|
|
||||||
|
|
@ -5960,35 +5993,35 @@ msgstr "Bir havuzda zaten bulunan birimler"
|
||||||
msgid "A copy of a template volume"
|
msgid "A copy of a template volume"
|
||||||
msgstr "Bir şablon biriminin kopyası"
|
msgstr "Bir şablon biriminin kopyası"
|
||||||
|
|
||||||
#: instances/views.py:2527
|
#: instances/views.py:2518
|
||||||
msgid "You haven't defined any storage pools"
|
msgid "You haven't defined any storage pools"
|
||||||
msgstr "Hiç bir depolama havuzu tanımlamadınız"
|
msgstr "Hiç bir depolama havuzu tanımlamadınız"
|
||||||
|
|
||||||
#: instances/views.py:2529
|
#: instances/views.py:2520
|
||||||
msgid "You haven't defined any network pools"
|
msgid "You haven't defined any network pools"
|
||||||
msgstr "Hiç bir ağ havuzu tanımlamadınız"
|
msgstr "Hiç bir ağ havuzu tanımlamadınız"
|
||||||
|
|
||||||
#: instances/views.py:2592
|
#: instances/views.py:2583
|
||||||
msgid "Flavor Created"
|
msgid "Flavor Created"
|
||||||
msgstr "Flavor Oluşturuldu"
|
msgstr "Flavor Oluşturuldu"
|
||||||
|
|
||||||
#: instances/views.py:2600
|
#: instances/views.py:2591
|
||||||
msgid "Create Flavor"
|
msgid "Create Flavor"
|
||||||
msgstr "Kullanıcı Oluştur"
|
msgstr "Kullanıcı Oluştur"
|
||||||
|
|
||||||
#: instances/views.py:2615
|
#: instances/views.py:2606
|
||||||
msgid "Flavor Updated"
|
msgid "Flavor Updated"
|
||||||
msgstr "Flavor Güncellendi"
|
msgstr "Flavor Güncellendi"
|
||||||
|
|
||||||
#: instances/views.py:2621
|
#: instances/views.py:2612
|
||||||
msgid "Update Flavor"
|
msgid "Update Flavor"
|
||||||
msgstr "Flavor Güncelle"
|
msgstr "Flavor Güncelle"
|
||||||
|
|
||||||
#: instances/views.py:2633
|
#: instances/views.py:2624
|
||||||
msgid "Flavor Deleted"
|
msgid "Flavor Deleted"
|
||||||
msgstr "Flavor Silindi"
|
msgstr "Flavor Silindi"
|
||||||
|
|
||||||
#: instances/views.py:2639
|
#: instances/views.py:2630
|
||||||
msgid "Only the size template goes; VMs made from it keep their size."
|
msgid "Only the size template goes; VMs made from it keep their size."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Yalnızca boyut şablonu silinir; ondan oluşturulan sanal makineler "
|
"Yalnızca boyut şablonu silinir; ondan oluşturulan sanal makineler "
|
||||||
|
|
|
||||||
|
|
@ -1189,48 +1189,47 @@ class wvmConnect(object):
|
||||||
# self.wvm.close()
|
# self.wvm.close()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def find_uefi_path_for_arch(self, arch, machine):
|
def get_firmwares(self, arch, machine):
|
||||||
"""
|
"""The firmware a new VM can have: BIOS on x86, UEFI without Secure Boot
|
||||||
Search the loader paths for one that matches the passed arch
|
when libvirt picks the firmware itself, and on x86_64 UEFI Secure Boot
|
||||||
"""
|
with Microsoft's keys enrolled, as Windows 11 needs."""
|
||||||
if not self.arch_can_uefi(arch):
|
|
||||||
return
|
|
||||||
|
|
||||||
loaders = self.get_os_loaders(arch, machine)
|
def firmwares(ctx):
|
||||||
patterns = util.UEFI_ARCH_PATTERNS.get(arch)
|
os_caps = "/domainCapabilities/os[@supported='yes']"
|
||||||
for pattern in patterns:
|
features = f"{os_caps}/firmwareFeatures[@supported='yes']"
|
||||||
for path in loaders:
|
if ctx.xpath(features): # libvirt 12.1 and later: what its firmware offers
|
||||||
if re.match(pattern, path):
|
secure_boot = ctx.xpath(f"{features}/enum[@name='secureBoot']/value/text()")
|
||||||
return path
|
enrolled_keys = ctx.xpath(f"{features}/enum[@name='enrolledKeys']/value/text()")
|
||||||
|
else: # earlier: the loaders; libvirt refuses the VM if no firmware has the keys
|
||||||
|
secure_boot = ctx.xpath(f"{os_caps}/loader[@supported='yes']/enum[@name='secure']/value/text()") or ["no"]
|
||||||
|
enrolled_keys = secure_boot
|
||||||
|
choices = ["BIOS"] if arch in ("i686", "x86_64") else []
|
||||||
|
if "efi" in ctx.xpath(f"{os_caps}/enum[@name='firmware']/value/text()"):
|
||||||
|
if "no" in secure_boot:
|
||||||
|
choices.append("UEFI")
|
||||||
|
if arch == "x86_64" and "yes" in secure_boot and "yes" in enrolled_keys:
|
||||||
|
choices.append("UEFI Secure Boot")
|
||||||
|
return choices
|
||||||
|
|
||||||
def label_for_firmware_path(self, arch, path):
|
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=firmwares)
|
||||||
"""
|
|
||||||
Return a pretty label for passed path, based on if we know
|
|
||||||
about it or not
|
|
||||||
"""
|
|
||||||
if not path:
|
|
||||||
if arch in ["i686", "x86_64"]:
|
|
||||||
return "BIOS"
|
|
||||||
return
|
|
||||||
|
|
||||||
for arch, patterns in util.UEFI_ARCH_PATTERNS.items():
|
def get_tpm_model(self, arch, machine):
|
||||||
for pattern in patterns:
|
"""The model of an emulated TPM 2.0 for a new VM; None when the host has
|
||||||
if re.match(pattern, path):
|
none (no swtpm)."""
|
||||||
return "UEFI %(arch)s: %(path)s" % {"arch": arch, "path": path}
|
|
||||||
|
|
||||||
return "Custom: %(path)s" % {"path": path}
|
def tpm_model(ctx):
|
||||||
|
tpm = "/domainCapabilities/devices/tpm[@supported='yes']"
|
||||||
|
if ("emulator" not in ctx.xpath(f"{tpm}/enum[@name='backendModel']/value/text()")
|
||||||
|
or "2.0" not in ctx.xpath(f"{tpm}/enum[@name='backendVersion']/value/text()")):
|
||||||
|
return None
|
||||||
|
models = ctx.xpath(f"{tpm}/enum[@name='model']/value/text()")
|
||||||
|
# x86: CRB, which Windows takes, or TIS; another machine: its own
|
||||||
|
for model in ("tpm-crb", "tpm-tis", *models):
|
||||||
|
if model in models:
|
||||||
|
return model
|
||||||
|
return None
|
||||||
|
|
||||||
def arch_can_uefi(self, arch):
|
return util.get_xml_path(self.get_dom_cap_xml(arch, machine), func=tpm_model)
|
||||||
"""
|
|
||||||
Return True if we know how to setup UEFI for the passed arch
|
|
||||||
"""
|
|
||||||
return arch in list(util.UEFI_ARCH_PATTERNS.keys())
|
|
||||||
|
|
||||||
def supports_uefi_xml(self, loader_enums):
|
|
||||||
"""
|
|
||||||
Return True if libvirt advertises support for proper UEFI setup
|
|
||||||
"""
|
|
||||||
return "readonly" in loader_enums and "yes" in loader_enums.get("readonly")
|
|
||||||
|
|
||||||
def is_supports_virtio(self, arch, machine):
|
def is_supports_virtio(self, arch, machine):
|
||||||
if not self.is_qemu():
|
if not self.is_qemu():
|
||||||
|
|
|
||||||
|
|
@ -203,10 +203,12 @@ class wvmCreate(wvmConnect):
|
||||||
mac=None,
|
mac=None,
|
||||||
qemu_ga=True,
|
qemu_ga=True,
|
||||||
add_cdrom="sata",
|
add_cdrom="sata",
|
||||||
add_input="default"
|
add_input="default",
|
||||||
|
tpm_model=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Create VM function
|
Create VM function. firmware: BIOS, UEFI or UEFI Secure Boot, of
|
||||||
|
get_firmwares; tpm_model: an emulated TPM 2.0 of get_tpm_model, or none.
|
||||||
"""
|
"""
|
||||||
caps = self.get_capabilities(arch)
|
caps = self.get_capabilities(arch)
|
||||||
dom_caps = self.get_dom_capabilities(arch, machine)
|
dom_caps = self.get_dom_capabilities(arch, machine)
|
||||||
|
|
@ -221,33 +223,30 @@ class wvmCreate(wvmConnect):
|
||||||
<memory unit='KiB'>{memory}</memory>
|
<memory unit='KiB'>{memory}</memory>
|
||||||
<vcpu>{vcpu}</vcpu>"""
|
<vcpu>{vcpu}</vcpu>"""
|
||||||
|
|
||||||
|
secure_boot = firmware == "UEFI Secure Boot"
|
||||||
if dom_caps["os_support"] == "yes":
|
if dom_caps["os_support"] == "yes":
|
||||||
xml += f"""<os>
|
# UEFI: libvirt picks the firmware and its variables, for Secure
|
||||||
|
# Boot one with Microsoft's keys enrolled
|
||||||
|
efi = " firmware='efi'" if firmware in ("UEFI", "UEFI Secure Boot") else ""
|
||||||
|
xml += f"""<os{efi}>
|
||||||
<type arch='{arch}' machine='{machine}'>{caps["os_type"]}</type>"""
|
<type arch='{arch}' machine='{machine}'>{caps["os_type"]}</type>"""
|
||||||
|
if secure_boot:
|
||||||
|
xml += """<firmware>
|
||||||
|
<feature enabled='yes' name='secure-boot'/>
|
||||||
|
<feature enabled='yes' name='enrolled-keys'/>
|
||||||
|
</firmware>"""
|
||||||
|
elif efi:
|
||||||
|
xml += """<firmware><feature enabled='no' name='secure-boot'/></firmware>"""
|
||||||
xml += """ <boot dev='hd'/>
|
xml += """ <boot dev='hd'/>
|
||||||
<boot dev='cdrom'/>
|
<boot dev='cdrom'/>
|
||||||
<bootmenu enable='yes'/>"""
|
<bootmenu enable='yes'/>"""
|
||||||
if firmware:
|
|
||||||
if firmware["secure"] == "yes":
|
|
||||||
xml += """<loader readonly='%s' type='%s' secure='%s'>%s</loader>""" % (
|
|
||||||
firmware["readonly"],
|
|
||||||
firmware["type"],
|
|
||||||
firmware["secure"],
|
|
||||||
firmware["loader"],
|
|
||||||
)
|
|
||||||
if firmware["secure"] == "no":
|
|
||||||
xml += """<loader readonly='%s' type='%s'>%s</loader>""" % (
|
|
||||||
firmware["readonly"],
|
|
||||||
firmware["type"],
|
|
||||||
firmware["loader"],
|
|
||||||
)
|
|
||||||
xml += """</os>"""
|
xml += """</os>"""
|
||||||
|
|
||||||
if caps["features"]:
|
if caps["features"]:
|
||||||
xml += """<features>"""
|
xml += """<features>"""
|
||||||
for feat in [x for x in ("acpi", "apic", "pae",) if x in caps["features"]]:
|
for feat in [x for x in ("acpi", "apic", "pae",) if x in caps["features"]]:
|
||||||
xml += f"""<{feat}/>"""
|
xml += f"""<{feat}/>"""
|
||||||
if firmware.get("secure", "no") == "yes":
|
if secure_boot:
|
||||||
xml += """<smm state="on"/>"""
|
xml += """<smm state="on"/>"""
|
||||||
xml += """</features>"""
|
xml += """</features>"""
|
||||||
|
|
||||||
|
|
@ -382,6 +381,8 @@ class wvmCreate(wvmConnect):
|
||||||
xml += f""" <video>
|
xml += f""" <video>
|
||||||
<model type='{video}'/>
|
<model type='{video}'/>
|
||||||
</video>"""
|
</video>"""
|
||||||
|
if tpm_model:
|
||||||
|
xml += f"""<tpm model='{tpm_model}'><backend type='emulator' version='2.0'/></tpm>"""
|
||||||
# q35 plugs each PCIe device (a virtio disk, a NIC) into a root port of
|
# q35 plugs each PCIe device (a virtio disk, a NIC) into a root port of
|
||||||
# its own and libvirt makes only as many as the VM is defined with: as
|
# its own and libvirt makes only as many as the VM is defined with: as
|
||||||
# virt-install, 14 ports, so a running VM can take more devices
|
# virt-install, 14 ports, so a running VM can take more devices
|
||||||
|
|
|
||||||
|
|
@ -195,31 +195,6 @@ def validate_macaddr(val):
|
||||||
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was {}".format(val))
|
raise ValueError("MAC address must be of the format AA:BB:CC:DD:EE:FF, was {}".format(val))
|
||||||
|
|
||||||
|
|
||||||
# Mapping of UEFI binary names to their associated architectures. We
|
|
||||||
# only use this info to do things automagically for the user, it shouldn't
|
|
||||||
# validate anything the user explicitly enters.
|
|
||||||
UEFI_ARCH_PATTERNS = {
|
|
||||||
"i686": [
|
|
||||||
r".*ovmf-ia32.*", # fedora, gerd's firmware repo
|
|
||||||
],
|
|
||||||
"x86_64": [
|
|
||||||
r".*OVMF_CODE\.fd", # RHEL
|
|
||||||
r".*ovmf-x64/OVMF.*\.fd", # gerd's firmware repo
|
|
||||||
r".*ovmf-x86_64-.*", # SUSE
|
|
||||||
r".*ovmf.*",
|
|
||||||
".*OVMF.*", # generic attempt at a catchall
|
|
||||||
],
|
|
||||||
"aarch64": [
|
|
||||||
r".*AAVMF_CODE\.fd", # RHEL
|
|
||||||
r".*aarch64/QEMU_EFI.*", # gerd's firmware repo
|
|
||||||
r".*aarch64.*", # generic attempt at a catchall
|
|
||||||
],
|
|
||||||
"armv7l": [
|
|
||||||
r".*arm/QEMU_EFI.*", # fedora, gerd's firmware repo
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def vol_dev_type(vol_bus):
|
def vol_dev_type(vol_bus):
|
||||||
if vol_bus == "ide":
|
if vol_bus == "ide":
|
||||||
return "hd"
|
return "hd"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue