diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index c8a0dbc8..1858c269 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -153,6 +153,7 @@ class QemuVM(BaseNode): self._kernel_command_line = "" self._replicate_network_connection_state = True self._tpm = False + self._uefi = False self._create_config_disk = False self._on_close = "power_off" self._cpu_throttling = 0 # means no CPU throttling @@ -900,6 +901,30 @@ class QemuVM(BaseNode): log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the Trusted Platform Module (TPM)') self._tpm = tpm + @property + def uefi(self): + """ + Returns whether UEFI boot mode is activated for this QEMU VM. + + :returns: boolean + """ + + return self._uefi + + @uefi.setter + def uefi(self, uefi): + """ + Sets whether UEFI boot mode is activated for this QEMU VM. + + :param uefi: boolean + """ + + if uefi: + log.info(f'QEMU VM "{self._name}" [{self._id}] has enabled the UEFI boot mode') + else: + log.info(f'QEMU VM "{self._name}" [{self._id}] has disabled the UEFI boot mode') + self._uefi = uefi + @property def options(self): """ @@ -2245,6 +2270,8 @@ class QemuVM(BaseNode): options = [] if self._bios_image: + if self._uefi: + raise QemuError("Cannot use a bios image and the UEFI boot mode at the same time") if not os.path.isfile(self._bios_image) or not os.path.exists(self._bios_image): if os.path.islink(self._bios_image): raise QemuError( @@ -2253,6 +2280,8 @@ class QemuVM(BaseNode): else: raise QemuError(f"bios image '{self._bios_image}' is not accessible") options.extend(["-bios", self._bios_image.replace(",", ",,")]) + elif self._uefi: + options.extend(["-bios", "OVMF.fd"]) # the OVMF bios image should be in the image directory return options def _linux_boot_options(self): diff --git a/gns3server/db/models/templates.py b/gns3server/db/models/templates.py index 05f09d13..210ce1c5 100644 --- a/gns3server/db/models/templates.py +++ b/gns3server/db/models/templates.py @@ -204,6 +204,7 @@ class QemuTemplate(Template): replicate_network_connection_state = Column(Boolean) create_config_disk = Column(Boolean) tpm = Column(Boolean) + uefi = Column(Boolean) on_close = Column(String) cpu_throttling = Column(Integer) process_priority = Column(String) diff --git a/gns3server/disks/OVMF.fd b/gns3server/disks/OVMF.fd new file mode 100644 index 00000000..d05e10cb Binary files /dev/null and b/gns3server/disks/OVMF.fd differ diff --git a/gns3server/schemas/compute/qemu_nodes.py b/gns3server/schemas/compute/qemu_nodes.py index 45822437..4cb23e3c 100644 --- a/gns3server/schemas/compute/qemu_nodes.py +++ b/gns3server/schemas/compute/qemu_nodes.py @@ -206,6 +206,7 @@ class QemuBase(BaseModel): None, description="Automatically create a config disk on HDD disk interface (secondary slave)" ) tpm: Optional[bool] = Field(None, description="Enable Trusted Platform Module (TPM)") + uefi: Optional[bool] = Field(None, description="Enable UEFI boot mode") on_close: Optional[QemuOnCloseAction] = Field(None, description="Action to execute on the VM is closed") cpu_throttling: Optional[int] = Field(None, ge=0, le=800, description="Percentage of CPU allowed for QEMU") process_priority: Optional[QemuProcessPriority] = Field(None, description="Process priority for QEMU") diff --git a/gns3server/schemas/controller/templates/qemu_templates.py b/gns3server/schemas/controller/templates/qemu_templates.py index d872cb42..6645a176 100644 --- a/gns3server/schemas/controller/templates/qemu_templates.py +++ b/gns3server/schemas/controller/templates/qemu_templates.py @@ -81,6 +81,7 @@ class QemuTemplate(TemplateBase): False, description="Automatically create a config disk on HDD disk interface (secondary slave)" ) tpm: Optional[bool] = Field(False, description="Enable Trusted Platform Module (TPM)") + uefi: Optional[bool] = Field(False, description="Enable UEFI boot mode") on_close: Optional[QemuOnCloseAction] = Field("power_off", description="Action to execute on the VM is closed") cpu_throttling: Optional[int] = Field(0, ge=0, le=800, description="Percentage of CPU allowed for QEMU") process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU") diff --git a/tests/compute/qemu/test_qemu_vm.py b/tests/compute/qemu/test_qemu_vm.py index 347f1e11..8241fb64 100644 --- a/tests/compute/qemu/test_qemu_vm.py +++ b/tests/compute/qemu/test_qemu_vm.py @@ -390,6 +390,25 @@ async def test_bios_option(vm, tmpdir, fake_qemu_img_binary): assert ' '.join(['-bios', str(tmpdir / "test.img")]) in ' '.join(options) +@pytest.mark.asyncio +async def test_uefi_boot_mode_option(vm, tmpdir, fake_qemu_img_binary): + + vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") + vm._uefi = True + options = await vm._build_command() + assert ' '.join(['-bios', 'OVMF.fd']) in ' '.join(options) + + +@pytest.mark.asyncio +async def test_uefi_with_bios_image_already_configured(vm, tmpdir, fake_qemu_img_binary): + + vm.manager.get_qemu_version = AsyncioMagicMock(return_value="3.1.0") + vm._bios_image = str(tmpdir / "test.img") + vm._uefi = True + with pytest.raises(QemuError): + await vm._build_command() + + @pytest.mark.asyncio async def test_vnc_option(vm, fake_qemu_img_binary):