后续-
很高兴,我在下面引用的故障单现在已解决。下一个版本的Twisted中将包含更简单的API。原始答案仍然是使用Conch的有效方法,并且可能会揭示出一些有趣的细节,但是从Twisted
13.1开始,如果您只想运行命令并处理它的I /
O,那么这个简单的界面就可以使用。
不幸的是,使用Conch客户端API在SSH上执行命令需要花费大量的代码。即使您只想要明智无聊的默认行为,海螺也可以使您处理许多不同的层。但是,这肯定是可能的。这是我一直想要完成并添加到Twisted中的代码,以简化这种情况:
import sys, osfrom zope.interface import implementsfrom twisted.python.failure import Failurefrom twisted.python.log import errfrom twisted.internet.error import ConnectionDonefrom twisted.internet.defer import Deferred, succeed, setDebuggingfrom twisted.internet.interfaces import IStreamClientEndpointfrom twisted.internet.protocol import Factory, Protocolfrom twisted.conch.ssh.common import NSfrom twisted.conch.ssh.channel import SSHChannelfrom twisted.conch.ssh.transport import SSHClientTransportfrom twisted.conch.ssh.connection import SSHConnectionfrom twisted.conch.client.default import SSHUserAuthClientfrom 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.commandConnectedclass 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.finisheddef 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()
需要注意的一些事情:
- 它使用了Twisted 10.1中引入的新端点API。可以直接在上执行此 *** 作
reactor.connectTCP
,但是我这样做是为了使其更有用。无需实际询问连接的代码即可轻松地交换端点。 - 它根本不进行主机密钥验证!
_CommandTransport.verifyHostKey
是您实现该目标的地方。看一下twisted/conch/client/default.py
有关您可能想做的事情的一些提示。 - 它
$USER
必须是远程用户名,您可能希望将其作为参数。 - 它可能仅适用于密钥认证。如果要启用密码验证,则可能需要子类化
SSHUserAuthClient
和重写getPassword
以执行某些 *** 作。 - SSH和Conch的几乎所有层都在这里可见:
_CommandTransport
在底部,是一个简单的旧协议,用于实现SSH传输协议。它创建了一个…_CommandConnection
它实现了协议的SSH连接协商部分。完成后,…_CommandChannel
用于与新打开的SSH通道进行对话。_CommandChannel
执行实际的执行程序来启动命令。打开通道后,它将创建一个…的实例。StdoutEcho
,或您提供的任何其他协议。该协议将从您执行的命令中获取输出,并且可以写入命令的stdin。
有关使用更少的代码支持Twisted的进展,请参见http://twistedmatrix.com/trac/ticket/4698。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)