diff --git a/docs/dependencies/qemu.md b/docs/dependencies/qemu.md new file mode 100644 index 000000000..001399bb4 --- /dev/null +++ b/docs/dependencies/qemu.md @@ -0,0 +1,28 @@ +# QEMU support + +GNS3 server requires QEMU 6.2 or newer. This is the minimum QEMU version that the server actively supports on Linux. + +For compatibility checks, the project evaluates the Linux distributions that are commonly used in server deployments and that still have supported releases: CentOS Stream, Debian, Fedora, openSUSE, RHEL, SLES, and Ubuntu LTS. Other distributions are assumed to ship similar package versions and are treated by the same rule: if the distro ships QEMU 6.2 or newer, it is considered compatible; if it ships an older version, it is not supported by GNS3 server. + +This document reflects the currently supported release set as of October 2026, based on the active releases listed by the major distro end-of-life trackers and package versions shipped by those releases. + +## Minimum supported version + +- Minimum supported QEMU version: 6.2 +- QEMU 6.2 was first shipped in Ubuntu 22.04 LTS and is the baseline used to determine whether a Linux distribution is suitable for GNS3 server. +- Distributions shipping QEMU 6.2 or later are generally acceptable. +- Older package versions are considered unsupported by policy. + +## Currently supported Linux releases and shipped QEMU versions + +The following supported releases are the current reference points used by the project. They are based on the active release lists published by the distributions' end-of-life trackers and the QEMU versions typically shipped by those releases. + +| Distribution | Current supported OS release(s) | QEMU version shipped | Source | +| --- | --- | --- | --- | +| Ubuntu LTS | 24.04 LTS, 22.04 LTS | 8.2 for 24.04, 6.2 for 22.04 | https://endoflife.date/ubuntu | +| Debian | 13 (trixie), 12 (bookworm) | 9.x for 13, 7.2 for 12 | https://endoflife.date/debian | +| Fedora | 42, 41, 40 | 9.x | https://endoflife.date/fedora | +| openSUSE | Leap 15.6, Tumbleweed | 8.x to 9.x | https://endoflife.date/opensuse | +| CentOS Stream | 10, 9 | 9.x / 8.x | https://endoflife.date/centos | +| RHEL | 10, 9.5, 9.4 | 9.x / 8.x | https://endoflife.date/redhat-enterprise-linux | +| SLES | 15 SP7, 15 SP6 | 8.x | https://endoflife.date/sles | diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index 9d882953e..0f2222f77 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -2316,16 +2316,7 @@ class QemuVM(KernelDatapathMixin, BaseNode): f"file={disk},if=none,id=drive{disk_index},index={disk_index},media=disk{extra_drive_options}", ] ) - if self._qemu_version and parse_version(self._qemu_version) >= parse_version("4.2.0"): - # The ‘ide-drive’ device is deprecated since version 4.2.0 - # https://qemu.readthedocs.io/en/latest/system/deprecated.html#ide-drive-since-4-2 - options.extend( - ["-device", f"ide-hd,drive=drive{disk_index},bus=ahci{disk_index}.0,id=drive{disk_index}"] - ) - else: - options.extend( - ["-device", f"ide-drive,drive=drive{disk_index},bus=ahci{disk_index}.0,id=drive{disk_index}"] - ) + options.extend(["-device", f"ide-hd,drive=drive{disk_index},bus=ahci{disk_index}.0,id=drive{disk_index}"]) elif interface == "nvme": options.extend( [ @@ -2721,15 +2712,12 @@ class QemuVM(KernelDatapathMixin, BaseNode): async def _disable_graphics(self): """ - Disable graphics depending of the QEMU version + Disable graphics for Qemu if no graphics options are specified. """ if any(opt in self._options for opt in ["-display", "-nographic", "-curses", "-sdl-spice", "-vnc"]): return [] - if self._qemu_version and parse_version(self._qemu_version) >= parse_version("3.0"): - return ["-display", "none"] - else: - return ["-nographic"] + return ["-display", "none"] async def _run_with_hardware_acceleration(self, qemu_path, options): """ @@ -2854,8 +2842,8 @@ class QemuVM(KernelDatapathMixin, BaseNode): """ self._qemu_version = await self.manager.get_qemu_version(self.qemu_path) - if self._qemu_version and parse_version(self._qemu_version) < parse_version("2.4.0"): - raise QemuError("Qemu version 2.4 or later is required to run Qemu VMs") + if self._qemu_version and parse_version(self._qemu_version) < parse_version("6.2.0"): + raise QemuError("Qemu version 6.2 or later is required to run Qemu VMs") vm_name = self._name.replace(",", ",,") project_path = self.project.path.replace(",", ",,") additional_options = self._options.strip() @@ -2877,14 +2865,6 @@ class QemuVM(KernelDatapathMixin, BaseNode): if await self._run_with_hardware_acceleration(self.qemu_path, self._options): if sys.platform.startswith("linux"): command.extend(["-enable-kvm"]) - # Issue on some combo Intel CPU + KVM + Qemu 2.4.0 - # https://github.com/GNS3/gns3-server/issues/685 - if ( - self._qemu_version - and parse_version(self._qemu_version) >= parse_version("2.4.0") - and self.platform == "x86_64" - ): - command.extend(["-machine", "smm=off"]) elif sys.platform.startswith("darwin"): command.extend(["-enable-hax"]) command.extend(["-boot", f"order={self._boot_priority}"]) diff --git a/tests/compute/qemu/test_qemu_vm.py b/tests/compute/qemu/test_qemu_vm.py index da5293261..f48418d4b 100644 --- a/tests/compute/qemu/test_qemu_vm.py +++ b/tests/compute/qemu/test_qemu_vm.py @@ -645,7 +645,7 @@ async def test_build_command_console_none(vm): @pytest.mark.asyncio async def test_build_command_manual_uuid(vm): """ - If user has set a uuid we keep it + If user has set a UUID we keep it """ vm.options = "-uuid e1c307a4-896f-11e6-81a5-3c07547807cc" @@ -657,75 +657,34 @@ async def test_build_command_manual_uuid(vm): @pytest.mark.asyncio -async def test_build_command_kvm_below_2_4(linux_platform, vm, fake_qemu_binary): +async def test_build_command_kvm_below_6_2(linux_platform, vm, fake_qemu_binary): """ Qemu 2.4 introduce an issue with KVM """ - vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.3.2") + vm.manager.get_qemu_version = AsyncioMagicMock(return_value="6.1.0") with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True): with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): with pytest.raises(QemuError): await vm._build_command() -@pytest.mark.asyncio -async def test_build_command_kvm_2_4(linux_platform, vm, fake_qemu_binary): - """ - Qemu 2.4 introduce an issue with KVM - """ - - vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.4.2") - os.environ["DISPLAY"] = "0:0" - with asyncio_patch("gns3server.compute.qemu.qemu_vm.QemuVM._run_with_hardware_acceleration", return_value=True): - with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process: - cmd = await vm._build_command() - nio = vm._local_udp_tunnels[0][0] - assert cmd == [ - fake_qemu_binary, - "-name", - "test", - "-m", - "256M", - "-smp", - "cpus=1,maxcpus=1,sockets=1", - "-enable-kvm", - "-machine", - "smm=off", - "-boot", - "order=c", - "-uuid", - vm.id, - "-serial", - f"telnet:127.0.0.1:{vm._internal_console_port},server,nowait", - "-net", - "none", - "-device", - "i82801b11-bridge,id=dmi_pci_bridge1", - "-device", - "pci-bridge,id=pci-bridge1,bus=dmi_pci_bridge1,chassis_nr=0x1,addr=0x1,shpc=off", - "-device", - f"e1000,mac={vm._mac_address},bus=pci-bridge1,addr=0x00,netdev=gns3-0", - "-netdev", - f"socket,id=gns3-0,udp=127.0.0.1:{nio.rport},localaddr=127.0.0.1:{nio.lport}", - "-nographic", - ] - - @pytest.mark.asyncio async def test_build_command_without_display(vm): - vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0") + vm.manager.get_qemu_version = AsyncioMagicMock(return_value="6.2.0") os.environ["DISPLAY"] = "" with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): cmd = await vm._build_command() - assert "-nographic" in cmd + print(cmd) + assert "-display" in cmd + assert "none" in cmd @pytest.mark.asyncio async def test_build_command_two_adapters(vm, fake_qemu_binary): - vm.manager.get_qemu_version = AsyncioMagicMock(return_value="2.5.0") + vm.manager.get_qemu_version = AsyncioMagicMock(return_value="6.2.0") os.environ["DISPLAY"] = "0:0" vm.adapters = 2 with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): @@ -760,7 +719,8 @@ async def test_build_command_two_adapters(vm, fake_qemu_binary): f"e1000,mac={int_to_macaddress(macaddress_to_int(vm._mac_address) + 1)},bus=pci-bridge1,addr=0x01,netdev=gns3-1", "-netdev", f"socket,id=gns3-1,udp=127.0.0.1:{nio2.rport},localaddr=127.0.0.1:{nio2.lport}", - "-nographic", + "-display", + "none", ]