diff --git a/CHANGELOG b/CHANGELOG index 227773671..8f75b1dfa 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ # Change Log +## 2.2.56.1 28/01/2026 + +* Fix telnet keepalive options on macOS +* Upgrade dependencies +* Drop Python 3.8 support + ## 3.0.6 28/01/2026 * Sync appliances diff --git a/dev-requirements.txt b/dev-requirements.txt index df3f6b494..97267fbe7 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,4 +1,5 @@ -pytest==8.4.2 # version 8.4.2 is the last one supporting Python 3.9 +pytest==8.4.2; python_version == '3.9' # version 8.4.2 is the last one supporting Python 3.9 +pytest==9.0.2; python_version >= '3.10' flake8==7.3.0 pytest-timeout==2.4.0 pytest-asyncio==0.26.0 # upgrading leads to stuck / failed tests diff --git a/gns3server/utils/asyncio/telnet_server.py b/gns3server/utils/asyncio/telnet_server.py index aad60b19e..e74694b35 100644 --- a/gns3server/utils/asyncio/telnet_server.py +++ b/gns3server/utils/asyncio/telnet_server.py @@ -184,15 +184,25 @@ class AsyncioTelnetServer: await writer.drain() async def run(self, network_reader, network_writer): - sock = network_writer.get_extra_info("socket") sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) # 60 sec keep alives, close tcp session after 4 missed # Will keep a firewall from aging out telnet console. - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) - sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4) + try: + # Keepalive options are platform dependent: Linux uses TCP_KEEPIDLE, + # while macOS exposes TCP_KEEPALIVE (in Python >= 3.10). + if hasattr(socket, "TCP_KEEPIDLE"): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, 60) + elif hasattr(socket, "TCP_KEEPALIVE"): + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPALIVE, 60) + else: + raise AttributeError("module 'socket' has no attribute 'TCP_KEEPIDLE' or 'TCP_KEEPALIVE'") + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 10) + sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, 4) + except (AttributeError, OSError): + log.debug("Failed to tune TCP keepalive for telnet client; using OS defaults", exc_info=True) + #log.debug("New connection from {}".format(sock.getpeername())) # Keep track of connected clients diff --git a/requirements.txt b/requirements.txt index 527d08c33..7e0cd2a5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,8 +6,8 @@ websockets==15.0.1 # version 15.0.1 is the last to support Python 3.9 aiohttp>=3.13.3,<3.14 aiofiles>=25.1.0,<26.0 Jinja2>=3.1.6,<3.2 -sentry-sdk>=2.50.0,<3 # optional dependency -psutil>=7.2.1 +sentry-sdk>=2.52.0,<3 # optional dependency +psutil>=7.2.2 async-timeout>=5.0.1,<5.1; python_version < '3.11' # this library has effectively been upstreamed into Python 3.11+ distro>=1.9.0 py-cpuinfo>=9.0.0,<10.0