介绍
基于python 实现对交换机命令日志文件分析,获取字段并表格保存结果,界面采用tkinter 布局,简单绘制
软件架构
单机版
安装教程
- 安装python3.7以上版本
- 项目运行 python iTextTool.py
- 打包 可使用pyinstaller 进行打包 #运行
pip install pyinstaller --noconsole
使用说明
-
界面
-
选择解析日志文件路径,自动生成输出路径,可自行修改
-
日志文件中的命令目前实现了提取vlan svlan值,代码如下
# This is a sample Python script. # Press Shift+F10 to execute it or replace it with your code. # Press Double Shift to search everywhere for classes, files, tool windows, actions, and settings. import re import csv import sys import tkinter as tk from tkinter import ttk import threading import os from tkinter import filedialog, messagebox class App: def __init__(self, master): self.t: threading.Thread = None self.master = master top_frame = tk.Frame(self.master) top_frame.pack(side=tk.TOP, anchor=tk.N, padx=15, pady=15, fill="both") settings_frame = tk.LabelFrame(top_frame, text="参数配置", padx=5, pady=5) settings_frame.pack(anchor=tk.W, fill="y") field_frame_input = tk.Frame(settings_frame) field_frame_input.pack(side=tk.TOP, expand=tk.YES, pady=10) tk.Label(field_frame_input, text="解析文件路径:").pack(side=tk.LEFT) self.var_input_path = tk.StringVar() self.input_path = tk.Entry(field_frame_input, textvariable=self.var_input_path, width=60) self.input_path.pack(side=tk.LEFT) browse_file_btn = tk.Button(field_frame_input, text="选择", command=self.browse_file) browse_file_btn.pack(side=tk.RIGHT) field_frame_output = tk.Frame(settings_frame) field_frame_output.pack(side=tk.TOP, expand=tk.YES, pady=10) self.var_output_path = tk.StringVar() tk.Label(field_frame_output, text="输出文件路径:", ).pack(side=tk.LEFT) self.out_path = tk.Entry(field_frame_output,textvariable=self.var_output_path, width=80) self.out_path.pack(side=tk.LEFT) field_frame_prefix = tk.Frame(settings_frame) field_frame_prefix.pack(side=tk.TOP, expand=tk.YES, pady=10) tk.Label(field_frame_prefix, text="行首前缀剔除:").pack(side=tk.LEFT) self.iv_prefix_default = tk.IntVar() tk.Radiobutton(field_frame_prefix, text="是", padx=10, value=1, variable=self.iv_prefix_default, command=self.click_prefix) \ .pack(side=tk.LEFT) tk.Radiobutton(field_frame_prefix, text="否", padx=10, value=2, variable=self.iv_prefix_default, command=self.click_prefix) \ .pack(side=tk.LEFT) self.iv_prefix_default.set(2) self.iv_prefix_word = tk.StringVar() self.prefix = tk.Entry(field_frame_prefix, width=60, textvariable=self.iv_prefix_word, state=tk.DISABLED) self.prefix.pack(side=tk.LEFT) field_frame_command = tk.Frame(settings_frame) field_frame_command.pack(side=tk.TOP, expand=tk.YES, pady=10) tk.Label(field_frame_command, text="执行命令类型:").pack(side=tk.LEFT) self.var_commands = tk.StringVar() self.used_commands = ttk.Combobox(field_frame_command, textvariable=self.var_commands, width=90) self.used_commands.config(values=['show vlan summary', 'show running-config interface', 'show onu running config']) self.used_commands.pack(side=tk.LEFT, expand=tk.YES) self.used_commands.current(0) # 按钮栏 action_frame = tk.Frame(top_frame) action_frame.pack(side=tk.TOP) self.var_info = tk.StringVar() self.info_label = tk.Label(action_frame, textvariable=self.var_info) self.info_label.config(fg='#f24444') self.info_label.pack(side=tk.LEFT, anchor=tk.W) self.var_info.set("") self.cancel_btn = tk.Button(top_frame, text="取消", width=10) self.cancel_btn.pack(side=tk.RIGHT, padx=10, pady=10) self.exec_btn = tk.Button(top_frame, text="执行", width=10, command=self.start_task) self.exec_btn.pack(side=tk.RIGHT, padx=10, pady=10) def start_task(self): if self.check_input_var(): if os.path.exists(self.out_path.get()): response = messagebox.askyesno("Confirm", "文件已存在,是否覆盖?") if not response: return self.t = threading.Thread(target=self.analysis) self.t.start() def browse_file(self): path = filedialog.askopenfilename(title="选择文件", filetypes=(('文本文件', '*.txt'), ('所有文件', '*.*'))) self.var_input_path.set(path) os.path.splitext(path) self.var_output_path.set(os.path.splitext(path)[0]+'.csv') self.var_info.set("") def click_prefix(self): if self.iv_prefix_default.get() == 1: self.prefix.config(state=tk.NORMAL) else: self.prefix.config(state=tk.DISABLED) def check_input_var(self): infile = self.input_path.get() outfile = self.out_path.get() command = self.used_commands.get() is_prefix = self.iv_prefix_default.get() prefix_word = self.prefix.get() checked = True if not infile: self.var_info.set("解析文件不能为空!") checked = False elif not outfile: self.var_info.set("输出文件不能为空!") checked = False elif not command: self.var_info.set("命令不能为空!") checked = False elif is_prefix == 1 and not prefix_word: self.var_info.set("行首前缀截取词不能为空!") checked = False if checked and not os.path.exists(infile): self.var_info.set("解析文件不存在!!") checked = False return checked def analysis(self): self.exec_btn.config(state=tk.DISABLED) infile = self.var_input_path.get() outfile = self.var_output_path.get() command = self.var_commands.get() is_prefix = self.iv_prefix_default.get() prefix_word = self.iv_prefix_word.get() print(infile, '|', outfile, '|', command, '|', is_prefix, '|', prefix_word) result = [] head = [] # 分析方法 with open(infile, "r", encoding="utf-8") as f: content = f.read() match command: case 'show vlan summary': result, head = self.show_vlan_summary(self, content) case 'show running-config interface': result, head = self.show_running_config_interface(self, content) case 'show onu running config': result, head = self.show_onu_running_config(self, content) f.close() if len(result) == 0: messagebox.showinfo('Warning', '根据匹配规则未解析出数据!') else: self.write_csv(outfile=outfile, head=head, data=result) self.var_info.set("文件解析完成!") self.exec_btn.config(state=tk.NORMAL) @staticmethod def show_vlan_summary(self, content): print("start analysis show vlan summary.") # 匹配段落 pattern = r'following:(.+?)#' match_list = re.findall(pattern, content, flags=re.DOTALL) result = [] for line in match_list: # print(line) # 获取字段 columns = re.split(r"\s{7}", line) result.append(list(reversed(columns))) return result, ["设备名", "vlan值"] @staticmethod def show_running_config_interface(self, content): # 匹配段落 print("start analysis show running-config interface.") graph_pattern = r'](.+?)#show running-config interface (.+?)\s(.+?)!' column_pattern = r'service-port (\d+) vport (.+?) user-vlan (.+?) vlan (.\d+)(.+)' match_list = re.findall(graph_pattern, content, flags=re.DOTALL) result = [] for line in match_list: if len(line) >= 3: values = re.findall(column_pattern, line[2]) for v in values: column = [line[0], line[1].split('_')[1], v[0], v[3]] svlan = re.findall(r'\d+', v[4]) if svlan: column.append(svlan[0]) else: column.append("") print(column) result.append(column) return result, ["设备名", "gpon-onu", "service port", "vlan", "svlan"] @staticmethod def show_onu_running_config(self, content): # 匹配段落 # pattern = r'following:(.+?)#' # match_list = re.findall(pattern, content, flags=re.DOTALL) # result = [] # for line in match_list: # # 获取字段 # columns = re.split(r"\s{7}", line) # # result.append(list(reversed(columns))) # return result return [], [] def write_csv(self, outfile, head, data): try: with open(outfile, "w", newline="") as csv_file: writer = csv.writer(csv_file) writer.writerow(head) writer.writerows(data) csv_file.close() except Exception as e: self.var_info.set(sys.exc_info()) def cancel_task(self): print("cancel task.") root = tk.Tk() root.geometry('600x500+500+200') root.title("iText Tool v1.0") app = App(root) root.mainloop()