From a1fcd29bce150485fe5b69cea24adbbbfc011ad3 Mon Sep 17 00:00:00 2001 From: YueGuobin Date: Wed, 30 Sep 2026 19:00:03 +0800 Subject: [PATCH] fix(qemu): build a command line for a headless node _console_options() fell through the "none" branch and returned None, so _build_command() blew up on `command.extend(None)`: a QEMU node with console_type "none" -- offered by the GUI, and what API/MCP-driven labs use to skip the console entirely -- failed to start with a 500 and a traceback. Return an empty list, exactly like _aux_options() already does, and cover it with a test that builds the command line for a headless node. Verified on a live instance: such a node now starts (204) with `console: null` and no -serial device in the QEMU command line. --- gns3server/compute/qemu/qemu_vm.py | 1 + tests/compute/qemu/test_qemu_vm.py | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/gns3server/compute/qemu/qemu_vm.py b/gns3server/compute/qemu/qemu_vm.py index e8c73774e..9309e25b5 100644 --- a/gns3server/compute/qemu/qemu_vm.py +++ b/gns3server/compute/qemu/qemu_vm.py @@ -2042,6 +2042,7 @@ class QemuVM(KernelDatapathMixin, BaseNode): return self._spice_with_agent_options(self.console) elif self._console_type != "none": raise QemuError(f"Console type {self._console_type} is unknown") + return [] def _aux_options(self): diff --git a/tests/compute/qemu/test_qemu_vm.py b/tests/compute/qemu/test_qemu_vm.py index 7b39bc4ae..1f4953c27 100644 --- a/tests/compute/qemu/test_qemu_vm.py +++ b/tests/compute/qemu/test_qemu_vm.py @@ -624,6 +624,22 @@ async def test_build_command(vm, fake_qemu_binary): ] +@pytest.mark.asyncio +async def test_build_command_console_none(vm): + """ + A headless QEMU node (console_type none) builds a command line without a + serial device: the console options for "none" used to fall through and + return None, which made node start fail with a TypeError. + """ + + vm.console_type = "none" + os.environ["DISPLAY"] = "0:0" + with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()): + cmd = await vm._build_command() + assert "-serial" not in cmd + assert "-netdev" in cmd + + @pytest.mark.asyncio async def test_build_command_manual_uuid(vm): """