Bootstrap

基于python 实现对交换机命令日志文件特征提取

介绍

基于python 实现对交换机命令日志文件分析,获取字段并表格保存结果,界面采用tkinter 布局,简单绘制

软件架构

单机版

安装教程
  1. 安装python3.7以上版本
  2. 项目运行 python iTextTool.py
  3. 打包 可使用pyinstaller 进行打包 #运行 pip install pyinstaller --noconsole
使用说明
  1. 界面 

    输入图片说明

  2. 选择解析日志文件路径,自动生成输出路径,可自行修改 

    输入图片说明

  3. 日志文件中的命令目前实现了提取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()

;