@@ -28,7 +28,9 @@ class NanoKVMSSHCommandError(NanoKVMSSHError):
2828class NanoKVMSSH :
2929 """SSH client for NanoKVM terminal access."""
3030
31- def __init__ (self , host : str , username : str = DEFAULT_SSH_USERNAME , port : int = 22 ) -> None :
31+ def __init__ (
32+ self , host : str , username : str = DEFAULT_SSH_USERNAME , port : int = 22
33+ ) -> None :
3234 """Initialize the SSH client."""
3335 self .host = host
3436 self .port = port
@@ -42,9 +44,10 @@ async def authenticate(self, password: str) -> None:
4244 self .ssh_client .set_missing_host_key_policy (paramiko .AutoAddPolicy ())
4345
4446 try :
47+ client = self .ssh_client # Capture for lambda
4548 await loop .run_in_executor (
4649 None ,
47- lambda : self . ssh_client .connect (
50+ lambda : client .connect (
4851 self .host ,
4952 port = self .port ,
5053 username = self .username ,
@@ -53,9 +56,9 @@ async def authenticate(self, password: str) -> None:
5356 )
5457 )
5558 except paramiko .AuthenticationException as e :
56- raise NanoKVMSSHAuthenticationError (f"SSH authentication failed: { e } " )
59+ raise NanoKVMSSHAuthenticationError (f"SSH authentication failed: { e } " ) from e
5760 except (paramiko .SSHException , paramiko .BadHostKeyException , OSError ) as e :
58- raise NanoKVMSSHAuthenticationError (f"SSH connection failed: { e } " )
61+ raise NanoKVMSSHAuthenticationError (f"SSH connection failed: { e } " ) from e
5962
6063 async def disconnect (self ) -> None :
6164 """Close SSH connection."""
@@ -66,7 +69,9 @@ async def disconnect(self) -> None:
6669 async def run_command (self , command : str , timeout : int = 30 ) -> str :
6770 """Run a command via SSH and return output."""
6871 if not self .ssh_client :
69- raise NanoKVMSSHNotConnectedError ("SSH not connected, call authenticate first" )
72+ raise NanoKVMSSHNotConnectedError (
73+ "SSH not connected, call authenticate first"
74+ )
7075 loop = asyncio .get_running_loop ()
7176 try :
7277 output , error = await asyncio .wait_for (
@@ -81,10 +86,11 @@ async def run_command(self, command: str, timeout: int = 30) -> str:
8186 except asyncio .TimeoutError :
8287 raise NanoKVMSSHCommandError (
8388 f"SSH command timed out after { timeout } seconds"
84- )
89+ ) from None
8590
8691 def _exec_command_sync (self , command : str ) -> tuple [str , str ]:
8792 """Synchronous SSH command execution."""
93+ assert self .ssh_client is not None # Should be set after authenticate()
8894 stdin , stdout , stderr = self .ssh_client .exec_command (command )
8995 output = stdout .read ().decode ('utf-8' )
9096 error = stderr .read ().decode ('utf-8' )
0 commit comments