2014-10-09 68 views
2

讓我從這開始......我根本不瞭解Python;我在圈子裏,我根本不明白。我完全接受替代和簡單的方法。Python扭曲的海螺 - 如何停止與多個連接的反應堆?

我的目標:連接到不同的服務器,在每個服務器上運行相同的命令,並且以後(如現在/不在)使用輸出來提高生產力。真棒。

我有什麼:在某處找到了一些代碼(我會試着找到一個鏈接並更新它)。我修改了一下。它連接到不同的服務器,運行相同的命令。

問題:一旦一切完成,我不知道如何停止反應堆。而我真的想要停止它不按cntrl+c。我想我需要推遲一些事情,但我不知道在哪裏或在哪裏。我覺得像SSHChannel關閉的時候,需要以某種方式冒泡到SSHConnection,停止服務...所以交通工具可以知道怎麼了?我一直想以某種方式將每個reactor.connectTCP(server, 22, factory)都推遲。我覺得我可能需要一個控制器類。我嘗試了這些東西,但沒有正確嘗試。也許gatherResults可能會有所幫助,但是,我不知道該怎麼說。

from twisted.conch.ssh import transport, connection, userauth, channel, common 
from twisted.internet import defer, protocol, reactor 
import sys, struct 

USER = 'username' 
PASS = 'thisisforpersonalusesoicanstoreit!' 
CMD = 'echo "merely this and nothing more"' 


from twisted.python import log 
import sys 
log.startLogging(sys.stdout) 


class ClientCommandTransport(transport.SSHClientTransport): 
    def __init__(self, username, password, command): 
     self.username = username 
     self.password = password 
     self.command = command 

    def verifyHostKey(self, pubKey, fingerprint): 
     print fingerprint 
     return defer.succeed(True) 

    def connectionSecure(self): 
     self.requestService(
      PasswordAuth(self.username, self.password, 
         ClientConnection(self.command)))  

class PasswordAuth(userauth.SSHUserAuthClient): 
    def __init__(self, user, password, connection): 
     userauth.SSHUserAuthClient.__init__(self, user, connection) 
     self.password = password 

    def getPassword(self, prompt=None): 
     return defer.succeed(self.password) 

class ClientConnection(connection.SSHConnection): 
    def __init__(self, cmd, *args, **kwargs): 
     connection.SSHConnection.__init__(self) 
     self.command = cmd 

    def serviceStarted(self): 
     self.openChannel(CommandChannel(self.command, conn=self)) 

class CommandChannel(channel.SSHChannel): 
    name = 'session' 

    def __init__(self, command, *args, **kwargs): 
     channel.SSHChannel.__init__(self, *args, **kwargs) 
     self.command = command 
     self.data = '' 

    def channelOpen(self, data): 
     self.conn.sendRequest(
      self, 'exec', common.NS(self.command), wantReply=True).addCallback(
                  self._gotResponse) 

    def _gotResponse(self, _): 
     self.conn.sendEOF(self) 
     self.loseConnection() 

    def dataReceived(self, data): 
     #self.data += data 
     print data 

    def request_exit_status(self, data): 
     (status,) = struct.unpack('>L', data) 
     # print 'exit status = ', status 

class ClientCommandFactory(protocol.ClientFactory): 
    def __init__(self, command=CMD): 
     self.username = USER 
     self.password = PASS 
     self.command = command 

    def buildProtocol(self, addr): 
     protocol = ClientCommandTransport(
      self.username, self.password, self.command) 
     return protocol  


masters = ['server1','server2','server3','server4','server5'] 

factory = ClientCommandFactory() 

for server in masters: 
    print server 
    reactor.connectTCP(server, 22, factory) 

reactor.run() 

我也有推遲getPage一個http請求劇(沒有工作),但我似乎無法與電抗器和SSH連接到重新應用它。

這些都是資源,我真希望我能看得懂:


與下面的一個答案......我測試了向下傳遞到工廠的參考,並最終停止在SSHChannelclosed()校長如果工廠沒有它的數組中再連接(或任何python調用數組)。

