fix: SPICE socket binding failure

This commit is contained in:
grossmj 2026-10-20 17:16:44 +07:00
parent 1498282c6b
commit cfba748677
No known key found for this signature in database
GPG Key ID: 1E7DD6DBB53FF3D7
4 changed files with 45 additions and 5 deletions

View File

@ -2064,7 +2064,7 @@ class QemuVM(KernelDatapathMixin, BaseNode):
console_host = "::"
except OSError as e:
raise QemuError(f"Could not check if IPv6 is enabled: {e}")
return ["-spice", f"addr={console_host},port={port},disable-ticketing", "-vga", "qxl"]
return ["-spice", f"addr={console_host},port={port},disable-ticketing=on", "-vga", "qxl"]
else:
return []

View File

@ -99,15 +99,26 @@ def is_ipv6_enabled() -> bool:
try:
with socket.socket(socket.AF_INET6, socket.SOCK_STREAM) as sock:
sock.bind(("::1", 0))
return True
except OSError as e:
if e.errno in (errno.EADDRNOTAVAIL, errno.EAFNOSUPPORT):
# EADDRNOTAVAIL is the errno if IPv6 modules/drivers are loaded but disabled.
# EAFNOSUPPORT is the errno if IPv6 modules/drivers are not loaded at all.
return False
if e.errno == errno.EADDRINUSE:
return True
raise
pass
else:
raise
# Applications like SPICE resolve the listen address with AI_ADDRCONFIG,
# which fails (EAI_ADDRFAMILY) for "::" when the IPv6 stack is loaded but no
# non-loopback IPv6 address is configured on the host.
try:
socket.getaddrinfo(
"::", None, socket.AF_UNSPEC, socket.SOCK_STREAM, 0, socket.AI_PASSIVE | socket.AI_ADDRCONFIG
)
except (socket.gaierror, UnicodeError):
return False
return True
def md5sum(filename):

View File

@ -481,7 +481,7 @@ async def test_spice_option(vm, fake_qemu_img_binary):
vm._console_type = "spice"
vm._console = 5905
options = await vm._build_command()
assert "-spice addr=127.0.0.1,port=5905,disable-ticketing" in " ".join(options)
assert "-spice addr=127.0.0.1,port=5905,disable-ticketing=on" in " ".join(options)
assert "-vga qxl" in " ".join(options)

29
tests/utils/test_ipv6.py Normal file
View File

@ -0,0 +1,29 @@
import socket
from unittest.mock import patch
from gns3server.utils import is_ipv6_enabled
def test_is_ipv6_enabled_no_ipv6_support():
with patch("socket.has_ipv6", False):
assert is_ipv6_enabled() is False
def test_is_ipv6_enabled_no_configured_ipv6_address():
"""
IPv6 stack loaded (binding ::1 works) but getaddrinfo with AI_ADDRCONFIG
fails, like SPICE does when no non-loopback IPv6 address is configured.
"""
error = socket.gaierror(
getattr(socket, "EAI_ADDRFAMILY", socket.EAI_FAMILY), "Address family for hostname not supported"
)
with patch("socket.has_ipv6", True), patch("socket.socket"), patch("socket.getaddrinfo", side_effect=error):
assert is_ipv6_enabled() is False
def test_is_ipv6_enabled():
with patch("socket.has_ipv6", True), patch("socket.socket"), patch("socket.getaddrinfo", return_value=[]):
assert is_ipv6_enabled() is True