南昌网站建设技术托管,郑州市金水区建设局网站,做会议活动的网站,湖南长沙大学自己为了便利写出来的基于python netmiko去ssh备份网络设备配置#xff0c;用过secureCRT的脚本去备份设备配置#xff0c;但是它没有图形化界面#xff0c;使用不方便#xff0c;自己就重新用python开发了一个#xff0c;同时用pyinstaller打包成可执行程序#xff08;这…自己为了便利写出来的基于python netmiko去ssh备份网络设备配置用过secureCRT的脚本去备份设备配置但是它没有图形化界面使用不方便自己就重新用python开发了一个同时用pyinstaller打包成可执行程序这里就不说明怎么打包了搜一下就出来了不打包也行看你的。感觉netmiko这个包还是很强大大部分的设备都支持不支持的也可以找到相似的去实现比如我这里迈普的设备就是这样。该项目需要对netmiko和pyqt5有一个基本的了解才能根据本人所写的去实现自定义的需求代码中也有相应的注释。控制线程个数本来是用线程池但是pyinstaller打包后有些线程无法执行完成不知道啥原因用信号量代替就正常了。
以下是几个源代码和配置文件的简单说明
main.py程序的入口main_ui.py图形化界面代码由qtdesigner生成并加以修改backup_cur.py具体功能的逻辑代码备份设备列表.txt目前只支持华为、迈普、锐捷、锐捷AC的配置备份其他类型的设备自己去实现就好不难已经有一个框架了分别对应配置文件中的Huawei、Mypower、Ruijie、RuijieAC配置文件每一个设备占用一行每一行的字段包含设备类型、IP地址、设备名称用空格分开。设备类型、IP地址对了就行设备名称随便起但不能是空。务必严格遵循配置文件的语法否则可能无法备份配置。具体可参考下面的文件示例。在还没有熟悉代码前所有文件名称最好别变否则无法使用。注意代码中的注释程序不难。
以下是各个源文件和文件 目录 main.py程序的入口main_ui.py图形化界面代码backup_cur.py具体功能的逻辑代码备份设备列表.txt main.py程序的入口
# Time : 2022/12/17
# Author : zhu
# Description : 程序入口
from sys import argv, exit
from PyQt5.QtWidgets import QApplication, QMainWindow
from main_ui import UiMainWindow
from backup_cur import BackupCurif __name__ __main__:app QApplication(argv)window QMainWindow()ui UiMainWindow(window)backup_cur BackupCur(ui) # 变量虽然不用但必须声明否则不生效window.show()exit(app.exec_())main_ui.py图形化界面代码
# Time : 2022/12/17
# Author : zhu
# Description : UI界面代码
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QActionclass UiMainWindow(object):由PyQt5 UI code generator 5.15.9生成def __init__(self, main_window):初始化:param main_window: QMainWindow类main_window.setObjectName(main_window)main_window.resize(1200, 800)main_window.setStyleSheet(QPushButton{background-color: rgb(0, 102, 179); color: rgb(255,255,255)})self.central_widget QtWidgets.QWidget(main_window)self.central_widget.setObjectName(central_widget)self.vertical_layout_3 QtWidgets.QVBoxLayout(self.central_widget)self.vertical_layout_3.setObjectName(vertical_layout_3)self.stacked_widget QtWidgets.QStackedWidget(self.central_widget)self.stacked_widget.setObjectName(stacked_widget)self.main_page QtWidgets.QWidget() # 主页self.main_page.setObjectName(main_page)self.horizontal_layout_2 QtWidgets.QHBoxLayout(self.main_page)self.horizontal_layout_2.setObjectName(horizontal_layout_2)self.horizontal_layout_1 QtWidgets.QHBoxLayout()self.horizontal_layout_1.setObjectName(horizontal_layout_1)self.label_1 QtWidgets.QLabel(self.main_page)font QtGui.QFont()font.setFamily(Agency FB)font.setPointSize(36)self.label_1.setFont(font)self.label_1.setAlignment(QtCore.Qt.AlignCenter)self.label_1.setWordWrap(True)self.label_1.setObjectName(label_1)self.horizontal_layout_1.addWidget(self.label_1)self.horizontal_layout_2.addLayout(self.horizontal_layout_1)self.stacked_widget.addWidget(self.main_page)self.backup_cur_page QtWidgets.QWidget() # 备份设备配置界面self.backup_cur_page.setObjectName(backup_cur_page)self.horizontal_layout_4 QtWidgets.QHBoxLayout(self.backup_cur_page)self.horizontal_layout_4.setContentsMargins(0, 0, 0, 0)self.horizontal_layout_4.setObjectName(horizontal_layout_4)self.vertical_layout_2 QtWidgets.QVBoxLayout()self.vertical_layout_2.setObjectName(vertical_layout_2)self.backup_cur_button QtWidgets.QPushButton(self.backup_cur_page)self.backup_cur_button.setObjectName(backup_cur_button)self.vertical_layout_2.addWidget(self.backup_cur_button)self.horizontal_layout_4.addLayout(self.vertical_layout_2)self.backup_plain_text_edit QtWidgets.QPlainTextEdit(self.backup_cur_page)self.backup_plain_text_edit.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)self.backup_plain_text_edit.setReadOnly(True)self.backup_plain_text_edit.setObjectName(backup_plain_text_edit)self.horizontal_layout_4.addWidget(self.backup_plain_text_edit)self.horizontal_layout_4.setStretch(0, 1)self.horizontal_layout_4.setStretch(1, 5)self.stacked_widget.addWidget(self.backup_cur_page)self.vertical_layout_3.addWidget(self.stacked_widget) # 菜单栏main_window.setCentralWidget(self.central_widget)self.menu_bar QtWidgets.QMenuBar(main_window)self.menu_bar.setGeometry(QtCore.QRect(0, 0, 1200, 26))self.menu_bar.setStyleSheet(QMenuBar::item::selected{background-color: rgb(0, 102, 179); color: rgb(255,255,255)})self.menu_bar.setObjectName(menu_bar)self.main_menu QAction() # 跳过传统的方式创建菜单栏的菜单项利用这种可以绑定点击出发时的要做的动作self.main_menu.setObjectName(main_menu)self.main_menu.triggered.connect(self.set_current_main) # 按下首页菜单时设置栈布局当前界面为首页self.backup_cur_menu QAction()self.backup_cur_menu.setObjectName(backup_cur_menu)self.backup_cur_menu.triggered.connect(self.set_current_backup_cur) # 按下首页菜单时设置栈布局当前界面为备份设备配置main_window.setMenuBar(self.menu_bar)self.menu_bar.addAction(self.main_menu)self.menu_bar.addAction(self.backup_cur_menu)self.retranslate_ui(main_window)self.stacked_widget.setCurrentIndex(0)QtCore.QMetaObject.connectSlotsByName(main_window)def retranslate_ui(self, main_window) - None:自定义字段设置:param main_window: QMainWindow类_translate QtCore.QCoreApplication.translatemain_window.setWindowTitle(_translate(main_window, 备份设备配置))self.label_1.setText(_translate(main_window, 未经允许不得擅自使用否则后果自负))self.backup_cur_button.setText(_translate(main_window, 开始备份))self.main_menu.setText(_translate(main_window, 首页))self.backup_cur_menu.setText(_translate(main_window, 备份设备配置))def set_current_main(self):设置栈布局当前界面为首页self.stacked_widget.setCurrentIndex(0)def set_current_backup_cur(self):设置栈布局当前界面为备份设备配置self.stacked_widget.setCurrentIndex(1)backup_cur.py具体功能的逻辑代码
# Time : 2022/12/17
# Author : zhu
# Description : 备份具体实现
# update : 使用pyqt制作图形化界面
import os
import datetime
import threading
import traceback
from socket import inet_pton, AF_INET, AF_INET6
from threading import Thread
from PyQt5.QtCore import *
from PyQt5.QtWidgets import QMessageBox
from netmiko import ConnectHandler
from main_ui import UiMainWindowdef is_ip_address(ip: str) - bool:判读给定的字符串是否是IPv4或IPv6地址:param ip: IP地址字符串try:inet_pton(AF_INET, ip)return Trueexcept:try:inet_pton(AF_INET6, ip)return Trueexcept:return Falseclass BackupCur(QObject):目前支持华为、迈普、锐捷、锐捷AC对应配置文件中的Huawei、Mypower、Ruijie、RuijieAC设备类型必须和以上说明的配置文件类型严格对上否则无法备份。控制线程个数本来是用线程池但是pyinstaller打包后有些线程无法执行完成就使用信号量代替了BACKUP_DEVICE_FILE ./备份设备列表.txtUSERNAME your_usernamePASSWORD your_passwordSSH_PORT 22SEM threading.Semaphore(10) # 信号量为10这里限制线程的最大数量为10个MY_SIGNAL pyqtSignal(list) # 该信号用于向主线程传递信息更新UI界面信息类型标识、信息体def __init__(self, ui_main_window: UiMainWindow):初始化:param ui_main_window: UiMainWindow类super().__init__(None)self.failed_count 0 # 统计备份失败的设备个数self.ui ui_main_windowself.thread None # 备份配置的子线程self.finished_device_count 0 # 统计备份完成的设备数量self.backup_path self.device_count 0 # 备份设备总数self.ui ui_main_windowself.ui.backup_cur_button.clicked.connect(self.confirm_backup)self.MY_SIGNAL.connect(self.my_slot)def confirm_backup(self) - None:确认是否进行备份message_box QMessageBox(QMessageBox.Question, 提示, 确认备份设备吗) # 自定义提示对话框yes message_box.addButton(message_box.tr(确定), QMessageBox.YesRole)message_box.addButton(message_box.tr(取消), QMessageBox.NoRole)message_box.exec_()if message_box.clickedButton() yes: # 选择确定self.finished_device_count 0self.failed_count 0Thread(targetself.start_backup).start() # 生成子线程避免主线程卡顿def start_backup(self) - None:开始进行备份self.MY_SIGNAL.emit([0])self.backup_path ./操作日志/ datetime.datetime.now().strftime(%Y-%m-%d_%H-%M-%S) /if not os.path.isdir(self.backup_path):os.makedirs(self.backup_path)try:with open(self.BACKUP_DEVICE_FILE) as file:device_list file.readlines() # 按行读取文件self.device_count len(device_list)for device in device_list:self.SEM.acquire() # 获取信号量保证最多只有10个线程同时运行self.thread threading.Thread(targetself.worker, args(device,))self.thread.start()except OSError: # 文件打开失败self.MY_SIGNAL.emit([1])def worker(self, worker_device: str) - None:备份设备的线程:param worker_device: 备份的设备信息列表类型、IP、设备名device_fields worker_device.split()for i in range(len(device_fields)):device_fields[i] device_fields[i].replace(\n, ).replace(\r, )try:if is_ip_address(device_fields[1]):device_type device_fields[0]if device_type Huawei:ssh_type huaweicmd dis curelse:ssh_type ruijie_os # Ruijie MaipuRT RuijieACcmd show runconnect_filed { # ssh各个字段device_type: ssh_type,host: device_fields[1],username: self.USERNAME,password: self.PASSWORD,port: self.SSH_PORT,session_log: self.backup_path device_fields[2] ( device_fields[1] ).log}device_name device_fields[2]try:net_connect ConnectHandler(**connect_filed) # ssh连接if device_type Huawei or device_type Ruijie:net_connect.send_command(command_stringcmd,read_timeout20.0,strip_promptFalse, # 输出结果包含设备提示strip_commandFalse) # 输出结果包含输入的命令elif device_type Mypower:net_connect.send_command(command_stringmore off,expect_stringdevice_name #,strip_promptFalse,strip_commandFalse)net_connect.send_command(command_stringcmd,expect_stringdevice_name #)else: # 锐捷ACnet_connect.send_command(command_stringcmd,expect_stringdevice_name #)net_connect.send_command(command_stringshow ap-config running,expect_stringdevice_name #)net_connect.disconnect()except:traceback.print_exc()self.MY_SIGNAL.emit([3, device_name, device_fields[1]])except: # 一般来说只会是由于配置文件的设备字段出错导致数组越界self.MY_SIGNAL.emit([2, worker_device])finally:self.SEM.release() # 释放信号量self.MY_SIGNAL.emit([4])def my_slot(self, args: list) - None:槽函数接收子线程信号并更改主界面UI:param args: 标识位、信息、设备名称、设备ip地址、未知设备if args[0] 0:self.ui.backup_plain_text_edit.appendPlainText(正在备份设备配置请稍后...)self.ui.backup_cur_button.setEnabled(False) # 设置按钮不可用以及颜色为灰色self.ui.backup_cur_button.setStyleSheet(QPushButton{background-color: rgb(128, 128, 128); color: rgb(255,255,255)})self.ui.backup_cur_button.repaint() # 重新绘制按钮否则样式可能不生效elif args[0] 1:self.ui.backup_plain_text_edit.appendPlainText(打开文件 os.path.abspath(self.BACKUP_DEVICE_FILE) 出错请检查配置文件是否存在)self.ui.backup_cur_button.setEnabled(True) # 备份完成恢复按钮为原样self.ui.backup_cur_button.setStyleSheet(QPushButton{background-color: rgb(1, 102, 179); color: rgb(255,255,255)})self.ui.backup_cur_button.repaint() # 重新绘制按钮否则样式可能不生效elif args[0] 2:self.failed_count 1self.ui.backup_plain_text_edit.appendPlainText(未知设备( args[1] )备份配置失败)elif args[0] 3:self.failed_count 1self.ui.backup_plain_text_edit.appendPlainText(args[1] (IP地址: args[2] ) 备份配置失败)elif args[0] 4:self.finished_device_count 1if self.finished_device_count self.device_count:self.ui.backup_plain_text_edit.appendPlainText(备份设备完成其中成功备份的设备为 str(self.device_count - self.failed_count) 台失败的为 str(self.failed_count) 台)self.ui.backup_plain_text_edit.appendPlainText(可前往[ os.path.abspath(self.backup_path) ]查看备份结果)self.ui.backup_cur_button.setEnabled(True) # 备份完成恢复按钮为原样self.ui.backup_cur_button.setStyleSheet(QPushButton{background-color: rgb(1, 102, 179); color: rgb(255,255,255)})self.ui.backup_cur_button.repaint() # 重新绘制按钮否则样式可能不生效self.ui.backup_plain_text_edit.repaint() # 重新绘制否则样式可能不生效备份设备列表.txt
RuijieAC 192.168.0.1 RUIJIE_AC_1
Mypower 192.168.0.2 MAIPU_RT_1
Ruijie 192.168.0.3 RUIJIE_SW_1
Huawei 192.168.0.4 HUAWEI_RT_1
文章转载自: http://www.morning.rhmpk.cn.gov.cn.rhmpk.cn http://www.morning.bmzxp.cn.gov.cn.bmzxp.cn http://www.morning.rwfj.cn.gov.cn.rwfj.cn http://www.morning.rqkck.cn.gov.cn.rqkck.cn http://www.morning.nhdmh.cn.gov.cn.nhdmh.cn http://www.morning.hkcjx.cn.gov.cn.hkcjx.cn http://www.morning.tdldh.cn.gov.cn.tdldh.cn http://www.morning.sdkaiyu.com.gov.cn.sdkaiyu.com http://www.morning.tpnxj.cn.gov.cn.tpnxj.cn http://www.morning.xuejitest.com.gov.cn.xuejitest.com http://www.morning.dqxnd.cn.gov.cn.dqxnd.cn http://www.morning.gypcr.cn.gov.cn.gypcr.cn http://www.morning.rxhn.cn.gov.cn.rxhn.cn http://www.morning.kxsnp.cn.gov.cn.kxsnp.cn http://www.morning.ntzbr.cn.gov.cn.ntzbr.cn http://www.morning.kmwsz.cn.gov.cn.kmwsz.cn http://www.morning.gswfs.cn.gov.cn.gswfs.cn http://www.morning.rrqgf.cn.gov.cn.rrqgf.cn http://www.morning.bkslb.cn.gov.cn.bkslb.cn http://www.morning.zcwzl.cn.gov.cn.zcwzl.cn http://www.morning.xjkfb.cn.gov.cn.xjkfb.cn http://www.morning.hngmg.cn.gov.cn.hngmg.cn http://www.morning.bsghk.cn.gov.cn.bsghk.cn http://www.morning.sxwfx.cn.gov.cn.sxwfx.cn http://www.morning.qfnrx.cn.gov.cn.qfnrx.cn http://www.morning.hqgkx.cn.gov.cn.hqgkx.cn http://www.morning.dmhs.cn.gov.cn.dmhs.cn http://www.morning.lmxzw.cn.gov.cn.lmxzw.cn http://www.morning.ypcbm.cn.gov.cn.ypcbm.cn http://www.morning.yprjy.cn.gov.cn.yprjy.cn http://www.morning.fxwkl.cn.gov.cn.fxwkl.cn http://www.morning.rzmkl.cn.gov.cn.rzmkl.cn http://www.morning.jrlxz.cn.gov.cn.jrlxz.cn http://www.morning.sbjhm.cn.gov.cn.sbjhm.cn http://www.morning.nmtyx.cn.gov.cn.nmtyx.cn http://www.morning.wdlg.cn.gov.cn.wdlg.cn http://www.morning.xjqkh.cn.gov.cn.xjqkh.cn http://www.morning.lnyds.cn.gov.cn.lnyds.cn http://www.morning.gyqnp.cn.gov.cn.gyqnp.cn http://www.morning.jgmlb.cn.gov.cn.jgmlb.cn http://www.morning.pgcmz.cn.gov.cn.pgcmz.cn http://www.morning.zrlms.cn.gov.cn.zrlms.cn http://www.morning.npqps.cn.gov.cn.npqps.cn http://www.morning.htrzp.cn.gov.cn.htrzp.cn http://www.morning.gqjzp.cn.gov.cn.gqjzp.cn http://www.morning.dtrzw.cn.gov.cn.dtrzw.cn http://www.morning.fdlyh.cn.gov.cn.fdlyh.cn http://www.morning.ydrml.cn.gov.cn.ydrml.cn http://www.morning.qnzld.cn.gov.cn.qnzld.cn http://www.morning.jcbmm.cn.gov.cn.jcbmm.cn http://www.morning.hksxq.cn.gov.cn.hksxq.cn http://www.morning.yxwcj.cn.gov.cn.yxwcj.cn http://www.morning.xdfkrd.cn.gov.cn.xdfkrd.cn http://www.morning.rksnk.cn.gov.cn.rksnk.cn http://www.morning.pwsnr.cn.gov.cn.pwsnr.cn http://www.morning.grjh.cn.gov.cn.grjh.cn http://www.morning.wkhfg.cn.gov.cn.wkhfg.cn http://www.morning.zkjqj.cn.gov.cn.zkjqj.cn http://www.morning.dwfxl.cn.gov.cn.dwfxl.cn http://www.morning.rnzwh.cn.gov.cn.rnzwh.cn http://www.morning.fktlr.cn.gov.cn.fktlr.cn http://www.morning.wngpq.cn.gov.cn.wngpq.cn http://www.morning.tgmwy.cn.gov.cn.tgmwy.cn http://www.morning.fmjzl.cn.gov.cn.fmjzl.cn http://www.morning.ldzxf.cn.gov.cn.ldzxf.cn http://www.morning.yysqz.cn.gov.cn.yysqz.cn http://www.morning.qlwfz.cn.gov.cn.qlwfz.cn http://www.morning.jtrqn.cn.gov.cn.jtrqn.cn http://www.morning.mhnr.cn.gov.cn.mhnr.cn http://www.morning.snktp.cn.gov.cn.snktp.cn http://www.morning.rgxn.cn.gov.cn.rgxn.cn http://www.morning.hmdyl.cn.gov.cn.hmdyl.cn http://www.morning.ldqrd.cn.gov.cn.ldqrd.cn http://www.morning.lxhgj.cn.gov.cn.lxhgj.cn http://www.morning.dmthy.cn.gov.cn.dmthy.cn http://www.morning.zzqgc.cn.gov.cn.zzqgc.cn http://www.morning.rrxnz.cn.gov.cn.rrxnz.cn http://www.morning.dzfwb.cn.gov.cn.dzfwb.cn http://www.morning.ktpzb.cn.gov.cn.ktpzb.cn http://www.morning.rbsmm.cn.gov.cn.rbsmm.cn