Search code examples
pythonsshparamiko

Paramiko `exec_command` timeout only works for short timeout, and exception type doesn't match


My Python script runs a remote command over ssh, using the Paramiko library's exec_command function. To test the timeout option to this command, I'm sending a do-nothing loop, and verifying that the exec_command function times out. However, the timeout only fires when I set it to a value less than 1. For example, the following code times out as expected:

command = 'while true; do :; done'
ssh = paramiko.SSHClient()
ssh.connect(ip, port=port, username=user, pkey=key, timeout=10)
_, stdout, stderr = ssh.exec_command(command, timeout=0.5)
print("Running command: {}".format(command))
exit_code = stdout.channel.recv_exit_status()

But when I change the timeout argument value to 1, it does not time out.

Another odd thing is that when the timeout does occur (with the 0.5 arg value), the exception type is paramiko.ssh_exception.SSHException, rather than socket.timeout, as the docs say it should be. The full exception message is paramiko.ssh_exception.SSHException: Timeout openning channel. But the stack trace starts at the exec_command line above, not the connect line:

Running command: while true; do :; done
Traceback (most recent call last):
  File "./t.py", line 40, in <module>
    _, stdout, stderr = ssh.exec_command(command, timeout=0.5)
  File "/usr/lib/python3/dist-packages/paramiko/client.py", line 414, in exec_command
    chan = self._transport.open_session(timeout=timeout)
  File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 703, in open_session
    timeout=timeout)
  File "/usr/lib/python3/dist-packages/paramiko/transport.py", line 828, in open_channel
    raise SSHException('Timeout openning channel.')
paramiko.ssh_exception.SSHException: Timeout openning channel.

So I have two questions:

  • Why does a timeout of 0.5 work, but 1 or greater does not?
  • Why does the exception report a timeout opening the channel, instead of a socket.timeout, even though the exception is coming from exec_command, not connect?

Any help is appreciated!


Solution

  • The timeout argument of SSHClient.exec_command has two purposes:

    • Time limit for opening the SSH "exec" channel.

      This is where the timeout for 0.5s is probably triggered, as it takes longer to open the channel on your server. I.e. the timeout has nothing to do with your infinite command.

      If you set longer timeout (1s), the channel succeeds to open.

      This indeed throws SSHException ("Timeout opening channel.").

    • Time limit for blocking operations, like reading/writing. You are not doing any reading/writing. This would throw socket.timeout.

      Channel.recv_exit_status does not seem to use the timeout. It waits forever.