0
点赞
收藏
分享

微信扫一扫

正则解析网络运维数据之分而治之

上一期我们分享了正则解析的OneTake方式,比如解析mac、arp等。其实我们也可以解析version、型号、系列等单个指标信息。在全文里一次查找,都不需要将文本分行切割。

今天我们分享一种解析的思路——分而治之,带大家一起边讲边写,希望大家跟着我的思路走。

这种主要是解析show interface这种配置

正则解析网络运维数据之分而治之_字符串

端口配置的特点是:

1、信息是按段分开的

2、每段里面含有大量的信息

3、每段信息里有些字段是一定有的,有些是可能有可能无的


我们的思路就是:

1、先找特点,将配置信息分成一段一段的文本

2、根据要取的信息特点写正则

3、去每一段文本里匹配正则提取信息,如果无则置空字段


我们开始写一段解析端口的配置。

1、首先我们先切割文本段

把每个端口的信息放到一个字符串里处理。

确定换行符是很重要的一个环节,我们用这段代码把换行符给找出来。

import re
with open('show_interface.log','r',encoding='utf8') as f:
    log = f.read()
    print(log.encode('utf8'))

正则解析网络运维数据之分而治之_正则_02

有时候设备的换行符是“\r\n”,这个里面是"\n"(说实话底层我也不太清楚,但是我知道Linux和windows对于换行是有区别的,与采集的设备和被采集的设备可能都有关系。建议初期大家都去看换行是什么。后期写的代码我很少通过本地直接采集设备,而是通过一个自己封装的API去ssh到设备采集信息,所以返回的格式比较统一,但是偶尔有时候也会出现有的设备返回的是\r\n,有的返回的是\\r\\n,所以具体问题具体分析,同时我会在处理正则的时候全局把换行统一一下)


正则解析网络运维数据之分而治之_正则_03

从上面的截图我们发现,端口的信息总是端口名称 is 开头,两个换行结束,所以我们用正则去把所需要的全部端口文本先筛选出来。

正则解析网络运维数据之分而治之_正则_04

import re
with open('show_interface.log','r',encoding='utf8') as f:
    log = f.read()
    intf_info_re = re.compile(r'(?:Ethernet\d+/\d+|Vlan\d+|mgmt\d+)\s+is[\s\S]+?\n\n')
    intf_info_texts = intf_info_re.findall(log)
    for i in intf_info_texts:
        print(i,end='以上是一个端口信息\n')

这里面还是有三点要说,

“(?:一段正则|)”,两个部分说,圆括号代表的是优先级,这几个端口类型是或的关系“|”,为了防止下面这种情况我们把端口类型几个用圆括号给圈起来

正则解析网络运维数据之分而治之_字段_05

圆括号表示是一个整体,同时默认代表的是提取这个串。结果如果不加“?:”,我们提取的结果是端口名称,而没有提取整个端口的信息,有两种做法,一种是用“?:”代表识别正则但是不提取子串,所以findall是以整体去提出每个端口的配置信息。

第二种方式是把整个串再用圆括号圈起来,那findall会识别第一个子串和整体这个子串,返回的是tuple的列表。

正则解析网络运维数据之分而治之_字段_06

我不太推荐第二种方法。

然后是findall的用法,之前正则也讲过,就是返回所有匹配的数据列表,数据可能是字符串可能是tuple等。大家尽量用第一种方法,返回的就是字符串了。

还有一个点就是我用了“[\s\S]+?"端口开头和两个换行中间的部分,同时用的是非贪婪,这样防止它把中间其他端口贪婪的也识别了。

2、文本内编写OneTake,提取重要信息

接下来做的就很简单了,在每个提取的文本段内(字符串)提取我们想要的端口信息就可以了,你可以写正则,甚至用字符串的一些find、index等方法提取,用最省力的。这段无疑用正则一个个去解析最合适。

我们打个样,在文本内去解析端口名和状态以及端口描述,其他的大家可以自己练习以下。

先写端口的,其实刚才我们已经写好了端口的正则了 

intf_name_re = re.compile(r'Ethernet\d+/\d+|Vlan\d+|mgmt\d+',re.I)
intf_protocol_status_re = re.compile( r'\d+\sis\s([a-zA-Z]+)',re.I)
intf_desc_re = re.compile( r'Description:([\s\S]+?)\n',re.I)

  • 端口的没什么好说的。后面加了re.I代表的是忽略大小写。
  • 状态的找规律即可,前面是端口号数字空格然后是is,紧接着是空格,后面是up、down,防止以后出幺蛾子,我们用字母框都匹配。这个地方设计到数据建模问题,我们的每个字段后期一定是我们的一个数据表,数据结构,所以前期每个字段都要斟酌,比如状态是分协议状态和配置状态的。
  • 描述的我们中间用空格非空格非贪婪一下匹配到换行结束,简单粗暴。
    上代码和结果
  • 正则解析网络运维数据之分而治之_字符串_07

可以看到拿到了端口的信息表,还有很多字段大家可以按需去添加。

有几点说明:

  • 我们用了search,注意它和match的区别
  • 我们取字段的时候group的一些操作
  • 注意我的缩进,变量命名方式
  • 我们预先赋值字段,防止到时候引用发现没有定义

以上就是今天的分享,今天的正则,我变着花的给大家写,希望尽量带入一些知识点,这些花儿也是必要的。后续还会继续分享一些TIPS。


希望大家体会我在写这个正则时候的思路,顺序。这是我们写了这么久的一个经验总结,最佳实践~可以少走一些弯路。

最后奉上今天的主体代码,希望大家点赞、分享、订阅、喜欢、在看~


下一篇文章,我打算写写非技术的,胡吹乱侃一些对NetDevOps的看法。大家在技术上有什么难题也欢迎与我分享,我也许可以提供一些思路~

同时在考虑 要不要来一个集赞写正则的活动:)

import re
intf_name_re = re.compile(r'Ethernet\d+/\d+|Vlan\d+|mgmt\d+',re.I)
intf_protocol_status_re = re.compile( r'\d+\sis\s([a-zA-Z]+)',re.I)
intf_desc_re = re.compile( r'Description:([\s\S]+?)\n',re.I)


with open('show_interface.log','r',encoding='utf8') as f:
    intfs = []
    log = f.read()
    intf_info_re = re.compile(r'(?:Ethernet\d+/\d+|Vlan\d+|mgmt\d+)\s+is[\s\S]+?\n\n')
    intf_info_texts = intf_info_re.findall(log)
    for i in intf_info_texts:
        intf_name = None
        intf_protocol_status = None
        intf_desc = None
        
        intf_name_match = intf_name_re.search(i)
        intf_protocol_status_match = intf_protocol_status_re.search(i)
        intf_desc_match = intf_desc_re.search(i)
        
        if intf_name_match:
            intf_name = intf_name_match.group()
            
        if intf_protocol_status_match:
            intf_protocol_status = intf_protocol_status_match.group(1)
            intf_protocol_status = intf_protocol_status.strip()
            
        if intf_desc_match:
            intf_desc = intf_desc_match.group(1)
            intf_desc = intf_desc.strip()
            
        intfs.append({
            'intf_name':intf_name,
            'intf_protocol_status':intf_protocol_status,
            'intf_desc':intf_desc
        })
    for intf in intfs:
        print(intf)



举报

相关推荐

0 条评论