作业需求:
1. 运行程序列出主机组或者主机列表
2. 选择指定主机或主机组
3. 选择让主机或者主机组执行命令或者向其传输文件(上传/下载)
4. 充分使用多线程或多进程
5. 不同主机的用户名密码、端口可以不同
思路解析:
1. 运用paramiko模块里的command和sftp方法执行命令传输文件
2. 指定主机和主机组,使用字典读取字典信息里的组和服务器信息,列出主机组和主机列表
3. 用threading模块控制paramiko模块里的command和sftp方法执行命令传输文件
4. 字典信息可以添加修改
程序核心代码
README
作者:yaobin 版本:简单Ftp示例版本 v0.1 开发环境: python3.6 程序介绍: 类 Fabric 主机管理程序开发: 1. 运行程序列出主机组或者主机列表 2. 选择指定主机或主机组 3. 选择让主机或者主机组执行命令或者向其传输文件(上传/下载) 4. 充分使用多线程或多进程 5. 不同主机的用户名密码、端口可以不同 使用说明: /bin/fortress_machine ├─bin │ fortress_machine.py #程序主目录 │ __init__.py │ ├─conf │ │ server_list │ │ setting.py │ │ __init__.py │ │ │ ├─core │ │ add_machine.py #添加server字典程序 │ │ basic_dict.py #初始化字典程序 │ │ fortress_main.py #交互程序 │ │ group_paramiko.py #组管理交互程序 │ │ group_sftp.py #组上传/下载交互程序 │ │ server_paramiko.py #server管理交互程序 │ │ server_sftp.py #server上传/下载交互程序 │ │ __init__.py │ │ │ └─__pycache__ ├─db │ ├─download #文件下载目录 │ └─upload #文件上传目录 │ AutoResponder.xml │ ├─logs │ __init__.py │ └─model │ prettytable.py PrettyTable模块 │ __init__.py │ ├─paramiko paramiko 模块 │ └─__pycache__ prettytable.cpython-36.pyc __init__.cpython-36.pyc
add_machine.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao '''添加主机和主机组类''' import os import sys import re import pickle base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import setting from model.prettytable import PrettyTable class add_machine_list(object): ''' add_machine类 ''' def __init__(self): ''' 构造函数 :param dict_file_object 字典文件 ''' with open(setting.server_list_path, 'rb') as dict_file_object: self.server_list_dict = pickle.load(dict_file_object) #server_list_dict服务器字典变量 dict_file_object.close() def add_group_object(self): ''' 添加新群组新群组server字典函数 :return: ''' self.view_server() self.group_server_name = input('请输入要添加的组名称>>>>>>>>:') # 组名key input 打印组内机器 if self.group_server_name in self.server_list_dict['machine']: print('%s组已经存在,请重新输入...' % self.group_server_name) self.add_group_object() else: self.add_new_group_server(self.group_server_name) self.group_server_object = {self.server_name: {'ip': self.server_name, 'port': self.server_port, 'username': self.server_username, 'passwd': self.server_passwd}} # 添加group self.server_list_dict['machine'][self.group_server_name] = self.group_server_object with open(setting.server_list_path, 'wb') as dict_file_object: dict_file_object.write(pickle.dumps(self.server_list_dict)) dict_file_object.close() self.view_server() def add_server_object(self): ''' 添加组内server字典函数 :return: ''' self.view_server() self.group_server_name = input('请输入要添加server的组>>>>>>>>:') if self.group_server_name in self.server_list_dict['machine']: self.add_server(self.group_server_name) server_object = {'ip': self.server_name, 'port': self.server_port, 'username': self.server_username, 'passwd': self.server_passwd} # 添加server self.server_list_dict['machine'][self.group_server_name][self.server_name] = server_object with open(setting.server_list_path, 'wb') as dict_file_object: dict_file_object.write(pickle.dumps(self.server_list_dict)) dict_file_object.close() print('Server add done please check') self.view_server() else: print('要添加server的组%s不存在请重新输入' % self.group_server_name) self.add_server_object() def add_server(self,group_server_name): ''' 添加组内server函数 :param:group_server_name: groupname :return: ''' self.view_server() try: self.server_name = input('请输入要添加的server ip>>>>>>>>:') # 添加server 机器 server_name_re_rr = re.compile("^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\." + "" # 判断ip正则 "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\." + "" "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\." + "" "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$") res = server_name_re_rr.findall(self.server_name) if res[0]: #如果有值判断是否正常 if self.server_name not in self.server_list_dict['machine'][self.group_server_name]: while True: try: self.server_port = input('请输入要添加的server port>>>>>>>>:') # 判断port 1-65535 if int(self.server_port) >= 22 and int(self.server_port) < 65535: self.server_username = input('请输入要添加的server username>>>>>>>>:') self.server_passwd = input('请输入要添加的server passwd>>>>>>>>:') break # 正常取完变量后才能结束 else: print('%s输入不正确,请输入22或22以上65535以下的数字' % self.server_port) except ValueError: # 如果出现valueerror print('%s输入不正确,请输入22或22以上65535以下的数字' % self.server_port) else: print('%s已经存在,请重新输入' % self.server_name) self.add_server(self.group_server_name) except IndexError as e: print('%s输入不正确,请输入正确的ip地址'%self.server_name) self.add_server(self.group_server_name) def add_new_group_server(self,group_server_name): ''' 添加新组server函数 :param group_server_name:要添加的组名 :return: ''' self.view_server() try: self.server_name = input('请输入要添加的server ip>>>>>>>>:') # 添加server 机器 server_name_re_rr = re.compile("^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\." + "" # 判断ip正则 "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\." + "" "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\." + "" "(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$") res = server_name_re_rr.findall(self.server_name) if res[0]: # 如果有值判断是否正常 while True: try: self.server_port = input('请输入要添加的server port>>>>>>>>:') # 判断port 1-65535 if int(self.server_port) >= 22 and int(self.server_port) < 65535: self.server_username = input('请输入要添加的server username>>>>>>>>:') self.server_passwd = input('请输入要添加的server passwd>>>>>>>>:') break # 正常取完变量后才能结束 else: print('%s输入不正确,请输入22或22以上65535以下的数字' % self.server_port) except ValueError: # 如果出现valueerror print('%s输入不正确,请输入22或22以上65535以下的数字' % self.server_port) except IndexError as e: print('%s输入不正确,请输入正确的ip地址' % self.server_name) self.add_server(self.group_server_name) self.view_server() def modifi_group(self,group,ip,port,username,passwd): ''' 修改server群组名和server群组内机器的方法,暂不做处理 :param group: :param ip: :param port: :param username: :param passwd: :return: ''' def view_server(self): ''' 查看server函数 :return: ''' view_ip = PrettyTable(['Group.', 'Ip', ]) for i in self.server_list_dict['machine']: for s in self.server_list_dict['machine'][i]: view_ip.add_row([i, s]) print('%s' % view_ip)
basic_dict.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao import os import sys import pickle from conf import setting base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) def basic_dict(): ''' 初始化程序字典 :return: ''' add_machine_dict = {'machine':{'group1':{'192.168.84.130': {'ip':'192.168.84.130','port':'12321','username':'root','passwd':'123456'}}}} with open(setting.server_list_path,'wb') as dict_file_object: dict_file_object.write(pickle.dumps(add_machine_dict)) dict_file_object.close()
fortress_main.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao '''主交互程序''' import os import sys base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from model.prettytable import PrettyTable from core import add_machine from core import group_paramiko from core import server_paramiko class fortress_machine(object): ''' 类Fabric主机管理程序 ''' def __init__(self): ''' 构造方法 ''' pass def cmd_choice(self): ''' 帮助函数 :return: ''' cmd_choice = PrettyTable(['No.', '说明']) cmd_choice.add_row(['1', '组管理']) cmd_choice.add_row(['2', '主机管理']) cmd_choice.add_row(['3', ' 添加server']) cmd_choice.add_row(['4', ' 添加group']) cmd_choice.add_row(['5', ' 查看server']) cmd_choice.add_row(['6', '退出程序']) print(cmd_choice) def user_choice(self): ''' 用户选择函数 :return: ''' while True: self.cmd_choice() user_choice = input('Please input your choice>>>>>>:') if user_choice == '1': print('Welcome ,The list of hosts is as follows ') group_paramiko.fortress_main().choice() if user_choice == '2': print('Welcome ,The list of hosts is as follows ') server_paramiko.fortress_main().choice() elif user_choice == '2': add_machine.add_machine_list().add_server_object() elif user_choice == '3': add_machine.add_machine_list().add_add_group_object() elif user_choice == '4' or user_choice == 'q' or user_choice == 'exit': add_machine.add_machine_list().view_server() elif user_choice == '5' or user_choice == 'q' or user_choice == 'exit': sys.exit('程序退出') else: print('disallow %s please input again'%user_choice)
group_paramiko.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao '''类Fabric主机管理交互程序''' import os import sys import pickle import threading base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import setting from core import add_machine from model import paramiko from core import group_sftp from model.prettytable import PrettyTable class fortress_main(object): '''fortress类''' def __init__(self): ''' 构造函数 ''' self.semaphore = threading.BoundedSemaphore(1) self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) with open(setting.server_list_path, 'rb') as dict_file_object: self.server_list_dict = pickle.load(dict_file_object) # server_list_dict服务器字典变量 dict_file_object.close() def choice(self): ''' 输入函数 :return: ''' add_machine.add_machine_list().view_server() self.group_input = input('Please input group name >>>>>>>>:') if self.group_input in self.server_list_dict['machine']: self.group_send_command() else: print('There is no group named %s ' % self.group_input) self.choice() return self.group_input def ssh_group_cmd(self,command): ''' 组管理函数 :return: ''' for s in self.server_list_dict['machine'][self.group_input]: self.server_ip = self.server_list_dict['machine'][self.group_input][s]['ip'] self.server_port = int(self.server_list_dict['machine'][self.group_input][s]['port']) self.server_username = self.server_list_dict['machine'][self.group_input][s]['username'] self.server_passwd = self.server_list_dict['machine'][self.group_input][s]['passwd'] self.ssh.connect(self.server_ip, self.server_port, self.server_username, self.server_passwd) self.t1 = threading.Thread(target=self.ssh_command(command)) self.t1.setDaemon(True) self.t1.start() def ssh_connect(self,ip,port,username,passwd): ''' 批量激活主机函数 :param ip: :param port: :param username: :param passwd: :return: ''' self.t = threading.Thread(target=self.ssh.connect,args=(ip, port, username,passwd))#开启线程 self.t.setDaemon(True) #设置守护线程 self.t.start() def ssh_command(self,command): ''' 发送ssh服务器命令 :param command: :return: ''' stdin, stdout, stderr = self.ssh.exec_command(command) res, err = stdout.read(), stderr.read() # 定义俩个变量 self.semaphore.acquire() result = res if res else err # 定义三元运算 print((self.server_ip).center(50,'-')) print(result.decode()) self.ssh.close() self.semaphore.release() def group_send_command(self): ''' group发送指令函数 :return: ''' while True: command_input = input('please input command,input help for help >>>>>>>>:').strip() # 这里要调用命令 if len(command_input) == 0: pass elif len(command_input.split()) == 1: if command_input == 'help': self.help_page() elif command_input == 'exit': self.ssh.close() exit() else: self.ssh_group_cmd(command_input) elif len(command_input.split()) >= 2: # 命令分割 cmd_split = command_input.split()[0] self.fileobject = command_input.split()[1] # 获取文件路径 self.filename = self.fileobject.split('/')[-1] # 获取文件名 if cmd_split == 'get': #未处理 group_sftp.group_ftp(self.group_input,self.fileobject,self.filename).download() elif cmd_split == 'put':#未处理 group_sftp.group_ftp(self.group_input,self.fileobject,self.filename).upload() elif cmd_split == 'help': self.help_page() else: self.ssh_group_cmd(command_input) else: print('unsupported command') self.help_page() def help_page(self): ''' help页面 :return: ''' help_page = PrettyTable(['Command.', 'Explain', ]) help_page.add_row(['get filename', ' download file']) help_page.add_row(['put /path/filename', 'upload file']) help_page.add_row(['other', 'send command']) help_page.add_row(['exit ', 'exit program']) help_page.add_row(['help ', '']) print('%s' % help_page)
group_sftp.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao import os import sys import pickle import platform import threading import time,random base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import setting from model import paramiko class group_ftp(object): ''' serverftp类 ''' def __init__(self,group_input,fileobject,filename): ''' 构造函数 :param group_input: :param server_input: :param fileobject: ''' self.group_input = group_input self.fileobject = fileobject self.filename = filename with open(setting.server_list_path, 'rb') as dict_file_object: self.server_list_dict = pickle.load(dict_file_object) # server_list_dict服务器字典变量 dict_file_object.close() def upload(self): ''' 上传函数 :param command: :return: ''' try: os.chdir(setting.upload_path) for s in self.server_list_dict['machine'][self.group_input]: self.server_ip = self.server_list_dict['machine'][self.group_input][s]['ip'] self.server_port = int(self.server_list_dict['machine'][self.group_input][s]['port']) self.server_username = self.server_list_dict['machine'][self.group_input][s]['username'] self.server_passwd = self.server_list_dict['machine'][self.group_input][s]['passwd'] self.transport = paramiko.Transport((self.server_ip, self.server_port)) self.transport.connect(username=self.server_username, password=self.server_passwd) self.sftp = paramiko.SFTPClient.from_transport(self.transport) t = threading.Thread(target=self.sftp.put, args=(self.filename, self.fileobject)) t.start() except FileNotFoundError as e: print('system error ', e) def download(self): ''' 下载函数 :param :return: ''' os.chdir(setting.download_path) for s in self.server_list_dict['machine'][self.group_input]: self.server_ip = self.server_list_dict['machine'][self.group_input][s]['ip'] self.server_port = int(self.server_list_dict['machine'][self.group_input][s]['port']) self.server_username = self.server_list_dict['machine'][self.group_input][s]['username'] self.server_passwd = self.server_list_dict['machine'][self.group_input][s]['passwd'] self.transport = paramiko.Transport((self.server_ip, self.server_port)) self.transport.connect(username=self.server_username, password=self.server_passwd) self.sftp = paramiko.SFTPClient.from_transport(self.transport) try: if platform.system() == 'Windows': #self.sftp.get(self.fileobject, (setting.download_path + '\' + self.filename)) t = threading.Thread(target=self.sftp.get,args=(self.fileobject, (setting.download_path + '\' + self.filename))) t.start() #time.sleep(10) else: #self.sftp.get(self.fileobject, (setting.download_path + '/' + self.filename)) t = threading.Thread(target=self.sftp.get, args=(self.fileobject, (setting.download_path + '\' + self.filename))) t.start() if os.path.exists(setting.download_path + '\' + self.filename): print('file download complete') else: print('file download error') except FileNotFoundError as e: print('system error ', e) os.remove(self.filename)
server_paramiko.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao '''类Fabric主机管理交互程序''' import os import sys import pickle import threading base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import setting from core import add_machine from core import server_sftp from model import paramiko from model.prettytable import PrettyTable class fortress_main(object): '''fortress类''' def __init__(self): ''' 构造函数 ''' self.semaphore = threading.BoundedSemaphore(1) self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) with open(setting.server_list_path, 'rb') as dict_file_object: self.server_list_dict = pickle.load(dict_file_object) # server_list_dict服务器字典变量 dict_file_object.close() def choice(self): ''' 模式选择函数 :return: ''' add_machine.add_machine_list().view_server() self.group_input = input('Please input group name >>>>>>>>:') self.server_input = input('Please input the server name you want to manage >>>>>>>>:') if self.group_input in self.server_list_dict['machine']: if self.server_input in self.server_list_dict['machine'][self.group_input]: self.server_send_command() else: print('There is no server named %s please input again' % self.server_input) self.choice() else: print('There is no group named %s please input again' % self.group_input) self.choice() return self.group_input,self.server_input def ssh_server_cmd(self,command): ''' server管理函数 :return: ''' self.server_ip = self.server_list_dict['machine'][self.group_input][self.server_input]['ip'] self.server_port = int(self.server_list_dict['machine'][self.group_input][self.server_input]['port']) self.server_username = self.server_list_dict['machine'][self.group_input][self.server_input]['username'] self.server_passwd = self.server_list_dict['machine'][self.group_input][self.server_input]['passwd'] self.ssh.connect(self.server_ip, self.server_port, self.server_username, self.server_passwd) self.t1 = threading.Thread(target=self.ssh_command(command)) self.t1.setDaemon(True) self.t1.start() print('thread name is %s' % self.t1.getName()) def ssh_connect(self,ip,port,username,passwd): ''' 批量激活主机函数 :param ip: :param port: :param username: :param passwd: :return: ''' self.t = threading.Thread(target=self.ssh.connect,args=(ip, port, username,passwd))#开启线程 self.t.setDaemon(True) #设置守护线程 self.t.start() def ssh_command(self,command): ''' 发送ssh服务器命令 :param command: :return: ''' stdin, stdout, stderr = self.ssh.exec_command(command) res, err = stdout.read(), stderr.read() # 定义俩个变量 self.semaphore.acquire() result = res if res else err # 定义三元运算 print((self.server_ip).center(50,'-')) print(result.decode()) self.ssh.close() self.semaphore.release() def server_send_command(self): ''' server发送指令函数 :return: ''' while True: command_input = input('please input command,input help for help >>>>>>>>:').strip() # 这里要调用命令 if len(command_input) == 0: pass elif len(command_input.split()) == 1: if command_input == 'help': self.help_page() elif command_input == 'exit': self.ssh.close() exit() else: self.ssh_server_cmd(command_input) elif len(command_input.split()) >= 2: # 命令分割 cmd_split = command_input.split()[0] self.fileobject = command_input.split()[1] # 获取文件路径 self.filename = self.fileobject.split('/')[-1] # 获取文件名 print(self.fileobject) print(self.filename) if cmd_split == 'get': server_sftp.server_ftp(self.group_input,self.server_input,self.fileobject, self.filename).download() elif cmd_split == 'put': server_sftp.server_ftp(self.group_input,self.server_input,self.fileobject, self.filename).upload() elif cmd_split == 'help': self.help_page() else: self.ssh_server_cmd(command_input) else: print('unsupported command') self.help_page() def help_page(self): ''' help页面 :return: ''' help_page = PrettyTable(['Command.', 'Explain', ]) help_page.add_row(['get filename', ' download file']) help_page.add_row(['put /path/filename', 'upload file']) help_page.add_row(['other', 'send command']) help_page.add_row(['exit ', 'exit program']) help_page.add_row(['help ', '']) print('%s' % help_page)
server_sftp.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao import os import sys import pickle, json import platform import threading base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) from conf import setting from model import paramiko class server_ftp(object): ''' serverftp类 ''' def __init__(self, group_input,server_input,fileobject,filename): ''' 构造函数 :param group_input: :param server_input: :param fileobject: ''' self.group_input = group_input self.server_input = server_input self.fileobject = fileobject self.filename = filename with open(setting.server_list_path, 'rb') as dict_file_object: self.server_list_dict = pickle.load(dict_file_object) # server_list_dict服务器字典变量 dict_file_object.close() if self.group_input in self.server_list_dict['machine']: if self.server_input in self.server_list_dict['machine'][self.group_input]: self.server_ip = self.server_list_dict['machine'][self.group_input][self.server_input]['ip'] self.server_port = int(self.server_list_dict['machine'][self.group_input][self.server_input]['port']) self.server_username = self.server_list_dict['machine'][self.group_input][self.server_input]['username'] self.server_passwd = self.server_list_dict['machine'][self.group_input][self.server_input]['passwd'] self.transport = paramiko.Transport((self.server_ip, self.server_port)) self.transport.connect(username=self.server_username, password=self.server_passwd) self.sftp = paramiko.SFTPClient.from_transport(self.transport) else: pass else: print('not allow') def upload(self): ''' 上传函数 :param command: :return: ''' try: os.chdir(setting.upload_path) self.sftp.put(self.filename,self.fileobject) except FileNotFoundError as e: print('system error ', e) def download(self): ''' 下载函数 :param :return: ''' os.chdir(setting.download_path) try: if platform.system() == 'Windows': self.sftp.get(self.fileobject, (setting.download_path + '\' + self.filename)) else: self.sftp.get(self.fileobject, (setting.download_path + '/' + self.filename)) if os.path.exists(setting.download_path + '\' + self.filename): print('file download complete') else: print('file download error') except FileNotFoundError as e: print('system error ', e) os.remove(self.filename)
setting.py
#!/usr/bin/env python # -*- coding:utf-8 -*- # Author: Colin Yao '''配置文件''' import os import sys import platform base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) sys.path.append(base_dir) if platform.system() == 'Windows': server_list_path = os.path.dirname(os.path.abspath(__file__))+'\'+'server_list'#主机列表字典路径变量 server_dict_path = os.path.dirname(os.path.abspath(__file__)) + '\' + 'server_dict' download_path = (base_dir+'\'+'dbdownload') #下载路径变量 upload_path = (base_dir + '\' + 'db\upload') #上传路径变量 else: server_list_path = os.path.dirname(os.path.abspath(__file__)) + '/' + 'server_list' server_dict_path = os.path.dirname(os.path.abspath(__file__)) + '/' + 'server_dict' download_path = (base_dir + '/' + 'dbdownload') upload_path = (base_dir + '/' + 'db\upload')
程序测试样图