. python+ssh 首先都会想到用 paramiko,可能因为水平有限,写出来的代码批量操作时就算用了多线程指令特别慢。所以直接封装ssh和scp
本人也是初学python,代码虽然感觉不太规范但能实现功能
准备工作:
- 机器首先密钥认证
- 目录结构
ssh
├── bin/ssh.py
├── conf/host.conf
└── log/201506/30/ 日志按日志分目录保存 - host配置文件:hosts.conf格式如下
ID 服务器 公网IP 内网IP 端口号 1 dev 201.120.111.xx 10.10.10.xx 22
代码:
#!/usr/bin/python # coding:utf8 import readline,os,commands,sys,threading,datetime,subprocess,re readline.set_completer_delims(' \t\n`~!@#$%^&*()=+[{]}\\|;:\'",<>/?') '''本地系统命令补全''' def allcommands(): a=commands.getoutput("PATH=$PATH:./:/usr/lib:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/cheung/bin;for c in $(echo $PATH |sed 's/:/ /g');do ls $c;done") return a.strip().split('\n') def alllocalpath(path=''): result = [] if not path: path = '.' for f in os.listdir(path): qf = os.path.join(path,f) if os.path.isdir(qf): result.append(f+os.sep) else: result.append(f) return result class BufferAwareCompleter(object): def __init__(self, custcmd, allcmd): self.options = custcmd self.current_candidates = [] self.allcmd = allcmd return def complete(self, text, state): response = None if state == 0: origline = readline.get_line_buffer() begin = readline.get_begidx() end = readline.get_endidx() being_completed = origline[begin:end] words = origline.split() if not words: self.current_candidates = sorted(self.options) else: try: if begin == 0: candidates = self.allcmd else: if origline.endswith(' '):words.append('') basedir,basefile = os.path.split(words[-1]) candidates = alllocalpath(basedir) being_completed = basefile if being_completed: self.current_candidates = [ w for w in candidates if w.startswith(being_completed) ] else: self.current_candidates = candidates except (KeyError, IndexError), err: self.current_candidates = [] try: response = self.current_candidates[state] except IndexError: response = None return response custcmd = ['exit','quit','ex','q','Q','bye'] allcmd = custcmd[:] allcmd.extend(allcommands()) readline.set_completer(BufferAwareCompleter(custcmd,allcmd).complete) readline.parse_and_bind('tab: complete') YearMouth = datetime.datetime.now().strftime('%Y%m') day = datetime.datetime.now().strftime('%d') subprocess.call(["mkdir -p ../log/%s/%s" % (YearMouth,day)],shell=True) class PrintHelp(object): '''输出类''' def __init__(self): self.cmd_ssh = [ '仅单服务器操作', '批量上传文件', '批量下载文件', ] def loopPrintInc(self): print for i, v in enumerate(self.cmd_ssh): print "%5s %-5s" % (i, v) class Config(object): '''获取IP列表''' def getipinfo(self): iplist = [] data=open('../conf/hosts.conf','r') ipinfo=data.readlines() if ipinfo: for i in ipinfo: ip = i.split()[3] if ip not in iplist: iplist.append(ip) return iplist '''生成字典用于单服操作''' def getipdict(self): ipinfo = [] dict = {} dictname = {} data=open('../conf/hosts.conf','r') servlist=data.readlines() if servlist: for i in servlist: ip = i.split()[3] name = i.split()[1] info = ip + ' ' + name if info not in ipinfo: ipinfo.append(info) for i, v in enumerate(ipinfo): print "%5s %-5s" % (i, v) dict[i]=v.split()[0] dictname[i]=v.split()[1] choose = raw_input("[choose nums]# ") if choose in qlist: exit() while not choose.isdigit(): choose = raw_input("[only input nums]# ") if choose in qlist: exit() num = int(choose) if dict.has_key(num): ip = dict[num] info = dictname[num] return ip,info '''交互输入指令''' def inputvalue(self): try: while True: printhelp_obj.loopPrintInc() print cmd_backlist = ['mv','half','shutdown','reboot','rm -fr','ex'] cmd = raw_input("[ssh_manage]#") if cmd in custcmd: exit() elif cmd in cmd_backlist: print "Dangerous command [%s],So exit." % cmd config_obj.inputvalue() elif cmd == '1': ip,info = config_obj.getipdict() while True: rawout = '[' + ip + '_' + info + ']#' scmd = raw_input(rawout) if scmd in qlist: config_obj.inputvalue() Control_obj.execommand(ip,scmd) elif cmd == '2': Control_obj.putfile() elif cmd == '3': Control_obj.getfile() if cmd: for ip in config_obj.getipinfo(): Control_obj.execommand(ip,cmd) '''如果不是特别熟悉避免使用多线程''' #threads = [] #for ip in config_obj.getipinfo(): # th = threading.Thread(target=Control_obj.execommand,args=(ip,cmd,)) # th.start() # threads.append(th) #for th in threads: # th.join() except EOFError: print exit() except KeyboardInterrupt: print exit() '''控制类''' class Control(object): '''执行命令''' def execommand(self,ip,cmd): data=open('../log/' + YearMouth + '/' + day + '/' + 'ssh.log','a') output=open('../log/' + YearMouth + '/' + day + '/' + 'cmd.log','a') history="%s|%s: \t%s\n" % (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),ip,cmd) p = subprocess.Popen(['ssh %s -l root -p 22 "%s"' % (ip,cmd)],shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout,stderr = p.communicate() if "sh" not in cmd: if stderr: result="[ %s ][ %s ] Execute Command: ' %s ',The Result is :\r\n\n\033[1;31m%s\033[0m" % (ip,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),cmd,stderr) else: result="[ %s ][ %s ] Execute Command: ' %s ',The Result is :\r\n\n\033[1;32m%s\033[0m" % (ip,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),cmd,stdout) print result else: result="[ %s ][ %s ] Execute Command: ' %s ',The Result is :\r\n\n%s" % (ip,datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),cmd,stdout) print result data.write(result) output.write(history) data.close() output.close() '''上传文件''' def putfile(self): local = raw_input("[本地文件路径]#") remote = raw_input("[远程上传路径]#") localfile = local.split('/')[-1] ullog = open('../log/' + YearMouth + '/' + day + '/' + 'upload.log','a') for ip in config_obj.getipinfo(): p = subprocess.Popen(["scp -rP 22 %s root@%s:%s" % (local,ip,remote)],shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout,stderr = p.communicate() if stderr: result="\033[1;31m[%s] Upload file \033[1;32m[%s]\033[0m \033[1;31mto [%s] : Failed\033[0m\n" % (datetime.datetime.now().strftime('%H:%M:%S'),localfile,ip) else: result="\033[1;32m[%s] Upload file \033[1;31m[%s]\033[0m \033[1;32mto [%s] [%s]: Ok\033[0m\n" % (datetime.datetime.now().strftime('%H:%M:%S'),localfile,ip,remote) print result ullog.write(result) ullog.close() config_obj.inputvalue() '''下载文件''' def getfile(self): remote = raw_input("[目标文件路径]#") local = raw_input("[本地绝对路径]#") remotefile = remote.split('/')[-1] dllog = open('../log/' + YearMouth + '/' + day + '/' + 'download.log','a') for ip in config_obj.getipinfo(): localdir = local + '/' + ip if os.path.exists(localdir): pass else: os.mkdir(localdir) p = subprocess.Popen(["scp -rP 22 root@%s:%s %s" % (ip,remote,localdir)],shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) stdout,stderr = p.communicate() if stderr: result="\033[1;31m[%s] Download file \033[1;32m[%s]\033[0m \033[1;31mfrom [%s] : Failed\033[0m\n" % (datetime.datetime.now().strftime('%H:%M:%S'),remotefile,ip) else: result="\033[1;32m[%s] Download file \033[1;31m[%s]\033[0m \033[1;32mfrom [%s] in [%s] : Ok\033[0m\n" % (datetime.datetime.now().strftime('%H:%M:%S'),remotefile,ip,localdir) print result dllog.write(result) dllog.close() config_obj.inputvalue() if __name__ == "__main__": dirname=os.path.split(os.path.realpath(sys.argv[0]))[0] os.chdir(dirname) print "欢迎使用[SSH批量操作脚本] 选择一项进行操作 或 直接输入系统命令 [exit]退出" printhelp_obj = PrintHelp() config_obj = Config() Control_obj = Control() config_obj.inputvalue()
操作功能演示:
登陆后直接对所有服务器进行操作
w
选择单服器操作
批量上传
批量下载
查看日志
[root@10-10-64-58 ssh]# tree log/
log/
└── 201506
└── 30
├── cmd.log
├── download.log
├── ssh.log
└── upload.log
2 directories, 4 files