Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long-running ssh commands in python paramiko module (and how to end them)

I want to run a tail -f logfile command on a remote machine using python's paramiko module. I've been attempting it so far in the following fashion:

interface = paramiko.SSHClient()
#snip the connection setup portion
stdin, stdout, stderr = interface.exec_command("tail -f logfile")
#snip into threaded loop
print stdout.readline()

I'd like the command to run as long as necessary, but I have 2 problems:

  1. How do I stop this cleanly? I thought of making a Channel and then using the shutdown() command on the channel when I'm through with it- but that seems messy. Is it possible to do something like sent Ctrl-C to the channel's stdin?
  2. readline() blocks, and I could avoid threads if I had a non-blocking method of getting output- any thoughts?
like image 624
user17925 Avatar asked Apr 17 '09 15:04

user17925


People also ask

What is timeout paramiko?

The connection timeout can be set with the timeout parameter (that indicated the number of seconds for the time out as described here) of the connect function.

What is the alternative of paramiko?

While you wait for paramiko on Python 3, you can use putty.

What is the use of paramiko module in python?

Paramiko is a Python library that makes a connection with a remote device through SSh. Paramiko is using SSH2 as a replacement of SSL to make a secure connection between two devices. It also supports the SFTP client and server model.

How do I get stdout from paramiko?

Using exec_command() in Paramiko returns a tuple (stdin, stdout, stderr) . Most of the time, you just want to read stdout and ignore stdin and stderr . You can get the output the command by using stdout. read() (returns a string) or stdout.


3 Answers

Instead of calling exec_command on the client, get hold of the transport and generate your own channel. The channel can be used to execute a command, and you can use it in a select statement to find out when data can be read:

#!/usr/bin/env python
import paramiko
import select
client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('host.example.com')
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command("tail -f /var/log/everything/current")
while True:
  rl, wl, xl = select.select([channel],[],[],0.0)
  if len(rl) > 0:
      # Must be stdout
      print channel.recv(1024)

The channel object can be read from and written to, connecting with stdout and stdin of the remote command. You can get at stderr by calling channel.makefile_stderr(...).

I've set the timeout to 0.0 seconds because a non-blocking solution was requested. Depending on your needs, you might want to block with a non-zero timeout.

like image 97
Andrew Aylett Avatar answered Oct 04 '22 08:10

Andrew Aylett


1) You can just close the client if you wish. The server on the other end will kill the tail process.

2) If you need to do this in a non-blocking way, you will have to use the channel object directly. You can then watch for both stdout and stderr with channel.recv_ready() and channel.recv_stderr_ready(), or use select.select.

like image 34
JimB Avatar answered Oct 04 '22 09:10

JimB


Just a small update to the solution by Andrew Aylett. The following code actually breaks the loop and quits when the external process finishes:

import paramiko
import select

client = paramiko.SSHClient()
client.load_system_host_keys()
client.connect('host.example.com')
channel = client.get_transport().open_session()
channel.exec_command("tail -f /var/log/everything/current")
while True:
    if channel.exit_status_ready():
        break
    rl, wl, xl = select.select([channel], [], [], 0.0)
    if len(rl) > 0:
        print channel.recv(1024)
like image 32
Anton Beloglazov Avatar answered Oct 04 '22 07:10

Anton Beloglazov