題目:簡單主機批量管理工具
需求:
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
#!/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示例,可自行修改
[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
###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