• 老男孩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 = '''33[33;1m
                                                请确认录入信息:
    %s:
                        友情提示:请务必确保信息填写无误否则将无法正常进行管理登录操作!!!
                33[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('33[32;1m信息载入完成33[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('33[32;1m当前已存在主机名列表如下:33[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('33[31;1m输入超出索引范围33[0m')
                    else:
                        print('33[31;1m当前主机索引不能在删除33[0m')
                else:
                    print('33[31;1m请输入有效索引33[0m')
            else:
                print('33[31;1m当前已存在主机名列表为空请先创建主机33[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 = """33[32;1m
                        警告!程序准备开启多线程模式激活主机,请确保:
                        1,远程服务器处于开启状态
                        2,DNS或本地hosts映射能够解析远程服务器主机名
                33[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("33[33;1m是否开始确认激活主机(y/n)?:33[0m")
                    if choose == 'n':
                        print('33[31;1m已取消激活33[0m')
                        return
                    elif choose == 'y':
                        for item in dic_list:
                            #print(item,dic_list)
                            print('33[38;0m程序开始激活主机请稍后...33[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('33[31;1m输入有误请重新输入33[0m')
                        continue
                else:
                    print('33[31;1m当前已激活主机为空请先创建主机33[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('33[31;1m主机: %s 连接尝试失败:%s33[0m'%(dic['username'],e))
                dic['status'] = 2
                self.update_dic(dic)
            else:
                stdin, stdou, stdeer = ssh.exec_command('who')
                result = stdou.read()
                print('33[32;1m主机: %s 连接尝试成功:33[0m
    %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("33[38;1m请输入你想控制的主机索引(可多选,输入n=返回上级):33[0m").strip()
                        if choose == 'n':
                            print('33[31;1m取消控制主机33[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("33[31;1m有一个索引超出范围33[0m")
                                    choose_list.clear()
                                    return
                        else:
                            print('33[31;1m请输入有效索引33[0m')
                            return
                    else:
                        print('33[31;1m当前已存在主机名列表为空请先激活主机33[0m')
    
                    self.action(choose_list)
                    return
            else:
                print('33[31;1m当前已激活主机为空请先创建主机33[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 = '''33[33;1m
                                help帮助提示:
                        1.程序的Home目录是本地文件目录
                        2,输入put向远程主机上传文件
                        3,输入get向远程主机下载文件
                        4,输入其他直接向远程主机发布命令
            '''
            while True:
                print(mesg)
                if len(choose_list) != 0:
                    print('33[32;1m您正在操控%s台主机,如下:33[0m'%(len(choose_list)))
                    for index,info in enumerate(choose_list):
                        print('%s-主机名:%s'% (index+1, info['hostname']))
                        command = input("33[38;1m请输入你想执行的命令(输入n=返回上级,输入任意键进入cmd模式)>>:33[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('33[37;1m请输入需上传文件名(例:xxx.txt)输入n返回上级菜单>>>:33[0m')
                    if filename == 'n':
                        print('33[31;1m取消上传文件33[0m')
                        return
                    file_path = os.path.join(home_path,filename)
                    if os.path.isfile(file_path):
                        remote = input("33[37;1m你想将文件保存到远程服务器路径(例如:/etc/)>>>:33[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('33[31;1m文件没有找到,请重新输入!33[0m')
                        continue
                else:
                    print('33[31;1m未找到指定用户目录33[0m')
                    time.sleep(1)
                    print('33[31;1m正在创建请稍后...33[0m')
                    time.sleep(1)
                    os.mkdir(home_path)
                    print('33[32;1m用户目录创建成功!!!33[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("33[37;1m请输入想要下载的远程服务器文件绝对路径(例如:/etc/hosts/):33[0m")
                remote_file = input("33[37;1m请输入想要下载的远程服务器文件名(例如:xxx.txt):33[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('33[31;1m未找到指定用户目录33[0m')
                time.sleep(1)
                print('33[31;1m正在创建请稍后...33[0m')
                time.sleep(1)
                os.mkdir(home_path)
                print('33[32;1m用户目录创建成功!!!33[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('33[31;1m主机名:[%s]文件上传失败...失败原因:
    %s33[0m'%(dic['hostname'],e))
            else:
                print('33[32;1m主机名:[%s]文件上传成功33[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('33[31;1m主机名:[%s]文件下载失败...失败原因:
    %s33[0m' % (dic['hostname'], e))
            else:
                print('33[32;1m主机名:[%s]文件下载成功33[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('33[31;1m主机: %s 连接尝试失败:%s33[0m' % (dic['hostname'], e))
            else:
                while True:
                    command = input("33[38;1m请输入你想执行的命令(输入n=返回上级)>>:33[0m")
                    if command == 'n':
                        return
                    else:
                        stdin, stdou, stdeer = ssh.exec_command(command)
                        erro = stdeer.read()
                        output =stdou.read()
                        if len(erro) !=0:
                            print("33[31;1m主机:%s 执行%s命令时出错:%s33[0m"%(dic['hostname'],command,(erro.decode())))
                            #return False
                        else:
                            if len(output.decode()) == 0:
                                print('该命令无返回结果')
                            else:
                                print("33[32;1m主机:%s 执行[%s]命令结果如下:33[0m
    %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
  • 相关阅读:
    GC选择之CMS 并发标记清除
    JVM内存概览与GC初步
    Shell 判断语句
    SUID SGID
    maven package
    ACL权限控制列表
    账户与密码管理
    Ubuntu与Centos在登陆安全方面的比较
    【PL/SQL Developer】动态执行表不可访问,本会话的自动统计被禁止
    【Centos7】Delete virtual bridge
  • 原文地址:https://www.cnblogs.com/catepython/p/8872274.html
Copyright © 2020-2023  润新知