[Twisted-Python] Executing a number of commands over SSH
Hi all, I'd like to run a few commands on a remote machine over SSH. Looking at the conch packages, I'm not sure what the best approach is for this. The only way I can see to pass the set of commands down into the SSHConnection and SSHChannel objects is through my transport factory, i.e. I can't add or remove commands to be run on an already instantiated SSHConnection. Also, I'm a newcomer to the Twisted way of doing things, so it's not clear to me how I would go about returning the status and output of these commands back up to my code. I'm guessing I should create a Deferred somewhere, but some guidance on how these should bubble up through the twisted layers would be much appreciated. Thanks, James
On Sat, 14 Jun 2008 16:08:58 -0700, James Brady <james@webmynd.com> wrote:
Hi all, I'd like to run a few commands on a remote machine over SSH. Looking at the conch packages, I'm not sure what the best approach is for this.
The only way I can see to pass the set of commands down into the SSHConnection and SSHChannel objects is through my transport factory, i.e. I can't add or remove commands to be run on an already instantiated SSHConnection.
It sounds like you've figured out a lot of the difficult parts already. :) Consider how you execute a command once `SSHConnection.serviceStarted´ is called (if you looked at `sshsimpleclient.py´, then this is where the True, False, and Cat channels get opened). All you need to do to execute another command is what you would do to execute those initial commands in that method. As long as you have a reference to the SSHConnection instance, you can start arbitrary commands at any time you like. So the problem is really just figuring out how to have a reference to the SSHConnection instance in the code that knows it wants to start a new command.
Also, I'm a newcomer to the Twisted way of doing things, so it's not clear to me how I would go about returning the status and output of these commands back up to my code. I'm guessing I should create a Deferred somewhere, but some guidance on how these should bubble up through the twisted layers would be much appreciated.
Indeed, a Deferred should help. You can have one which is fired with the SSHConnection once it is available. Then, callbacks on this Deferred will be able to start any commands they want. One way to do this would be to pass a Deferred all the way down into the SSHConnection and fire it with `self´ in `serviceStarted´. This basically turns control flow inside-out, allowing arbitrary code which is not part of the SSHConnection class to use the connection. This has been a bit vague, but Conch certainly has a lot of pieces and it's hard to talk more specifically without having some code at hand. If you're still stuck, I suggest posting a short example of what you have (preferably self-contained and runnable) and then someone can make more specific code suggestions based on it. Jean-Paul
Indeed, a Deferred should help. You can have one which is fired with the SSHConnection once it is available. Then, callbacks on this Deferred will be able to start any commands they want. One way to do this would be to pass a Deferred all the way down into the SSHConnection and fire it with `self´ in `serviceStarted´. This basically turns control flow inside-out, allowing arbitrary code which is not part of the SSHConnection class to use the connection.
Hi Jean-Paul, Thanks for that advice - as you suggested I'm now passing a Deferred down into the SSHConnection to receive a reference to the connection object, which works really well. I now have a follow-on question. This is my situation: I'd like to set up an SSHConnection and associate it with a Machine object at program startup, then execute arbitrary commands over this SSHConnection at run-time. The problem I'm facing is that after instantiating the Twisted objects, calling reactor.run() doesn't return, so I can't create a standalone SSHConnection and attach it as a utility to a Machine object. For example: class Machine(object): def __init__(self, address): self.conn = MachineConnection(self, address, 'user') class MachineConnection(object): def __init__(self, address, user): self.address = address self.port = 22 self.user = user d=defer.Deferred() d.addCallback(self.__conn_ready) d.addErrback(self.__conn_err) factory = protocol.ClientFactory(self.user, d) # I'm actually using a subclass reactor.connectTCP(self.address, self.port, factory) reactor.run() Here, I've left out definitions of my conch subclasses, and methods on Machine and MachineConnection, but I don't think they're relevant. The issue is that reactor.run() in the last line of MachineConnection.__init__ starts Twisted up, creates the connection and so on, but doesn't return control to the original flow of code. I've tried starting separate threads for the connections, but Twisted seems to rely on running in the main thread. Are the any suggestions on how to set up backgrounded Twisted connections that can be interacted with as daemonic threads? Thank you, James
participants (2)
-
James Brady -
Jean-Paul Calderone