From 08d766a8b77b95b8e761a6cedac259c0573745d3 Mon Sep 17 00:00:00 2001 From: dv Date: Tue, 14 Apr 2026 15:52:08 +0300 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D1=82=D0=B0=D0=B9?= =?UTF-8?q?=D0=BC=D0=B0=D1=83=D1=82=D0=B0=20=D1=81=D1=81=D1=85=20=D0=B4?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=D0=B2=D0=B5=D1=80=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/discovery/ssh_client.py | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/backend/app/services/discovery/ssh_client.py b/backend/app/services/discovery/ssh_client.py index 57b6bfb..0513884 100644 --- a/backend/app/services/discovery/ssh_client.py +++ b/backend/app/services/discovery/ssh_client.py @@ -22,6 +22,8 @@ async def read_remote_file( remote_path: str, ) -> str: """SSH into host and return the contents of remote_path.""" + if not await is_port_open(host, port, timeout=3.0): + raise ConnectionRefusedError(f"{host}:{port} — порт недоступен") async with asyncssh.connect( host, port=port, username=username, password=password, known_hosts=None, connect_timeout=10, @@ -35,6 +37,9 @@ async def get_ip_neighbors( ) -> list[str]: """Run `ip neigh show` and return REACHABLE/DELAY neighbour IPs.""" try: + if not await is_port_open(host, port, timeout=3.0): + logger.info("[discovery] %s:%d — port closed, skipping ip neigh", host, port) + return [] async with asyncssh.connect( host, port=port, username=username, password=password, known_hosts=None, connect_timeout=10, @@ -46,12 +51,34 @@ async def get_ip_neighbors( return [] +# ── TCP port probe ──────────────────────────────────────────────────────────── + +async def is_port_open(host: str, port: int, timeout: float = 3.0) -> bool: + """Quick TCP connect check. Returns True if port is reachable, False otherwise.""" + try: + _, writer = await asyncio.wait_for( + asyncio.open_connection(host, port), timeout=timeout + ) + writer.close() + try: + await writer.wait_closed() + except Exception: + pass + return True + except Exception: + return False + + # ── Multi-auth helpers ──────────────────────────────────────────────────────── async def ssh_run_multi( host: str, port: int, options: list, command: str, timeout: int = 15, ) -> str: """Try each SSH auth option in order; return stdout on first success or raise.""" + # Fast pre-check: skip SSH entirely if port is not open + if not await is_port_open(host, port, timeout=3.0): + raise ConnectionRefusedError(f"{host}:{port} — порт недоступен (TCP check failed)") + last_exc: Exception = Exception("No auth options provided") for i, opt in enumerate(options): _log_attempt(i, len(options), host, opt)