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:
- import os
- import re
- import time
- import sys
- import pyssh
- from threading import Thread
- class SSHController(Thread):
- """Connect to remote host with SSH and issue commands.
- This is a facade/wrapper that uses PySSH to spawn and control an SSH client.
- You must have OpenSSH installed.
- @ivar host_name: Host name or IP address
- @ivar user_name: User name
- @ivar password: Password
- @ivar prompt: Command prompt (or partial string matching the end of the prompt)
- @ivar ssh: Instance of a pyssh.Ssh object
- """
- def __init__(self, host_name, user_name, password, cmd):
- """
- @param host_name: Host name or IP address
- @param user_name: User name
- @param password: Password
- @param prompt: Command prompt (or partial string matching the end of the prompt)
- """
- Thread.__init__(self)
- self.host_name = host_name
- self.user_name = user_name
- self.password = password
- self.port = '22' #default SSH port
- self.ssh = None
- self.cmd = cmd
- def login(self):
- """Connect to a remote host and login.
- """
- self.ssh = pyssh.Ssh(self.user_name, self.host_name, self.port)
- self.ssh.login(self.password)
- def run_command(self, command):
- """Run a command on the remote host.
- @param command: Unix command
- @return: Command output
- @rtype: String
- """
- response = self.ssh.sendcmd(command)
- return self.__strip_output(command, response)
- def logout(self):
- """Close the connection to the remote host.
- """
- self.ssh.logout()
- def run_atomic_command(self, command):
- """Connect to a remote host, login, run a command, and close the connection.
- @param command: Unix command
- @return: Command output
- @rtype: String
- """
- self.login()
- command_output = self.run_command(command)
- self.logout()
- return command_output
- def __strip_output(self, command, response):
- """Strip everything from the response except the actual command output.
- @param command: Unix command
- @param response: Command output
- @return: Stripped output
- @rtype: String
- """
- lines = response.splitlines()
- # if our command was echoed back, remove it from the output
- if command in lines[0]:
- lines.pop(0)
- # remove the last element, which is the prompt being displayed again
- lines.pop()
- # append a newline to each line of output
- lines = [item + '\n' for item in lines]
- # join the list back into a string and return it
- return ''.join(lines)
- def run(self):
- self.run_atomic_command(self.cmd)
- print time.ctime()
- pinglist = []
- for host in range(1,2):
- ip = "10.0.0."+str(host)
- print ip
- current = SSHController(ip,"tao","123456","ls")
- pinglist.append(current)
- current.start()
- for pingle in pinglist:
- pingle.join()
- print time.ctime()
But again, this script failed. It says: ValueError: signal only works in main thread.I 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:
- #!/usr/bin/env python
- import os, sys, socket
- import paramiko
- from threading import Thread
- class myThread(Thread):
- def __init__ (self, ip, usr, pwd, command):
- Thread.__init__(self)
- self.ip = ip
- self.usr= usr
- self.pwd= pwd
- self.command = command
- def run (self):
- client = paramiko.SSHClient()
- client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
- client.connect(self.ip, username=self.usr, password=self.pwd)
- stdin, stdout, stderr = client.exec_command(self.command)
- if self.command.startswith("sudo")
- stdin.write(“123456\n”)
- stdin.flush()
- for line in stdout.read().splitlines():
- print 'host: %s: %s' % (self.ip, line)
- for line in stderr.read().splitlines():
- print 'host: %s: %s' % (self.ip, line)
- def test (self):
- pass # some code to test this class
- mythreads = []
- hosts = []
- cmd = ""
- if __name__ == "__main__":
- num = int(sys.argv[1])
- print "total %d nodes\n" % num
- for i in range(2,2+num):
- hosts.append("10.0.0." + str(sys.argv[i]))
- for i in range(2+num,len(sys.argv)):
- cmd += " " + sys.argv[i]
- print "cmd to execute: %s\n" % cmd
- for ip in hosts:
- t = myThread(ip, "tao","123456",cmd)
- mythreads.append(t);
- t.start()
- for thread in mythreads:
- thread.join()
- print "execution on %s completes!\n" % thread.ip
- 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
本文介绍了一种使用Python批量执行SSH命令到多个远程服务器的方法,并通过多线程解决并发执行的问题。重点阐述了如何利用paramiko库实现远程服务器的连接、命令执行、读取输出及错误处理,同时提供了处理SSH命令执行状态的技巧。

468

被折叠的 条评论
为什么被折叠?



