我有一个扭曲的应用程序,现在需要监视在几个机器上运行的进程。我手动做的方式是'ssh and ps',现在我想让我的扭曲的应用程序来做。我有两个选择。
使用paramiko或利用twisted.conch的强大功能
我真的很想使用twisted.conch,但我的研究让我相信它的主要目的是创建SSHServers和SSHClients。然而,我的需求是一个简单的remoteExecute(some_cmd)
我能够弄清楚如何使用paramiko来实现这一点,但在研究如何使用twisted.conch之前,我不想在我扭曲的应用程序中使用paramiko
关于如何使用ssh运行remote_cmds的使用twisted的代码片段将不胜感激。谢谢。
发布于 2011-01-07 02:03:33
后续-令人高兴的是,我下面引用的罚单现在已经解决了。更简单的API将包含在Twisted的下一个版本中。最初的答案仍然是使用Conch的有效方法,可能会揭示一些有趣的细节,但是从Twisted 13.1开始,如果你只想运行一个命令并处理它的I/O,this simpler interface will work。
不幸的是,使用Conch客户端API在SSH上执行命令需要大量代码。Conch可以让你处理很多不同的层,即使你只是想要合理的、无聊的默认行为。然而,这当然是有可能的。下面是一些代码,我一直打算完成这些代码并添加到Twisted中,以简化这种情况:
import sys, os
from zope.interface import implements
from twisted.python.failure import Failure
from twisted.python.log import err
from twisted.internet.error import ConnectionDone
from twisted.internet.defer import Deferred, succeed, setDebugging
from twisted.internet.interfaces import IStreamClientEndpoint
from twisted.internet.protocol import Factory, Protocol
from twisted.conch.ssh.common import NS
from twisted.conch.ssh.channel import SSHChannel
from twisted.conch.ssh.transport import SSHClientTransport
from twisted.conch.ssh.connection import SSHConnection
from twisted.conch.client.default import SSHUserAuthClient
from twisted.conch.client.options import ConchOptions
# setDebugging(True)
class _CommandTransport(SSHClientTransport):
_secured = False
def verifyHostKey(self, hostKey, fingerprint):
return succeed(True)
def connectionSecure(self):
self._secured = True
command = _CommandConnection(
self.factory.command,
self.factory.commandProtocolFactory,
self.factory.commandConnected)
userauth = SSHUserAuthClient(
os.environ['USER'], ConchOptions(), command)
self.requestService(userauth)
def connectionLost(self, reason):
if not self._secured:
self.factory.commandConnected.errback(reason)
class _CommandConnection(SSHConnection):
def __init__(self, command, protocolFactory, commandConnected):
SSHConnection.__init__(self)
self._command = command
self._protocolFactory = protocolFactory
self._commandConnected = commandConnected
def serviceStarted(self):
channel = _CommandChannel(
self._command, self._protocolFactory, self._commandConnected)
self.openChannel(channel)
class _CommandChannel(SSHChannel):
name = 'session'
def __init__(self, command, protocolFactory, commandConnected):
SSHChannel.__init__(self)
self._command = command
self._protocolFactory = protocolFactory
self._commandConnected = commandConnected
def openFailed(self, reason):
self._commandConnected.errback(reason)
def channelOpen(self, ignored):
self.conn.sendRequest(self, 'exec', NS(self._command))
self._protocol = self._protocolFactory.buildProtocol(None)
self._protocol.makeConnection(self)
def dataReceived(self, bytes):
self._protocol.dataReceived(bytes)
def closed(self):
self._protocol.connectionLost(
Failure(ConnectionDone("ssh channel closed")))
class SSHCommandClientEndpoint(object):
implements(IStreamClientEndpoint)
def __init__(self, command, sshServer):
self._command = command
self._sshServer = sshServer
def connect(self, protocolFactory):
factory = Factory()
factory.protocol = _CommandTransport
factory.command = self._command
factory.commandProtocolFactory = protocolFactory
factory.commandConnected = Deferred()
d = self._sshServer.connect(factory)
d.addErrback(factory.commandConnected.errback)
return factory.commandConnected
class StdoutEcho(Protocol):
def dataReceived(self, bytes):
sys.stdout.write(bytes)
sys.stdout.flush()
def connectionLost(self, reason):
self.factory.finished.callback(None)
def copyToStdout(endpoint):
echoFactory = Factory()
echoFactory.protocol = StdoutEcho
echoFactory.finished = Deferred()
d = endpoint.connect(echoFactory)
d.addErrback(echoFactory.finished.errback)
return echoFactory.finished
def main():
from twisted.python.log import startLogging
from twisted.internet import reactor
from twisted.internet.endpoints import TCP4ClientEndpoint
# startLogging(sys.stdout)
sshServer = TCP4ClientEndpoint(reactor, "localhost", 22)
commandEndpoint = SSHCommandClientEndpoint("/bin/ls", sshServer)
d = copyToStdout(commandEndpoint)
d.addErrback(err, "ssh command / copy to stdout failed")
d.addCallback(lambda ignored: reactor.stop())
reactor.run()
if __name__ == '__main__':
main()关于它需要注意的一些事情:
reactor.connectTCP上做这件事,但我把它作为端点来做,使它更有用;端点可以很容易地交换,而不需要实际请求连接的代码知道。_CommandTransport.verifyHostKey是你要实现它的地方。在twisted/conch/client/default.py中可以找到一些关于你可能想要做的事情的提示,$USER作为远程用户名,你可能希望它成为一个key身份验证可能只适用于密钥认证。如果您想要启用密码身份验证,您可能需要子类化SSH并重写SSH来执行something._CommandTransport的所有层:SSHUserAuthClient在底部,这是一种实现getPassword传输协议的普通旧协议。它创建a..._CommandConnection来实现协议的SSH连接协商部分。完成此操作后,将使用a..._CommandChannel与新打开的SSH通道通信。_CommandChannel执行实际的exec来启动命令。一旦通道打开,它就会创建一个实例、of...StdoutEcho,或您提供的任何其他协议。此协议将从您执行的命令中获得输出,并可以写入命令的stdin.
在Twisted中用更少的代码支持这一点的进展请参见http://twistedmatrix.com/trac/ticket/4698。
https://stackoverflow.com/questions/4617507
复制相似问题