ArtOfWarfare
ArtOfWarfare

Reputation: 21486

None of Paramiko's Read methods work for me?

I have this class I've written:

class Remote(object):
    def __init__(self, address, username, password):
        self.address  = address
        self.username = username
        self.password = password

    def stdout(self, s):
        print('out: ' + s)

    def stderr(self, s):
        print('err: ' + s)

    def sh(self, s):
        from paramiko  import AutoAddPolicy, SSHClient
        from threading import Thread
        from time      import sleep

        ssh = SSHClient()
        ssh.set_missing_host_key_policy(AutoAddPolicy())
        ssh.connect(self.address, username = self.username, password = self.password)
        stdin, stdout, stderr = ssh.exec_command(s)

        def monitor(channel, method):
            while True:
                for line in channel.readlines():
                    method(line)
                sleep(1)

        Thread(target = monitor, args = (stdout, self.stdout)).start()
        Thread(target = monitor, args = (stderr, self.stderr)).start()

Then I try running it like this:

>>> from remote import Remote
>>> address  = <removed>
>>> username = 'root'
>>> password = <removed>
>>> r = Remote(address, username, password)
>>> r.sh('echo Hello')

And I get no output. If I change the monitor method around so instead of:

for line in channel.readlines():
    method(line)

I have simply method(channel.read()) or method(channel.readline()), but in that case, I just see:

out:
err:

Once a second - it never actually gives me the expected results of:

out: Hello

I know that my address, username, and password are all right, because I can feed them into fabric just fine.

>>> from fabric.api        import env
>>> from fabirc.operations import sudo
>>> env.host_string, env.user, env.password = address, username, password
>>> sudo('echo Hello')
[<omitted>]: Hello

What am I doing wrong in my paramiko based class which fabric is evidently able to handle?

Edit

I want the method to by asynchronous. It should return immediately. For example, if I do this:

r1 = Remote(<one set of credentials removed>)
r2 = Remote(<another set of credentials removed>)
r1.sh('echo Hello; sleep 5; echo World')
r2.sh('echo Hello; sleep 5; echo World')

Then the results should be:

out: Hello
out: Hello
out: World
out: World

Indicating that the two calls ran in parallel, not:

out: Hello
out: World
out: Hello
out: World

Which would indicate that the two calls ran synchronously.

Upvotes: 2

Views: 62

Answers (1)

fernandezcuesta
fernandezcuesta

Reputation: 2448

The problem is that while True loop in monitor prevents the thread to end. Leaving the first part as is and changing the last lines to:

def monitor(channel, method):
    while True:
        l = channel.readline()
        if l:
            method(l)
        else:
            break
tout = Thread(target = monitor, args = (stdout, self.stdout))
terr = Thread(target = monitor, args = (stderr, self.stderr))
tout.start()
terr.start()
tout.join()
terr.join()
ssh.close()

will print the output of the given command line by line, while there's something to be returned.

Upvotes: 1

Related Questions