设为首页 收藏本站
查看: 834|回复: 0

[经验分享] [python] 简单主机批量管理工具(多线程模式)

[复制链接]

尚未签到

发表于 2017-11-7 09:16:12 | 显示全部楼层 |阅读模式
                      题目:简单主机批量管理工具

需求:
1、主机分组,主机信息配置文件用configparser解析
2、可批量执行命令、发送文件,结果实时返回,执行格式如下
batch_run  -h h1,h2,h3   -g web_clusters,db_servers    -cmd  "df -h" 
batch_scp   -h h1,h2,h3   -g web_clusters,db_servers  -action put  -local test.py  -remote /tmp/ 
3、主机用户名密码、端口可以不同
4、执行远程命令使用paramiko模块
5、批量命令需使用multiprocessing并发
6、记录操作日志

README
设计说明
1、按提题目要求,使用各个规定好的模块来开发,实现起来很简单,特别是使用multiprocessing实现多线程并发操作,本来以为会最难的部分,却是最简单的,建议后来者把这功能放在最后来实现。
2、困扰我较长时间的倒是主机信息文件中,内容格式的设计。由于题目要求主机用户名和端口可以不同,也就是你在执行一条命令的时候,连接远程的主机列表中,账号密码端口不一定都相同,这些信息都是从文件中读取。我可以在文件里,按照ip:port:username:passwd存放主机信息,但考虑到实际生产环境中,一般都是相同应用的几台主机这些信息都一致(便于管理需要),参照了他人的格式,我最终选择了如下格式(请见后面hosts.txt示例)。
3、接收文件功能我没有实现,考虑到从多台主机上同时拷贝文件到本机上,会使文件重复覆盖,直接拷贝不可行,其中一种理想的方式是,文件拷贝过来后,将文件名+来源主机名这样的方式。这也许是出题人考虑到这个功能实现会有些麻烦,所以题目里只要求发送文件,并未要求接收文件。
over!



目录结构
├── bin
│   ├── start.py    主程序
├── conf
│   ├── hosts.txt    主机信息配置文件
│   └── server.conf    程序运行配置文件
└── log
    └── log.txt     操作日志

start.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import paramiko
import os, time, configparser, logging, json,re
from multiprocessing import Process

def formattime():  ####格式化时间,输出格式如:2017-09-16 16:32:35
    return time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())

####读取配置文件####
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
config_file = os.path.join(base_dir, 'conf/server.conf')    ####设置应用配置文件所在文件路径
cf = configparser.ConfigParser()        ####创建一个ConfigParser() 实例,用于读取配置信息
cf.read(config_file)                                        ####读取应用配置文件信息
####设定日志目录####
logfile = cf.get('log', 'logfile')
####读取主机组信息####
hostsfile = cf.get('hosts_conf', 'hosts_file')  ####获取主机配置文件所在文件路径
hf = configparser.ConfigParser()        ####创建另外一个ConfigParser() 实例,用于读取主机相关信息
hf.read(hostsfile)                              ####从主机配置文件读取信息
sects = hf.sections()
####设置日志格式###
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s %(levelname)s %(message)s',
                    datefmt='%Y-%m-%d %H:%M:%S',
                    filename=logfile,
                    filemode='a+')
def gethostinfo():
    ####从主机配置文件中取出主机的IP/PORT/USERNAME/PASSWORD信息,组成类似 {group_name:{ip:{port:22,username:try,passwd:123456}} 形式####
    dict = {}
    for a in sects:
        ip_list = hf.get(a, 'ip').split(',')
        port = hf.getint(a, 'port')
        username = hf.get(a, 'username')
        password = hf.get(a, 'password')
        dict_ip = {}
        for ip in ip_list:
            dict_ip[ip] = {'port': port, 'username': username, 'password': password}
        # print(dict_ip)
        dict[a] = dict_ip
    #print(dict)
    return dict

####远程执行命令####
def remote_ssh(ip,port,username,password,cmd):
    try:
        transport = paramiko.Transport((ip, port))
        transport.connect(username=username, password=password)
        ssh = paramiko.SSHClient()
        ssh._transport = transport
        stdin, stdout, stderr = ssh.exec_command(cmd)
        print(stdout.read().decode(),stderr.read().decode())
    except Exception as e:
        print(e)
