import os import sys import time import easygui import netmiko import encodings.idna from concurrent.futures import ThreadPoolExecutor def get_user_info(): """获取用户输入信息""" while True: dev_type = str() # 定义用户输入的字段 fields = ['设备厂商', '只读账号', '只读密码'] default_values = ['H3C', '123456', 'abcd123'] vendor, username, password = easygui.multpasswordbox('请输入用户信息', '', fields, default_values) # 验证用户的输入正确与否 if not all(input_value.strip() for input_value in (vendor, username, password)): easygui.msgbox("\n\n输入信息不能为空。", "error", "ok") continue # 判断设备厂商 if (vendor.strip().upper() == "H3C") or (vendor.strip() == "华三"): dev_type = "hp_comware" break elif (vendor.strip().upper() == "CISCO") or (vendor.strip() == "思科"): dev_type = "cisco_ios" break elif (vendor.strip().upper() == "HUAWEI") or (vendor.strip() == "华为"): dev_type = "huawei" break else: easygui.msgbox(f"\n\n无效的设备厂商:{vendor}", "Error", "OK") continue return dev_type, username, password def get_resource_path(relative_path): """实现外部资源路径的定位""" # 获取当前执行文件的绝对路径 base_path = os.path.realpath(sys.argv[0]) # 获取非临时缓存文件路径的上一级目录,即实际resource文件夹路径 base_dir = os.path.dirname(base_path) return os.path.join(base_dir, relative_path) # 返回resources绝对路径 def get_dev_ip_info(): """从本地文档中获取所有设备的管理地址信息""" ip_list = list() # 保存设备IP地址的列表 # 获取存放设备IP地址resource的路径 dev_ip_path = get_resource_path(os.path.join("resources", "devs_ip.yhw")) try: # 遍历设备管理地址,并保存到列表中 with open(dev_ip_path, mode="r", encoding="utf-8") as f: dev_ip_info = f.read() # 读取设备管理地址信息 for ip in dev_ip_info.split(): # 对读取到的管理地址进行格式清除 ip_list.append(ip) ip_list = list(set(ip_list)) # 去重 except Exception as e: print(f"发生错误:{e}") return ip_list def make_dir(): """创建一个文件夹,用来保存巡检数据""" # 获取当前日期 date_time = time.strftime("%Y.%m.%d", time.localtime()) # 判断当前目录下是否存在该文件目录,不存在则创建 if not os.path.exists(f"巡检数据_{date_time}"): os.makedirs(f"巡检数据_{date_time}") file_path = f"巡检数据_{date_time}" return file_path # 返回创建的文件夹相对路径 def append_to_exception_file(error_info, file_path): """将异常设备信息写入异常设备log文件中""" with open(os.path.join(file_path, "异常设备log.txt"), mode="a", encoding="utf-8") as f: f.write(error_info + "\n" + "*" * 60 + "\n") def create_connect_info(dev_type, ip_list, username, password): """整合设备厂商、用户信息、设备管理地址,满足netmiko连接设备所需的参数""" devs_ssh_list = list() # 用列表保存所有设备的SSH登录参数信息 # 用于保存单台设备的SSH登录信息的字典 for ip in ip_list: devs_dict = { "device_type": dev_type, "host": ip, "username": username, "password": password, "conn_timeout": 20, "timeout": 600, } # 将字典添加到列表中 devs_ssh_list.append(devs_dict) return devs_ssh_list def connect_dev(dev_info, file_path, normal_devs_list): """通过SSH连接到设备,读取巡检信息并保存到本地""" # 获取巡检命令文件绝对路径 comm_info_path = get_resource_path(os.path.join("resources", "com_info.yhw")) try: with netmiko.ConnectHandler(**dev_info) as conn_dev: # 获取设备名称 dev_name = conn_dev.send_command("display current-configuration | include sysname").strip().split(" ")[1] with open(os.path.join(file_path, dev_name + ".txt"), mode="w", encoding="utf-8") as dev_file: with open(comm_info_path, mode="r", encoding="utf-8") as command_info: for com_info in command_info: """ strip_command=False 表示不从返回的输出中移除发送的命令; strip_prompt=False 表示不从返回的输出中移除设备的提示符; read_timeout=300 表示读取超时为300秒 """ dev_file.write( conn_dev.send_command(com_info, strip_command=False, strip_prompt=False, read_timeout=300)) normal_devs_list.append(dev_info["host"]) print(f"设备{dev_name}命令配置收集完成") except netmiko.NetMikoAuthenticationException as e: print(f"设备{dev_info['host']}身份认证失败,请确认用户名和密码是否正确") append_to_exception_file(f"设备{dev_info['host']}身份认证失败,错误信息:\n{e}", file_path) except netmiko.NetMikoTimeoutException as e: print(f"设备{dev_info['host']}连接超时") append_to_exception_file(f"设备{dev_info['host']}连接超时,错误信息:\n{e}", file_path) except Exception as e: print(f"无法连接到设备{dev_info['host']}") append_to_exception_file(f"无法连接到设备{dev_info['host']},错误信息:\n{e}", file_path) def thread_pool_ssh(devs_ssh_list, file_path, normal_devs_list): """多线程执行SSH连接""" print("信息收集中......", "\n") with ThreadPoolExecutor(5) as pool: for dev_info in devs_ssh_list: pool.submit(connect_dev, dev_info, file_path, normal_devs_list) def main(): """主函数""" normal_devs_list = list() # 统计正常设备数量 dev_type, username, password = get_user_info() # 获取用户输入的设备厂商、用户名、密码 start_time = time.time() # 记录程序开始时间 ip_list = get_dev_ip_info() # 获取设备管理地址信息 file_path = make_dir() # 创建一个文件夹,用来保存巡检数据 # 整合设备厂商、用户信息、设备管理地址,满足netmiko连接设备所需的参数 devs_ssh_list = create_connect_info(dev_type, ip_list, username, password) thread_pool_ssh(devs_ssh_list, file_path, normal_devs_list) # 开启多线程,并发执行SSH连接 end_time = time.time() # 程序结束时间 used_time = int(end_time - start_time) # 耗时时间 # 提示退出系统 while 1: res_info = easygui.msgbox( f"设备巡检信息收集完成。\n\n\n本次巡检共涉及{len(ip_list)}台设备,正常收集{len(normal_devs_list)}台,存在连接异常设备{len(ip_list) - len(normal_devs_list)}台,共计用时{used_time}秒。\n\n\n异常设备信息请查看'异常设备log'文档,确认无误后可退出!", "收集完成提示信息", "退出") if res_info == '退出': break if __name__ == '__main__': print("华三设备display命令收集工具", "\n", "编辑:YHW", "\n", "联系方式:136****0032", sep="") print("*" * 40) main()
对华三设备日常巡检,使用python中的netmiko模块,通过线程池实现多线程执行。
于 2023-12-31 20:46:41 首次发布