我廠更新到現在也包括這種方法:

class ClientCommandFactory(protocol.ClientFactory): 

    def clientConnectionLost(self, connector, reason): 
     print reason 

我看了一下日誌,因爲我對所發生的事情,一般興趣......(有些是我自己的聲明,一些是默認的)

014-10-16 13:42:58-0500 [SSHChannel session (0) on SSHService ssh-connection on ClientCommandTransport,client] closed last TCP connection 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] service stopped 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] connection lost 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionLost'>: Connection to the other side was lost in a non-clean fashion: Connection lost. 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] ] 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] connection lost 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] [Failure instance: Traceback (failure with no frames): <class 'twisted.internet.error.ConnectionLost'>: Connection to the other side was lost in a non-clean fashion: Connection lost. 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] ] 
2014-10-16 13:42:58-0500 [ClientCommandTransport,client] Stopping factory <__main__.ClientCommandFactory instance at 0x02323030> 
2014-10-16 13:42:58-0500 [-] Main loop terminated. 

所以......它說連接失去了不潔淨的方式。有沒有更好的辦法,我應該阻止事情..?

+0

我在一個星期前寫了這個問題...決定放棄paramiko。剛剛意識到我需要使用相同的連接運行多個命令(並且'&&'連接不夠)。我仍然不知道該怎麼做。最後,我想根據命令的響應來處理斷開連接,但沒有任何問題。 – 2014-10-15 15:48:19

+0

