首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Twisted中通过ssh运行远程命令的最佳方式?

在Twisted中通过ssh运行远程命令的最佳方式?
EN

Stack Overflow用户
提问于 2011-01-07 01:02:12
回答 1查看 7.5K关注 0票数 12

我有一个扭曲的应用程序,现在需要监视在几个机器上运行的进程。我手动做的方式是'ssh and ps',现在我想让我的扭曲的应用程序来做。我有两个选择。

使用paramiko或利用twisted.conch的强大功能

我真的很想使用twisted.conch,但我的研究让我相信它的主要目的是创建SSHServers和SSHClients。然而,我的需求是一个简单的remoteExecute(some_cmd)

我能够弄清楚如何使用paramiko来实现这一点,但在研究如何使用twisted.conch之前,我不想在我扭曲的应用程序中使用paramiko

关于如何使用ssh运行remote_cmds的使用twisted的代码片段将不胜感激。谢谢。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-01-07 02:03:33

后续-令人高兴的是,我下面引用的罚单现在已经解决了。更简单的API将包含在Twisted的下一个版本中。最初的答案仍然是使用Conch的有效方法,可能会揭示一些有趣的细节,但是从Twisted 13.1开始,如果你只想运行一个命令并处理它的I/O,this simpler interface will work

不幸的是,使用Conch客户端API在SSH上执行命令需要大量代码。Conch可以让你处理很多不同的层,即使你只是想要合理的、无聊的默认行为。然而,这当然是有可能的。下面是一些代码,我一直打算完成这些代码并添加到Twisted中,以简化这种情况:

代码语言:javascript
复制
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()

关于它需要注意的一些事情:

  • 它使用Twisted 10.1中引入的新端点API。可以直接在reactor.connectTCP上做这件事,但我把它作为端点来做,使它更有用;端点可以很容易地交换,而不需要实际请求连接的代码知道。
  • 它根本不做主机密钥验证!_CommandTransport.verifyHostKey是你要实现它的地方。在twisted/conch/client/default.py中可以找到一些关于你可能想要做的事情的提示,
  • 它需要$USER作为远程用户名,你可能希望它成为一个key身份验证可能只适用于密钥认证。如果您想要启用密码身份验证,您可能需要子类化SSH并重写SSH来执行something.
  • Almost。这里可以看到SSH和
    • _CommandTransport的所有层:SSHUserAuthClient在底部,这是一种实现getPassword传输协议的普通旧协议。它创建a...
    • _CommandConnection来实现协议的SSH连接协商部分。完成此操作后,将使用a...
    • _CommandChannel与新打开的SSH通道通信。_CommandChannel执行实际的exec来启动命令。一旦通道打开,它就会创建一个实例、of...
    • StdoutEcho,或您提供的任何其他协议。此协议将从您执行的命令中获得输出,并可以写入命令的stdin.

在Twisted中用更少的代码支持这一点的进展请参见http://twistedmatrix.com/trac/ticket/4698

票数 16
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4617507

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档