2011-01-06 6 views
12

필자는 여러 상자에서 실행되는 프로세스를 모니터링해야하는 꼬인 응용 프로그램이 있습니다. 내가 수동으로하는 방법은 'ssh와 ps'이다. 이제는 내가 뒤틀린 응용 프로그램을 사용하고 싶다. 2 가지 옵션이 있습니다.Twisted에서 ssh를 통해 원격 명령을 실행하는 가장 좋은 방법은 무엇입니까?

사용 paramiko 또는 정말 twisted.conch를 사용하려면 twisted.conch

의 힘을 활용하지만 내 연구는 주로 SSHServers 및 SSHClients을 만들 목적으로 믿고 나를 이끌었다. 내 요구 사항은 간단 remoteExecute(some_cmd)

는 그러나 나는 paramiko를 사용하여이 작업을 수행하는 방법을 알아낼 수 있었다 그러나 나는 사용 twisted.conch

코드 조각을 사용하여이 작업을 수행하는 방법에보고하기 전에 내 트위스트 응용 프로그램에 paramiko도 설치하려면 didnt는 twisted을 실행하는 방법 remote_cmds ssh를 사용하면 매우 감사하겠습니다. 감사.

답변

16

추가 정보 - 행복하게, 아래에서 참조한 티켓이 해결되었습니다. 더 간단한 API는 Twisted의 다음 릴리스에 포함될 것입니다. 원래의 대답은 여전히 ​​Conch를 사용하는 유효한 방법이며, 진행중인 작업에 대한 흥미로운 세부 사항을 표시 할 수 있지만, 명령을 실행하고 I/O (this simpler interface will work)를 처리하려는 경우 Twisted 13.1 이상에서 나타낼 수 있습니다.


아쉽게도 Conch 클라이언트 API를 사용하여 SSH에서 명령을 실행하는 데 많은 양의 코드가 필요합니다. Conch는 지루하고 지루한 기본 동작을 원할지라도 다양한 레이어를 처리합니다. 그러나 확실히 가능합니다.

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() 

어떤 일이에 대한주의 사항 :

  • 그것은 트위스트 10.1에 도입 된 새로운 엔드 포인트 API를 사용하여 여기에 내가이 사건을 단순화하기 위해 마치고 트위스트에 추가 했었어 일부 코드는 . reactor.connectTCP에서이 작업을 직접 수행 할 수도 있지만, 더 유용하게 사용할 끝점으로 사용했습니다. 엔드 포인트는 실제로 연결을 알기 원하는 코드없이 쉽게 스와핑 할 수 있습니다.
  • 호스트 키 확인이 전혀 없습니다! _CommandTransport.verifyHostKey은이를 구현할 곳입니다. 어떤 종류의 일을하고 싶은지 힌트를 얻으려면 twisted/conch/client/default.py을보십시오.
  • 원격 사용자 이름으로 $USER이 필요하며 이는 매개 변수가 될 수 있습니다.
  • 아마도 키 인증에서만 작동합니다. 암호 인증을 사용하려면 SSHUserAuthClient의 서브 클래스를 만들고 getPassword을 대체해야합니다.
  • 는 거의 모든 SSH와 조가의 층의 여기 볼 수 있습니다 :
    • _CommandTransport 하단의 SSH 전송 프로토콜을 구현하는 평범한 구식 프로토콜이다. ...
    • _CommandConnection 프로토콜의 SSH 연결 협상 부분을 구현합니다. 완료되면 ...
    • _CommandChannel 새로 열린 SSH 채널과 통신하는 데 사용됩니다. _CommandChannel 명령을 실행하기 위해 실제 exec를 수행합니다. 채널이 열리면 인스턴스가 생성됩니다.
    • StdoutEcho 또는 다른 프로토콜을 제공하십시오. 이 프로토콜은 사용자가 실행 한 명령의 출력을 가져오고 명령의 표준 입력에 쓸 수 있습니다.

적은 코드로이 지원에 트위스트 진전을 위해 http://twistedmatrix.com/trac/ticket/4698를 참조하십시오.

+0

감사합니다. exarkun! 당신이 옳게 언급했듯이,이 사소한 일의 단순하고 즉각적인 해결책이 있어야하기 때문에 그것은 정말 이상합니다. 그 방향으로 이미 효과가 있다니 다행이다. 신속한 대응에 다시 한번 감사드립니다. –

관련 문제