老男孩Day10作业:主机管理程序

一、作业需求:

1, 运行程序列出主机组或者主机列表

2,选择指定主机或主机组

3,选择主机或主机组传送文件(上传/下载)

4,充分使用多线程或多进程

5,不同主机的用户名,密码,端口可以不同

6,可向主机或主机组批量发布命令

7,可一次性执行多条操作命令

 

二、

 

一、作业需求:

1, 运行程序列出主机组或者主机列表(已完成)

2,选择指定主机或主机组(已完成)

3,选择主机或主机组传送文件(上传/下载)(已完成)

4,充分使用多线程或多进程(已完成)

5,不同主机的用户名,密码,端口可以不同(已完成)

6,可向主机或主机组批量发布命令(已完成)

7,可一次性执行多条操作命令(已完成)

二、博客地址:http://www.cnblogs.com/catepython/p/8872274.html

三、运行环境

操作系统:Win10

Python:3.6.4rcl

Pycharm:2017.3.4

四、功能实现

1)实现所有基本需求

2)充分利用了面向对象式编程


五、测试

1)文件名为空判断

2)用户信息判断

3)指令格式化判断

4)上传/下载到指定路径判断 

5)文件名/用户目录有效判断


六、备注
readme

 

三、流程图

 

四、目录架构

 

五、核心代码

 bin目录--程序开始

#-*-coding:utf-8 -*-
# Author: D.Gray
from core import main
start = main.MyFabric()
start.run()
start

conf目录

#-*-coding:utf-8 -*-
# Author: D.Gray
import os
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
#print(BASE_DIR)

HOST_NAME_PATH = os.path.join(BASE_DIR,'db')
#print(HOST_NAME_PATH)

HOME_PATH = os.path.join(BASE_DIR,'home')
setting

core目录

