python示例
要看颜色的,下载 DEF_COLOR.py 放相同目录即可 http://t.csdnimg.cn/RFd2p
不要看的,修改打印部分代码即可
# -*- coding: utf8 -*-
import os, zipfile
from DEF_COLOR import * ## 终端显示颜色
## 读取网络设备上下载的 startup.cfg 配置文件(文本文件)
## 读取 dis cu 查询配置保存下来的文本文件
def 读取文本文件内容(FILE_PATH):
L_编码 = ['UTF8', 'GB2312', 'GBK', 'BIG5']
TEXT = None
if os.path.isfile(FILE_PATH):
for 编码 in L_编码:
try:
#print(f"尝试 {编码}")
f = open(FILE_PATH, 'r', encoding=编码)
TEXT = f.read()
except Exception as e:
f.close()
#打印_红(f"{编码}编码读取{FILE_PATH}失败{e}")
else:
f.close()
#print(f"成功 TEXT={TEXT}")
return(TEXT)
else:
打印_红(f"ERROR 文件 {FILE_PATH} 不存在")
return(TEXT)
打印_红(f"ERROR 文件 {FILE_PATH} 字符编码解码失败,超出{L_编码}范围")
return(TEXT)
## 网络设备上下载的配置文件有压缩包类型,如:vrpcfg.zip 里面就一个配置文本文件 vrpcfg.cfg
def 读取ZIP中第一个文件(FILE_PATH):
TEXT = None
if os.path.isfile(FILE_PATH):
if zipfile.is_zipfile(FILE_PATH):
try:
f = zipfile.ZipFile(FILE_PATH)
except Exception as e:
ERROR = f'ERROR zipfile 打开 {FILE_PATH} 失败 {e}'
打印_红(ERROR)
return()
else:
for i in f.namelist():
#print("ZIP内文件", i)
文件名, 扩展名 = os.path.splitext(i)
#print(文件名, 扩展名)
if i.endswith('/'):
#print(f"目录 {i}")
pass
else:
#print(f"文件 {i}")
try:
DATA = f.open(i)
except Exception as e:
ERROR = f'ERROR 打开 {i} 失败 {e}'
print(ERROR)
else:
TEXT = DATA.read().decode('GB2312') # 一般是 GB2312
#print("TEXT", TEXT)
break
else:
打印_红(f"ERROR {FILE_PATH} 不是ZIP格式文件")
else:
打印_红(f"ERROR {FILE_PATH} 不存在或不是文件")
return(TEXT)
def 前置空格数(TEXT):
N = 0
for i in TEXT:
if i == ' ':
N += 1
else:
break
return(N)
def 配置文本分段分行(TEXT_CONF, SHOW=0):
LL_CONF = []
LL_配置行 = [[j for j in i.split('\r')] for i in TEXT_CONF.split('\n')] # 以换行符分段
#打印_蓝(f"换行符分段 LL_配置行={LL_配置行}", SHOW)
L_配置段 = []
for L_配置行 in LL_配置行:
#打印_红(f"{L_配置行}", SHOW)
for 配置行 in L_配置行:
if 配置行.strip() != '': # 忽略空配置行
if 配置行 == '#': # 遇到分段符号'#'进行分段保存
if L_配置段 != []:
LL_CONF.append(L_配置段)
L_配置段 = [] # 重置为空列表
else:
L_配置段.append(配置行)
#打印_绿(f"{LL_CONF}", SHOW)
#for i in LL_CONF:
# 打印_蓝(f"{i}\n", SHOW)
return(LL_CONF)
def 配置行转配置信息字典(LL_CONF, SHOW=0):
打印_红("[配置行转配置信息字典]", SHOW)
D_NET_CONF = {'GLOBAL':[]}
for L_配置段 in LL_CONF:
打印_红(f"L_配置段={L_配置段}", SHOW)
KEY = 'GLOBAL'
for TEXT_配置行 in L_配置段:
配置行前置空格数 = 前置空格数(TEXT_配置行)
打印_蓝(f" 前置空格数={配置行前置空格数} TEXT_配置行={TEXT_配置行}", SHOW)
if 配置行前置空格数 == 0:
KEY = TEXT_配置行 # 更新KEY
if KEY not in D_NET_CONF:
D_NET_CONF[KEY] = []
打印_绿(f" KEY={KEY}", SHOW)
else:
D_NET_CONF[KEY].append(TEXT_配置行)
打印_绿(f" D_NET_CONF[{KEY}].append({TEXT_配置行})", SHOW)
return(D_NET_CONF)
def 转换网络TXT配置文件(FILE_CONF_PATH, SHOW=0):
D_NET_CONF = {}
TEXT_CONF = 读取文本文件内容(FILE_CONF_PATH)
打印_黄(f"TEXT_CONF={TEXT_CONF}", SHOW)
if TEXT_CONF != None:
LL_CONF = 配置文本分段分行(TEXT_CONF, SHOW)
D_NET_CONF = 配置行转配置信息字典(LL_CONF, SHOW)
return(D_NET_CONF)
def 转换网络ZIP配置文件(FILE_CONF_PATH, SHOW=0):
D_NET_CONF = {}
TEXT_CONF = 读取ZIP中第一个文件(FILE_CONF_PATH)
打印_黄(f"TEXT_CONF={TEXT_CONF}", SHOW)
if TEXT_CONF != None:
LL_CONF = 配置文本分段分行(TEXT_CONF, SHOW)
D_NET_CONF = 配置行转配置信息字典(LL_CONF, SHOW)
return(D_NET_CONF)
def AAA用户信息转字典(LL_CONF, SHOW=0):
打印_红("[AAA用户信息转字典]", SHOW)
D_AAA = {'aaa':[]}
KEY = 'aaa'
for TEXT_配置行 in LL_CONF:
配置行前置空格数 = 前置空格数(TEXT_配置行)
打印_蓝(f" 前置空格数={配置行前置空格数} TEXT_配置行={TEXT_配置行}", SHOW)
if 配置行前置空格数 == 1:
KEY = ('aaa', TEXT_配置行) # 更新KEY
if KEY not in D_AAA:
D_AAA[KEY] = []
打印_绿(f" KEY={KEY}", SHOW)
else:
D_AAA[KEY].append(TEXT_配置行)
打印_绿(f" D_AAA[{KEY}].append({TEXT_配置行})", SHOW)
return(D_AAA)
def 配置字典再加工(D_NET_CONF, SHOW=0):
# 删除一些无用信息(注释、查询命令等)
for K in [i for i in D_NET_CONF]:
if K == 'dis cu' or K[0] in ('*', '<', '[', '\x00'):
del D_NET_CONF[K]
打印_黄(f" 删除 {K}", SHOW)
elif K[-7:] == ' +08:00': # '2022-11-21 09:40:43.824 +08:00'
del D_NET_CONF[K]
打印_黄(f" 删除 {K}", SHOW)
elif K[:27] == 'Info: The max number of VTY': # Info: The max number of VTY users is 10, and the number
del D_NET_CONF[K]
打印_黄(f" 删除 {K}", SHOW)
#else:
# 打印_绿(f" 保留 {K}", SHOW)
# 删除空全局配置
if 'GLOBAL' in D_NET_CONF and D_NET_CONF['GLOBAL'] == []:
del D_NET_CONF['GLOBAL']
打印_黄(f" 删除 空 D_NET_CONF['GLOBAL']", SHOW)
# 进一步细分,整理防火墙安全策略信息,方便对比策略变化
if 'security-policy' in D_NET_CONF:
#print(D_NET_CONF['security-policy'])
LL_CONF = D_NET_CONF['security-policy']
D_security_policy = 防火墙安全规则转字典(LL_CONF, SHOW)
del D_NET_CONF['security-policy']
for K in D_security_policy:
D_NET_CONF[K] = D_security_policy[K]
# 进一步细分,整理aaa用户信息,方便对比用户信息变化
if 'aaa' in D_NET_CONF:
LL_CONF = D_NET_CONF['aaa']
D_AAA = AAA用户信息转字典(LL_CONF, SHOW)
del D_NET_CONF['aaa']
for K in D_AAA:
D_NET_CONF[K] = D_AAA[K]
def 网络配置文件转配置字典(FILE_CONF_PATH, SHOW=0):
FILE_NAME = os.path.basename(FILE_CONF_PATH)
SP = FILE_NAME.split('.')
if SP != [] and SP[-1].lower() == 'zip':
D_NET_CONF = 转换网络ZIP配置文件(FILE_CONF_PATH, SHOW)
else:
D_NET_CONF = 转换网络TXT配置文件(FILE_CONF_PATH, SHOW)
配置字典再加工(D_NET_CONF, SHOW)
return(D_NET_CONF)
def 对比配置字典(D_OLD, D_NEW, SHOW=1):
TEXT_对比结果 = ''
if D_OLD != {} and D_NEW != {}:
## 对比新旧配置项是否有新增或删除
P_KEY_D_OLD = set([K for K in D_OLD]) # 旧配置项
P_KEY_D_NEW = set([K for K in D_NEW]) # 新配置项
#print(f"P_KEY_D_OLD={P_KEY_D_OLD}")
#print(f"P_KEY_D_NEW={P_KEY_D_NEW}")
P_KEY_OLD_独有 = P_KEY_D_OLD - P_KEY_D_NEW
P_KEY_NEW_独有 = P_KEY_D_NEW - P_KEY_D_OLD
if P_KEY_OLD_独有 == P_KEY_NEW_独有 == set():
P_KEY_共有 = P_KEY_D_OLD
INFO = f" [新旧配置项名] 一致"
TEXT_对比结果 += INFO+'\n'
打印_绿(INFO, SHOW)
else:
P_KEY_共有 = P_KEY_D_OLD & P_KEY_D_NEW
INFO = f" [新旧配置项名] 不同\n P_KEY_OLD_独有 {P_KEY_OLD_独有}\n P_KEY_NEW_独有 {P_KEY_NEW_独有}"
打印_黄(INFO, SHOW)
TEXT_对比结果 += INFO+'\n'
for KEY in P_KEY_OLD_独有:
INFO = f" [删除配置项] {KEY} 【配置项内容】 {D_OLD[KEY]}"
TEXT_对比结果 += INFO+'\n'
打印_蓝(INFO, SHOW)
for KEY in P_KEY_NEW_独有:
INFO = f" [新增配置项] {KEY} 【配置项内容】 {D_NEW[KEY]}"
TEXT_对比结果 += INFO+'\n'
打印_青(INFO, SHOW)
## 相同配置项对比配置内容是否有变化
for KEY in P_KEY_共有:
if D_OLD[KEY] != D_NEW[KEY]:
P_OLD_配置行 = set(D_OLD[KEY])
P_NEW_配置行 = set(D_NEW[KEY])
if P_OLD_配置行 == P_NEW_配置行:
INFO = f" [配置项] {KEY} 配置行内容有重复"
TEXT_对比结果 += INFO+'\n'
打印_红(INFO, SHOW)
else:
INFO = f" [变化配置项] {KEY}"
TEXT_对比结果 += INFO+'\n'
打印_红(INFO, SHOW)
for i in P_OLD_配置行-P_NEW_配置行:
INFO = f" [DEL] {i}"
TEXT_对比结果 += INFO+'\n'
打印_蓝(INFO, SHOW)
for i in P_NEW_配置行-P_OLD_配置行:
INFO = f" [ADD] {i}"
TEXT_对比结果 += INFO+'\n'
打印_青(INFO, SHOW)
else:
if D_OLD == {}:
INFO = " D_OLD 无有效内容"
TEXT_对比结果 += INFO+'\n'
打印_红(INFO)
if D_NEW == {}:
INFO = " D_NEW 无有效内容"
TEXT_对比结果 += INFO+'\n'
打印_红(INFO)
return(TEXT_对比结果)
## 测试
if __name__ == '__main__':
def 测试_对比两个配置文件差异(FILE_CONF_OLD_PATH, FILE_CONF_NEW_PATH):
D_NET_CONF_OLD = 网络配置文件转配置字典(FILE_CONF_OLD_PATH, SHOW=0)
D_NET_CONF_NEW = 网络配置文件转配置字典(FILE_CONF_NEW_PATH, SHOW=0)
打印_蓝(f"OLD {FILE_CONF_OLD_PATH}")
打印_青(f"NEW {FILE_CONF_NEW_PATH}")
TEXT_对比结果 = 对比配置字典(D_NET_CONF_OLD, D_NET_CONF_NEW, SHOW=1)
#打印_紫(TEXT_对比结果)
#FILE_CONF_OLD_PATH = '192.168.1.254.22.20240723.vrpcfg.zip' # 压缩文件格式
FILE_CONF_OLD_PATH = '192.168.1.254.22.20240724.startup.cfg' # 文本文件格式
FILE_CONF_NEW_PATH = '192.168.1.254.22.20240725.startup.cfg'
测试_对比两个配置文件差异(FILE_CONF_OLD_PATH, FILE_CONF_NEW_PATH)