python+ssh 批量管理linux服务器

python批量操作   subprocess   Shell/Python  

. python+ssh 首先都会想到用 paramiko,可能因为水平有限,写出来的代码批量操作时就算用了多线程指令特别慢。所以直接封装ssh和scp

本人也是初学python,代码虽然感觉不太规范但能实现功能

准备工作:
  1. 机器首先密钥认证
  2. 目录结构
    ssh
    ├── bin/ssh.py
    ├── conf/host.conf
    └── log/201506/30/ 日志按日志分目录保存
  3. 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