您是否檢查[this](http://stackoverflow.com/questions/13920962/what-is-the-correct-way-to-close-a-twisted-conch-ssh-connection?lq=1)? 這可能是因爲你必須自己實施一些事情來妥善地關閉連接。無論如何,這與工作完成後停止反應堆無關。 – koleS 2014-10-18 12:15:32

+0

@ koleS - 謝謝,我沒有看到。我嘗試在下面有人給我的答案中應用這些東西,但是反應器偶爾停止得太快,因爲在第一個或第二個完成後沒有添加所有連接。到處都有這麼多事情發生,我不確定在哪裏或如何捕捉或管理事情。 – 2014-10-18 18:16:46

回答

0

所以首先這是行不通的,因爲connectTCP接受與IP address字符串作爲第一個參數,你從這個名單通過元素:

masters = ['server1','server2','server3','server4','server5'] 

所有任務後停止反應器完成是很常見的扭曲的用例。一種方法是存儲工廠中執行的任務計數器。每次實例化該工廠協議的實例時,每增加一個該協議實例(任務)返回的結果,減少計數器,當計數器達到0時停止反應堆。示例代碼:

from twisted.conch.ssh import transport, connection, userauth, channel, common 
from twisted.internet import defer, protocol, reactor 
import sys, struct 

USER = 'username' 
PASS = 'thisisforpersonalusesoicanstoreit!' 
CMD = 'echo "merely this and nothing more"' 


from twisted.python import log 
import sys 
log.startLogging(sys.stdout) 


class ClientCommandTransport(transport.SSHClientTransport): 
    def __init__(self, username, password, command, factory): 
     self.username = username 
     self.password = password 
     self.command = command 
     self.factory = factory 

    def verifyHostKey(self, pubKey, fingerprint): 
     print fingerprint 
     return defer.succeed(True) 

    def connectionSecure(self): 
     self.requestService(
      PasswordAuth(self.username, self.password, 
         ClientConnection(self.command, self.factory))) 

class PasswordAuth(userauth.SSHUserAuthClient): 
    def __init__(self, user, password, connection): 
     userauth.SSHUserAuthClient.__init__(self, user, connection) 
     self.password = password 

    def getPassword(self, prompt=None): 
     return defer.succeed(self.password) 

class ClientConnection(connection.SSHConnection): 
    def __init__(self, cmd, *args, **kwargs): 
     connection.SSHConnection.__init__(self) 
     self.command = cmd 
     self.factory = factory 

    def serviceStarted(self): 
     self.openChannel(CommandChannel(self.command, self.factory, conn=self)) 

class CommandChannel(channel.SSHChannel): 
    name = 'session' 

    def __init__(self, command, factory, *args, **kwargs): 
     channel.SSHChannel.__init__(self, *args, **kwargs) 
     self.command = command 
     self.data = '' 
     self.factory = factory 
     self.factory.num_connections += 1 
     self.factory.connections.append(self) 

    def channelOpen(self, data): 
     self.conn.sendRequest(
      self, 'exec', common.NS(self.command), wantReply=True).addCallback(
                  self._gotResponse) 

    def _gotResponse(self, _): 
     self.conn.sendEOF(self) 
     self.loseConnection() 
     self.factory.num_connections -= 1 
     self.factory.connections.remove(self) 
     if self.factory.num_connections == 0: 
      reactor.stop() 

    def dataReceived(self, data): 
     #self.data += data 
     print data 

    def request_exit_status(self, data): 
     (status,) = struct.unpack('>L', data) 
     # print 'exit status = ', status 

class ClientCommandFactory(protocol.ClientFactory): 
    def __init__(self, command=CMD): 
     self.username = USER 
     self.password = PASS 
     self.command = command 
     self.connections = [] 
     self.num_connections = 0 

    def buildProtocol(self, addr): 
     protocol = ClientCommandTransport(
      self.username, self.password, self.command, self) 
     return protocol  


masters = ['server1','server2','server3','server4','server5'] 

factory = ClientCommandFactory() 

for server in masters: 
    print server 
    reactor.connectTCP(server, 22, factory) 

reactor.run() 

我在這裏做的是我在工廠self.connectionsself.num_connections中添加了兩個變量來存儲對工廠中連接的引用並計算連接數。然後在工廠的buildProtocol工廠將自己傳遞給ClientCommandTransport,該工廠依次將工廠參考傳遞給ClientConnection,後者最終將需要的參考傳遞給工廠,即CommandChannel。每當一個CommandChannel的實例被實例化時,它就有了對工廠的引用,所以它將連接數增加1,並將其自身添加到存儲在工廠中的連接列表中。我認爲當一個任務/命令完成時,會觸發_gotResponse回調。因此,每當它被觸發時,它就像之前一樣丟失連接,但是現在,此外,它減少了連接計數器並從工廠中刪除對自身的引用。它還檢查是否有其他開放連接,如果沒有停止反應堆。

我還沒有測試過這個代碼,但它是Twisted中的一個常見模式,即工廠保存了它創建的協議實例的引用列表,以便每個實例都可以通過工廠訪問其他實例,並且能夠一旦所有事例都已完成,停止反應堆。

注意,這個層次也有些深,Factory - >ClientCommandTransport - >ClientConnection - >CommandChannel,我不知道這是否是最佳的解決方案,通過參考出廠一路下跌。

其中一個變量是實際上是多餘的 - 你可以存儲或者只self.num_connections和增加/減少,或self.connections,添加/從列表中刪除實例並使用len(self.connections),看是否還有任何打開的連接。

+0

哦...所以我發佈的確實起作用。我只是無法阻止反應堆,一旦我想完成的事情。我現在沒有時間通過​​你的整個回答,但我會稍微回來。 – 2014-10-15 18:41:20

+0

你的答案看起來不錯,但...有點什麼我在想什麼,但我不能這樣做是出於某種原因。不是「冒泡」斷開連接,我想我需要向下傳遞信息。只要您確信自己提出的想法,就不需要爲我測試代碼。 – 2014-10-15 18:47:31

+0

如果你認爲我的答案'看起來不錯',那麼你可以接受它。我不明白你的意思是什麼東西向下(什麼東西,向下到什麼點等)。我認爲連接的數量必須存儲在工廠中,並且每次調用'buildProtocol'時都會實例化所有其他類,因此工廠是唯一可以跟蹤連接數量的實體。 [Here](http://krondo.com/blog/?p=1209)你可以找到一個很好的扭曲教程。其中的一個部分,即[這一個](http://bit.ly/1Dd2wWk)提出了計算任務的想法。 – koleS 2014-10-15 20:00:37