Python example of SSH in multi-threads

本文介绍了一种使用Python批量执行SSH命令到多个远程服务器的方法,并通过多线程解决并发执行的问题。重点阐述了如何利用paramiko库实现远程服务器的连接、命令执行、读取输出及错误处理,同时提供了处理SSH命令执行状态的技巧。


I need to execute commands on several remote servers at the same time via ssh. Writing shell script to send commands to servers with code below is quite simple but has trouble to know when all the tasks are finished on all servers.

            ssh $ip “nohup sh go.sh > log 2>&1 &” 

Then I turn to python…first try the library pyssh:
With following code:

Python代码   收藏代码
  1. import os  
  2. import re  
  3. import time  
  4. import sys  
  5. import pyssh  
  6. from threading import Thread   
  7.   
  8.   
  9. class SSHController(Thread):   
  10.   
  11.   
  12.     """Connect to remote host with SSH and issue commands. 
  13.     This is a facade/wrapper that uses PySSH to spawn and control an SSH client. 
  14.     You must have OpenSSH installed.  
  15.  
  16.  
  17.     @ivar host_name: Host name or IP address 
  18.     @ivar user_name: User name 
  19.     @ivar password: Password 
  20.     @ivar prompt: Command prompt (or partial string matching the end of the prompt) 
  21.     @ivar ssh: Instance of a pyssh.Ssh object 
  22.     """  
  23.     def __init__(self, host_name, user_name, password, cmd):  
  24.         """ 
  25.         @param host_name: Host name or IP address 
  26.         @param user_name: User name 
  27.         @param password: Password 
  28.         @param prompt: Command prompt (or partial string matching the end of the prompt) 
  29.         """  
  30.         Thread.__init__(self)  
  31.         self.host_name = host_name  
  32.         self.user_name = user_name  
  33.         self.password = password  
  34.         self.port = '22'  #default SSH port  
  35.         self.ssh = None  
  36.         self.cmd = cmd   
  37.   
  38.   
  39.     def login(self):  
  40.   
  41.         """Connect to a remote host and login. 
  42.         """  
  43.         self.ssh = pyssh.Ssh(self.user_name, self.host_name, self.port)  
  44.         self.ssh.login(self.password)  
  45.   
  46.   
  47.     def run_command(self, command):  
  48.         """Run a command on the remote host. 
  49.         @param command: Unix command 
  50.         @return: Command output 
  51.         @rtype: String 
  52.         """  
  53.         response = self.ssh.sendcmd(command)  
  54.         return self.__strip_output(command, response)   
  55.   
  56.   
  57.     def logout(self):  
  58.         """Close the connection to the remote host. 
  59.         """  
  60.         self.ssh.logout()  
  61.   
  62.   
  63.     def run_atomic_command(self, command):  
  64.         """Connect to a remote host, login, run a command, and close the connection. 
  65.         @param command: Unix command 
  66.         @return: Command output 
  67.         @rtype: String 
  68.         """  
  69.         self.login()  
  70.         command_output = self.run_command(command)  
  71.         self.logout()  
  72.         return command_output  
  73.   
  74.   
  75.     def __strip_output(self, command, response):  
  76.         """Strip everything from the response except the actual command output. 
  77.         @param command: Unix command 
  78.         @param response: Command output 
  79.         @return: Stripped output 
  80.         @rtype: String 
  81.         """  
  82.         lines = response.splitlines()  
  83.         # if our command was echoed back, remove it from the output  
  84.         if command in lines[0]:  
  85.             lines.pop(0)  
  86.         # remove the last element, which is the prompt being displayed again  
  87.         lines.pop()  
  88.         # append a newline to each line of output  
  89.         lines = [item + '\n' for item in lines]  
  90.         # join the list back into a string and return it  
  91.         return ''.join(lines)  
  92.   
  93.   
  94.     def run(self):          
  95.         self.run_atomic_command(self.cmd)  
  96.   
  97.   
  98. print time.ctime()  
  99. pinglist = []  
  100. for host in range(1,2):  
  101.    ip = "10.0.0."+str(host)  
  102.    print ip  
  103.    current = SSHController(ip,"tao","123456","ls")  
  104.    pinglist.append(current)  
  105.    current.start()  
  106.   
  107.   
  108. for pingle in pinglist:  
  109.    pingle.join()   
  110.   
  111.   
  112. print time.ctime()  

 

But again, this script failed.  It says:  ValueError: signal only works in main threadI do not know if it’s pyssh library’s problem.
Then I turn to a library named paramiko, and finally I succeed!
First you should install this library; possibly you may need another library pycrypto-2.0.1.
With all things ready, and the following script:
 

Python代码   收藏代码
  1. #!/usr/bin/env python  
  2. import os, sys, socket  
  3. import paramiko  
  4. from threading import Thread   
  5.   
  6.   
  7. class myThread(Thread):   
  8.   
  9.   
  10.     def __init__ (self, ip, usr, pwd, command):  
  11.         Thread.__init__(self)  
  12.         self.ip = ip  
  13.         self.usr= usr  
  14.         self.pwd= pwd  
  15.         self.command = command  
  16.   
  17.   
  18.     def run (self):  
  19.         client = paramiko.SSHClient()  
  20.         client.set_missing_host_key_policy(paramiko.AutoAddPolicy())  
  21.         client.connect(self.ip, username=self.usr, password=self.pwd)  
  22.         stdin, stdout, stderr = client.exec_command(self.command)  
  23.         if self.command.startswith("sudo")  
  24.                   stdin.write(“123456\n”)  
  25.                   stdin.flush()  
  26.         for line in stdout.read().splitlines():  
  27.                 print 'host: %s: %s' % (self.ip, line)  
  28.         for line in stderr.read().splitlines():  
  29.                 print 'host: %s: %s' % (self.ip, line)  
  30.   
  31.   
  32.     def test (self):  
  33.         pass # some code to test this class  
  34.   
  35.  mythreads = []  
  36. hosts = []  
  37. cmd = ""  
  38. if __name__ == "__main__":  
  39.     num = int(sys.argv[1])  
  40.     print "total %d nodes\n" % num  
  41.     for i in range(2,2+num):  
  42.         hosts.append("10.0.0." + str(sys.argv[i]))  
  43.     for i in range(2+num,len(sys.argv)):  
  44.         cmd += " " + sys.argv[i]  
  45.     print "cmd to execute: %s\n" % cmd  
  46.   
  47.   
  48.     for ip in hosts:  
  49.         t = myThread(ip, "tao","123456",cmd)  
  50.         mythreads.append(t);  
  51.         t.start()  
  52.   
  53.   
  54.     for thread in mythreads:  
  55.         thread.join()  
  56.         print "execution on %s completes!\n" % thread.ip  
  57. print "test done!"  

 

 

This script accepts arguments like this: 2 1 4 du –h
    First argument 2 means I want to execute this command on two hosts, and followed with the last part of ip. ( ip of my machines all have the pattern of 10.0.0.*).
    Then the command follows the hosts. If you need to execute a sudo command, remember to modify /etc/sudoers to allow you send ssh commands without a real tty.
    You can add a line like this to walk around this problem.

Defaults:tao !requiretty

文章转载地址: http://standalone.iteye.com/blog/430189
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值