From 54c851ce8d307fb1723b43068b797dcf807aa7f9 Mon Sep 17 00:00:00 2001 From: dv Date: Wed, 29 Apr 2026 12:46:15 +0300 Subject: [PATCH] =?UTF-8?q?=D1=84=D0=B8=D0=BA=D1=81=20=D0=BE=D1=82=D0=B2?= =?UTF-8?q?=D0=B0=D0=BB=D0=B0=20=D0=BF=D1=80=D0=B8=20=D1=83=D1=81=D0=BF?= =?UTF-8?q?=D0=B5=D1=88=D0=BD=D0=BE=D0=B9=20=D0=B0=D0=B2=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/services/discovery/ssh_client.py | 32 +++++++++++----- backend/app/services/ssh.py | 39 ++++++++++++++------ 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/backend/app/services/discovery/ssh_client.py b/backend/app/services/discovery/ssh_client.py index 0513884..d55aaef 100644 --- a/backend/app/services/discovery/ssh_client.py +++ b/backend/app/services/discovery/ssh_client.py @@ -84,21 +84,35 @@ async def ssh_run_multi( _log_attempt(i, len(options), host, opt) try: connect_kwargs = _build_connect_kwargs(host, port, opt) - async with asyncssh.connect(**connect_kwargs) as conn: - result = await asyncio.wait_for( - conn.run(command, check=True), timeout=timeout - ) - logger.info( - "[discovery] SSH auth succeeded for %s with %s(%s)", - host, opt.type, opt.username, - ) - return result.stdout + conn = await asyncssh.connect(**connect_kwargs) except Exception as exc: last_exc = exc logger.info( "[discovery] SSH auth failed for %s with %s(%s): %s", host, opt.type, opt.username, exc, ) + continue + + logger.info( + "[discovery] SSH auth succeeded for %s with %s(%s)", + host, opt.type, opt.username, + ) + try: + async with conn: + result = await asyncio.wait_for( + conn.run(command, check=True), timeout=timeout + ) + return result.stdout + except asyncio.TimeoutError as exc: + err = TimeoutError(f"SSH command timed out after {timeout}s on {host}: {command}") + logger.warning("[discovery] %s", err) + raise err from exc + except Exception as exc: + logger.warning( + "[discovery] SSH command failed on %s after auth with %s(%s): %s", + host, opt.type, opt.username, exc, + ) + raise raise last_exc diff --git a/backend/app/services/ssh.py b/backend/app/services/ssh.py index 923e946..a101bd8 100644 --- a/backend/app/services/ssh.py +++ b/backend/app/services/ssh.py @@ -251,21 +251,10 @@ async def _try_connect( try: conn = await asyncio.wait_for(asyncssh.connect(**connect_kwargs), timeout=15) - async with conn: - result = await asyncio.wait_for(conn.run(command, check=False), timeout=timeout) - duration = int((time.monotonic() - start) * 1000) - return SSHResult( - success=result.exit_status == 0, - stdout=(result.stdout or "").strip(), - stderr=(result.stderr or "").strip(), - exit_code=result.exit_status or 0, - duration_ms=duration, - auth_ok=True, # Connection was established — auth succeeded - ) except asyncio.TimeoutError: return SSHResult( success=False, - error="Connection timed out", + error="SSH connection timed out", duration_ms=int((time.monotonic() - start) * 1000), ) except (asyncssh.DisconnectError, asyncssh.PermissionDenied, asyncssh.ConnectionLost) as e: @@ -281,6 +270,32 @@ async def _try_connect( duration_ms=int((time.monotonic() - start) * 1000), ) + try: + async with conn: + result = await asyncio.wait_for(conn.run(command, check=False), timeout=timeout) + duration = int((time.monotonic() - start) * 1000) + return SSHResult( + success=result.exit_status == 0, + stdout=(result.stdout or "").strip(), + stderr=(result.stderr or "").strip(), + exit_code=result.exit_status or 0, + duration_ms=duration, + auth_ok=True, + ) + except asyncio.TimeoutError: + return SSHResult( + success=False, + error=f"SSH command timed out after {timeout}s", + duration_ms=int((time.monotonic() - start) * 1000), + auth_ok=True, + ) + except Exception as e: + return SSHResult( + success=False, + error=str(e), + duration_ms=int((time.monotonic() - start) * 1000), + auth_ok=True, + ) async def _save_last_auth(device: Device, db: AsyncSession, opt: _AuthOption) -> None: """Persist the successful auth method to device.ssh_last_auth."""