#-*-coding:utf-8 -*-
# Author: D.Gray
import os,sys,paramiko,threading,time,json
from conf import setting
import re
class MyFabric(object):
    def __init__(self):
        self.run = self.run()

    def run(self):
        '''
        主界面运行函数
        :return:
        '''
        text = """
                    欢迎来到Fabric主机管理界面
                        1.创建主机
                        2.删除主机
                        3.自动激活所有主机
                        4.开始远程操控
                        5.退出程序
            """
        while True:
            print(text)
            choose = input("请输入您的选择>>>:").strip()
            #print(type(choose))
            self.dic = {
                '1':self.new_host,      #创建主机模块
                '2':self.delect_host,   #删除主机模块
                '3':self.auto_host,     #激活主机模块
                '4':self.action_host,   #控制主机模块
                '5':self.exit
            }
            if choose in self.dic:
                self.dic[choose]()
            else:
                print("请输入有效操作")

    def new_host(self):
        '''
        创建主机模块函数
        :return:
        '''
        #print('in the new_host')
        db_path = setting.HOST_NAME_PATH
        while True:
            name = input('请输入登录名称(输入n=返回上级)>>>:').strip()
            name_path = os.path.join(db_path,'%s.json'%(name))
            #print(name_path)
            if os.path.exists(name_path):
                print('登录名称已存在')
                continue
            if name == 'n':
                return
            hostname = input('请输入主机名(输入n=返回上级)>>>:').strip()
            if hostname == 'n':
                return
            port = input('请输入端口号(输入n=返回上级)>>>:').strip()
            if port.isdigit():
                port = int(port)
            else:
                print('端口号必须是整数')
                return
            if port == 'n':
                return
            username = input('请输入用户名(输入n=返回上级)>>>:').strip()
            if username == 'n':
                return
            password = input('请输入密码(输入n=返回上级)>>>:').strip()
            if password == 'n':
                return
            newhost_dic = {
                "name":name,    #用户文件名(主机链接名称)
                "hostname":hostname,
                "username":username,
                "port":port,
                "password":password,
                "status": 0,  #0---未激活,1---已激活,2--激活失败

            }
            mesag = '''\033[33;1m
                                            请确认录入信息:\n%s:
                    友情提示:请务必确保信息填写无误否则将无法正常进行管理登录操作!!!
            \033[0m'''%newhost_dic
            print(mesag)
            choose = input("开人确认录入信息(y/n)?:")
            if choose == 'n':
                print('信息确认失败将重新录入:')
                return
            elif choose == 'y':
                if os.path.isdir(name_path) == False: #判断用户文件是否已存在
                    with open(name_path,'w') as fw:
                        json.dump(newhost_dic,fw,indent=4)  #使用json.dump()函数将用户信息格式化写入
                    print('\033[32;1m信息载入完成\033[0m')
                    break
                else:
                    print('已存在改文件')
            else:
                print('输入有误请重新输入')


    def delect_host(self):
        '''
        删除主机模块函数
        :return:
        '''
        #print('in the delect_host')
        host_dic = self.add_host()  #接收add_host()函数  {文件名:{用户字典信息}} 格式的返回值
        host_list = []
        print('\033[32;1m当前已存在主机名列表如下:\033[0m')
        for index,values in enumerate(host_dic.values()):   #遍历用户字典信息
            host_list.append(values['hostname'])
            print('%s-主机名:%s' % (index + 1, values['hostname']))
        if len(host_list) != 0:
            choose = input("请输入你想删除的主机索引(输入n=返回上级):")
            if choose =='n':
                print('取消删除主机名')
                return
            if choose.isdigit():
                choose = int(choose)
                if len(host_list) != 0:
                    if choose <= len(host_list):
                        # host_name(文件名称)
                        #  list(host_dic.keys())[choose-1](用户输入索引对应的主机名,也就是key)
                        host_name = list(host_dic.keys())[choose-1]
                        db_path = os.path.join(setting.HOST_NAME_PATH, host_name)
                        os.remove(db_path)
                        print('删除成功')
                    else:
                        print('\033[31;1m输入超出索引范围\033[0m')
                else:
                    print('\033[31;1m当前主机索引不能在删除\033[0m')
            else:
                print('\033[31;1m请输入有效索引\033[0m')
        else:
            print('\033[31;1m当前已存在主机名列表为空请先创建主机\033[0m')


    def add_host(self):
        '''
        获取db文件夹中json文件中的主机名
        :return:将已获取到的 {文件名:{用户字典信息}} 格式做为返回值
        '''
        names_list = [] #定义一个文件名列表
        dic_list = []  #定义一个文件字典列表
        db_dic = {} #定义一个db文件内容字典
        db_path = setting.HOST_NAME_PATH
        ol = os.listdir(db_path)
        for i in ol:
            if i.endswith('.json'): #只获取后缀名为.json文件的信息
                json_path = os.path.join(db_path,i)
                with open(json_path,'r') as f:
                    fd = json.load(f)
                    dic_list.append(fd)
                    names_list.append(i)
        db_dic = dict(zip(names_list,dic_list))

        return db_dic


    def auto_host(self):
        '''
        激活所有主机模块(尝试主机连接)
        :return:
        '''
        #print('in the auto_host')
        text = """\033[32;1m
                    警告!程序准备开启多线程模式激活主机,请确保:
                    1,远程服务器处于开启状态
                    2,DNS或本地hosts映射能够解析远程服务器主机名
            \033[0m"""
        while True:
            print(text)
            host_dic = self.add_host()
            host_list = []
            dic_list = []
            for index,values in enumerate(host_dic.values()):
                host_list.append(values['hostname'])
                dic_list.append(values)
                print('当前已存在主机名列表如下:')
                print('%s-主机名:%s' % (index + 1,values['hostname']))
            if len(host_list) != 0:
                choose = input("\033[33;1m是否开始确认激活主机(y/n)?:\033[0m")
                if choose == 'n':
                    print('\033[31;1m已取消激活\033[0m')
                    return
                elif choose == 'y':
                    for item in dic_list:
                        #print(item,dic_list)
                        print('\033[38;0m程序开始激活主机请稍后...\033[0m')
                        time.sleep(1)
                        dic = item
                        t = threading.Thread(target=self.auto_action,args=(dic,)) #调用激活执行模块
                        t.setDaemon(True)
                        t.start()
                        while threading.active_count() != 1:
                            pass
                    print('当前活跃线程个数:',threading.activeCount())
                    break
                else:
                    print('\033[31;1m输入有误请重新输入\033[0m')
                    continue
            else:
                print('\033[31;1m当前已激活主机为空请先创建主机\033[0m')
                return


    def auto_action(self,dic):
        '''
        激活执行模块函数
        :param dic:
        :return:
        '''
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            ssh.connect(hostname=dic['hostname'],port=dic['port'],username=dic['username'],password=dic['password'])
        except Exception as e:
            print('\033[31;1m主机: %s 连接尝试失败:%s\033[0m'%(dic['username'],e))
            dic['status'] = 2
            self.update_dic(dic)
        else:
            stdin, stdou, stdeer = ssh.exec_command('who')
            result = stdou.read()
            print('\033[32;1m主机: %s 连接尝试成功:\033[0m\n%s'%(dic['username'],result.decode()))
            dic['status'] = 1
            self.update_dic(dic)    #通过json.dump方法修改json文件中某一参数
        finally:
            ssh.close()

    def update_dic(self,dic):
        '''
        更改主机状态函数
        :param dic:
        :return:
        '''
        db_path = os.path.join(setting.HOST_NAME_PATH,'%s.json'%(dic['name']))
        with open(db_path,'w') as f:
            json.dump(dic,f,indent=4)  #通过json.dump方法修改json文件中某一参数
            return True


    def action_host(self):
        '''
        从已激活主机中选择相应的主机来进行操控
        1、遍历出已激活主机列表
        2、从激活主机列表中选择相应的主机进行操控(可多选)
        3、将选择操控的主机添加至choose_lsit列表中并传参给action()函数
        :return:
        '''
        print('in the action_host')
        auto_dic = self.add_host()
        auto_list = []  #已激活主机列表
        choose_list = []  # 用户选择操控主机列表
        for info in auto_dic.values():
            if info['status'] == 1:
                auto_list.append(info)
        if len(auto_list) != 0:
            while True:
                for index,values in enumerate(auto_list):
                    print('%s-主机名:%s 已激活'%(index+1,values['hostname']))
                if len(auto_list) != 0:
                    choose = input("\033[38;1m请输入你想控制的主机索引(可多选,输入n=返回上级):\033[0m").strip()
                    if choose == 'n':
                        print('\033[31;1m取消控制主机\033[0m')
                        return
                    if choose.isdigit():  #限制索引只能是数字
                        chooses = list(set(choose))     #去重多选
                        for index in chooses:
                            index = int(index)
                            if index <= len(auto_list) and index !=0:     #限制输入的索引在有效范围内
                                choose_list.append(auto_list[index-1])
                            else:
                                print("\033[31;1m有一个索引超出范围\033[0m")
                                choose_list.clear()
                                return
                    else:
                        print('\033[31;1m请输入有效索引\033[0m')
                        return
                else:
                    print('\033[31;1m当前已存在主机名列表为空请先激活主机\033[0m')

                self.action(choose_list)
                return
        else:
            print('\033[31;1m当前已激活主机为空请先创建主机\033[0m')
            return

    def action(self,choose_list):
        '''
        1、接收action_host()函数传来的 已操控主机列表([{用户1字典信息},{用户2字典信息}])格式
        2、遍历用户字典信息
        3、判断用户选择操作情况, 如put---进入put上传文件函数、get---进入get下载文件函数、其他进入cmd命令行操作函数

        :param choose_list:
        :return:
        '''
        #print('in the action:',choose_list)
        mesg = '''\033[33;1m
                            help帮助提示:
                    1.程序的Home目录是本地文件目录
                    2,输入put向远程主机上传文件
                    3,输入get向远程主机下载文件
                    4,输入其他直接向远程主机发布命令
        '''
        while True:
            print(mesg)
            if len(choose_list) != 0:
                print('\033[32;1m您正在操控%s台主机,如下:\033[0m'%(len(choose_list)))
                for index,info in enumerate(choose_list):
                    print('%s-主机名:%s'% (index+1, info['hostname']))
                    command = input("\033[38;1m请输入你想执行的命令(输入n=返回上级,输入任意键进入cmd模式)>>:\033[0m")
                    dic = info
                    if command == 'n':
                        return
                    if hasattr(self,command):
                        getattrs = getattr(self,command)
                        getattrs(dic)
                    else:
                        print('准备向远程主机发布命令...请稍后')
                        t = threading.Thread(target=self.execute_command,args=(dic,))
                        t.setDaemon(True)
                        t.start()
                    while threading.active_count() != 1:
                        pass
                print('当前活跃线程个数:', threading.activeCount())
                break

    def put(self,dic):
        '''
        上传文件函数
        1、判断home目录中是否存在用户文件夹
        2、输入需上传的文件名
        3、输入所需上传到服务器的地址路径
        4、调用put_action()函数开始上传文件
        :param dic:
        :return:
        '''
        print('准备上传文件!!!')
        home_path = os.path.join(setting.HOME_PATH,dic['name'])
        while True:
            if os.path.exists(home_path):
                filename = input('\033[37;1m请输入需上传文件名(例:xxx.txt)输入n返回上级菜单>>>:\033[0m')
                if filename == 'n':
                    print('\033[31;1m取消上传文件\033[0m')
                    return
                file_path = os.path.join(home_path,filename)
                if os.path.isfile(file_path):
                    remote = input("\033[37;1m你想将文件保存到远程服务器路径(例如:/etc/)>>>:\033[0m")
                    remote_path = os.path.join('%s/'%remote,filename)
                    t = threading.Thread(target=self.put_action,args=(dic,file_path,remote_path,))
                    t.setDaemon(True)
                    print('程序准备开始上传文件!!!')
                    time.sleep(1)
                    t.start()
                    while threading.activeCount() != 1:
                        pass
                    return
                    # print('当前活跃线程个数:', threading.activeCount())
                else:
                    print('\033[31;1m文件没有找到,请重新输入!\033[0m')
                    continue
            else:
                print('\033[31;1m未找到指定用户目录\033[0m')
                time.sleep(1)
                print('\033[31;1m正在创建请稍后...\033[0m')
                time.sleep(1)
                os.mkdir(home_path)
                print('\033[32;1m用户目录创建成功!!!\033[0m')
                return


    def get(self,dic):
        '''
        下载文件
        1、判断home目录中是否存在用户文件夹
        2、输入服务端地址路径
        3、输入下载的文件名
        4、调用get_action()函数开始下载文件
        :param dic:接收action函数传来的用户字典信息
        :return:
        '''
        print('准备下载文件!!!')
        home_path = os.path.join(setting.HOME_PATH, dic['name']) #用户目录路径
        if os.path.exists(home_path):
            remote = input("\033[37;1m请输入想要下载的远程服务器文件绝对路径(例如:/etc/hosts/):\033[0m")
            remote_file = input("\033[37;1m请输入想要下载的远程服务器文件名(例如:xxx.txt):\033[0m")
            remote_path = os.path.join('%s/'%remote,remote_file) #拼接服务端路径
            t = threading.Thread(target=self.get_action,args=(dic,home_path,remote_path,remote_file,))
            t.setDaemon(True)
            t.start()
            while threading.activeCount() != 1:
                pass
            return
        else:
            print('\033[31;1m未找到指定用户目录\033[0m')
            time.sleep(1)
            print('\033[31;1m正在创建请稍后...\033[0m')
            time.sleep(1)
            os.mkdir(home_path)
            print('\033[32;1m用户目录创建成功!!!\033[0m')
            return

    def put_action(self,*args):
        '''
        上传文件执行函数
        :param args:
        :return:
        '''
        #print('in the put_action:',*args)
        dic = args[0]   #主机用户字典信息
        file_path = args[1]     #本地用户目录路径
        remote_path = args[2]       #服务端路径
        transport = paramiko.Transport((dic['hostname'], int(dic['port'])))
        try:
            transport.connect(username=dic['username'],password=dic['password'])
            sftp = paramiko.SFTPClient.from_transport(transport)
            sftp.put(file_path,remote_path)
        except Exception as e:
            print('\033[31;1m主机名:[%s]文件上传失败...失败原因:\n%s\033[0m'%(dic['hostname'],e))
        else:
            print('\033[32;1m主机名:[%s]文件上传成功\033[0m' % (dic['hostname']))

        finally:
            transport.close()

    def get_action(self,*args):
        '''
        下载文件执行函数
        :param args:
        :return:
        '''
        #print('in the get_action:',*args)
        dic = args[0]   #主机信息字典
        home_path = args[1] #本地目录路径
        remote_path = args[2]   #服务器路径
        remote_file = args[3]   #从服务器下载的文件名
        file_path = os.path.join(home_path,remote_file)  #下载倒本地目录路径
        transport = paramiko.Transport((dic['hostname'],int(dic['port'])))
        try:
            transport.connect(username=dic['username'],password=dic['password'])
            sftp = paramiko.SFTPClient.from_transport(transport)
            sftp.get(remote_path,file_path)
        except Exception as e:
            print('\033[31;1m主机名:[%s]文件下载失败...失败原因:\n%s\033[0m' % (dic['hostname'], e))
        else:
            print('\033[32;1m主机名:[%s]文件下载成功\033[0m' % (dic['hostname']))
        finally:
            transport.close()


    def execute_command(self,dic):
        '''
        cmd模式
        :param dic:
        :return:
        '''
        #print('in the execute_command:%s '% (dic))
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy)
        try:
            ssh.connect(hostname=dic['hostname'],port=dic['port'],username=dic['username'],password=dic['password'])
        except Exception as e:
            print('\033[31;1m主机: %s 连接尝试失败:%s\033[0m' % (dic['hostname'], e))
        else:
            while True:
                command = input("\033[38;1m请输入你想执行的命令(输入n=返回上级)>>:\033[0m")
                if command == 'n':
                    return
                else:
                    stdin, stdou, stdeer = ssh.exec_command(command)
                    erro = stdeer.read()
                    output =stdou.read()
                    if len(erro) !=0:
                        print("\033[31;1m主机:%s 执行%s命令时出错:%s\033[0m"%(dic['hostname'],command,(erro.decode())))
                        #return False
                    else:
                        if len(output.decode()) == 0:
                            print('该命令无返回结果')
                        else:
                            print("\033[32;1m主机:%s 执行[%s]命令结果如下:\033[0m\n%s"
                                  % (dic['hostname'], command, (output.decode())))
        finally:
            ssh.close()

    def exit(self):
        #print('in the exit')
        exit('程序退出')
main

db目录

{
    "name": "admin_kyo",
    "hostname": "192.168.111.128",
    "username": "admin_kyo",
    "port": 22,
    "password": "admin1988",
    "status": 1
}
admin_kyo.json

 

posted @ 2018-04-18 10:03  空s蝉灬  阅读(556)  评论(0编辑  收藏  举报