####远程传输文件####
def remote_ftp(ip,port,username,password,action,local_file,remote_file):
    try:
        transport = paramiko.Transport((ip, port))
        transport.connect(username=username, password=password)
        sftp = paramiko.SFTPClient.from_transport(transport)
        #print(type(action),len(action))
        if action == 'put':
            print('准备上传文件')
            sftp.put(local_file,remote_file)
            print('传输完毕')
        elif action == 'get':
            print("暂不支持get操作")
            # sftp.get(remote_file,local_file)
        else:
            print("error, no put or get",action)
    except Exception as e:
        print(e)
    finally:
        transport.close()

if __name__ == '__main__':
    hostinfo_dict = gethostinfo()
    while True:
        cmd = input('>>>:').strip()
        ####需要输入类似: batch_run -h h1,h2,h3 -g web_clusters,db_servers -cmd "df -h"
        logging.info(cmd)
        if 'batch_run' in cmd:
            catch = re.findall('batch_run\s+-h\s+(.*?)\s+-g\s+(.*?)\s+-cmd\s+[\"|\'](.*?)[\"|\']',cmd,re.I)
            #print(catch)
            input_ip_list = catch[0][0].split(',')
            input_group_list = catch[0][1].split(',')
            input_running_cmd = catch[0][2]
            #print(input_ip_list,input_group_list,input_running_cmd)
            for group_name in input_group_list:
                if group_name in hostinfo_dict:
                    for ip in input_ip_list:
                        if ip in hostinfo_dict[group_name]:
                            print("login,", ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'],hostinfo_dict[group_name][ip]['password'], input_running_cmd)
                            #remote_ssh(ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'], hostinfo_dict[group_name][ip]['password'], input_running_cmd)
                            p = Process(target=remote_ssh, args=(ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'], hostinfo_dict[group_name][ip]['password'], input_running_cmd,))  ####使用多线程同时处理ssh请求,args中最后的逗号不能省略
                            p.start()
                            #p.join() ####开启该行为串行操作
        elif 'batch_scp' in cmd:
            catch = re.findall('batch_scp\s+-h\s+(.*?)\s+-g\s+(.*?)\s+-action\s+(.*?)\s-local\s+(.*?)\s+-remote\s+(.*)', cmd, re.I)
            #print(catch)
            input_ip_list = catch[0][0].split(',')
            input_group_list = catch[0][1].split(',')
            input_action = catch[0][2]
            input_local_file = catch[0][3]
            input_remote_file = catch[0][4]
            #print(input_ip_list, input_group_list, input_action,input_local_file,input_remote_file)
            for group_name in input_group_list:
                if group_name in hostinfo_dict:
                    for ip in input_ip_list:
                        if ip in hostinfo_dict[group_name]:
                            print("transfer,", ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'],hostinfo_dict[group_name][ip]['password'], input_action,input_local_file,input_remote_file)
                            #remote_ftp(ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'],hostinfo_dict[group_name][ip]['password'], input_action, input_local_file, input_remote_file)
                            p = Process(target=remote_ftp, args=(ip, hostinfo_dict[group_name][ip]['port'], hostinfo_dict[group_name][ip]['username'],hostinfo_dict[group_name][ip]['password'], input_action, input_local_file, input_remote_file,))  ####使用多线程同时处理ssh请求,args中最后的逗号不能省略
                            p.start()
        else:
            print(' 命令输入错误,请按 batch_run -h h1,h2,h3 -g web_clusters,db_servers -cmd "df -h"\n',
                  '或者 batch_scp -h h1,h2,h3 -g web_clusters,db_servers -action put -local test.py -remote /tmp/ 格式输入命令')




hosts.txt示例,可自行修改
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[g1]
ip = 192.168.1.1,192.168.1.2,192.168.1.3
port = 22
username = root
password = 123456

[g2]
ip = 192.168.1.4,192.168.1.5
port = 22
username = root
password = 123456

[g3]
ip = 192.168.1.6
port = 22
username = root
password = 123456




server.conf
1
2
3
4
5
6
7
8
9
10
11
12
###Default configuration###
[DEFAULT]
logfile = ../log/log.txt
hosts_file = ../conf/hosts.txt

###Configuration file of hosts###
[hosts_conf]
hosts_file = ../conf/hosts.txt

###Log file###
[log]
logfile = ../log/log.txt




                  


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-406593-1-1.html 上篇帖子: 使用python+txt构建测试数据 下篇帖子: python实现简单的百度搜索
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表