3

I'm trying to write a Client/Server using Twisted that will allow the client to issue remote commands on the server and receive response data in real time. i.e. If I run $> ssh server someProg.sh, I will see the results in 'real time', not all at once when the process finishes. Is this sort of thing possible in Twisted? Thanks.

Doug
  • 45
  • 4
  • 3
    This appears to be an exact duplicate of http://stackoverflow.com/questions/4617507/best-way-to-run-remote-commands-thru-ssh-in-twisted – David Wolever Aug 06 '11 at 01:20

2 Answers2

5

Absolutely. As already noted in a comment, you can do this by connecting to the SSH server directly with Twisted's "conch" library. This is more scalable (you can open lots of connections without any extra processes) and more portable (it can work on Windows) but it won't take into account your OpenSSH configuration, and you have to write a bunch of additional code to deal with things like host key verification. The other question doesn't directly address your main question here, either, which is about the output being processed as it arrives.

The simple answer is "yes", but here's a demonstration program that spawns several subprocesses and displays their output as it goes. You can replace the sys.executable with another program to spawn (i.e. ssh) and it'll work exactly the same way.

import os, sys

from twisted.internet.protocol import ProcessProtocol
from twisted.internet import reactor
from twisted.internet.defer import Deferred, gatherResults

script = """
import time
for x in range(3):
    time.sleep(1)
    print(x)
"""

class SimpleProcess(ProcessProtocol):
    def __init__(self, id, d):
        self.id = id
        self.d = d
    def outReceived(self, out):
        print('Received output: {out} from: {proc}'
              .format(out=repr(out), proc=self.id))
    def processEnded(self, reason):
        self.d.callback(None)

ds = []
for x in range(3):
    d = Deferred()
    reactor.callLater(
        x * 0.5, reactor.spawnProcess, SimpleProcess(x, d),
        sys.executable, [sys.executable, '-u', '-c', script],
        os.environ)
    ds.append(d)

gatherResults(ds).addBoth(lambda ignored: reactor.stop())

reactor.run()
Community
  • 1
  • 1
Glyph
  • 31,152
  • 11
  • 87
  • 129
  • 1
    Thanks for the responses. I realize now that my question seems to imply that I specifically want to use SSH but I'm actually trying to avoid it. When I said I was writing a client/server I meant that I need to control both ends; I also would rather operate without authentication, which SSH requires. – Doug Aug 18 '11 at 00:10
  • In that case, you would want to do `spawnProcess` in your server, response to some protocol message (i.e. somewhere down the stack from `dataReceived`) rather than in a `callLater` as this example shows. But please feel free to ask a more specific question with similar tags :-). – Glyph Sep 26 '11 at 21:47
  • Thanks; The unbuffered flag (-u) actually gets me the behavior I wanted. – Doug Nov 02 '11 at 04:21
-1

You could use paramiko lib http://www.lag.net/paramiko/

import paramiko

class XXX():

def ssh_processing(self, params):

        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy( paramiko.AutoAddPolicy() )

        ssh_connection = ssh.connect(ip, username=params['username'] , password=params['password'])

        result = self.exec_ssh(ssh, cmd)

def exec_ssh(self, ssh, cmd):
    self._log('Exec [%s]' % cmd)
    ( stdin, stdout, stderr ) = ssh.exec_command(cmd)
    data = {
            'stdin'  : '', #self._read_all(stdin),
            'stdout' : self._read_all(stdout), 
            'stderr' : self._read_all(stderr)
        }
    if len(data['stderr']):
        msg = 'SSH Error: [%s]' % data['stderr']
        self._error(msg)

    if 'command not found' in data['stderr']:
        raise Exception(msg)

    return data 
Michael_XIII
  • 175
  • 1
  • 14