0
点赞
收藏
分享

微信扫一扫

TextFSM:强无敌的配置解析利器-全面解读

雷亚荣 2023-05-11 阅读 101

TextFSM的深入浅出

之前我们简单介绍过TextFSM,简单讲了一下基本的定义以及使用,这次我们深入讲解一下,希望大家在写一些比较复杂的解析的时候能够游刃有余。

这部分,全网中文的相关资源几乎也是没的(至少我是没看到了。。),一开始不太清楚,为什么TextFSM这么便捷的一个工具没有推广开来,思来想去,还是NetDevOps圈子太小,在国内甚至没圈子,不像系统运维的ansible、elk等,教程和用户都非常多。

whatever,今天我们继续分享Textfsm,把上一期没讲的一些概念补全一下,同时带大家写几个例子去感受一下。

主要会讲讲几个action,使其像状态机一样在state之间转换。

今天的内容大纲:

state的定义

rule的定义

rule的action

record的action

State定义

value的定义咱们之前讲过也讲的比较全了,这次回顾并补充一下state

value定义之后,隔一个空行,我们定义state。顶头是state的name,按照大驼峰命名法我觉得比较合适。state内含一系列rule,每个rule一行,用上箭头表示开始,然后是rule,rule就是如何处理这一行文本,一会我们讲讲rule。

一个state的定义如下:

stateName
 ^rule
 ^rule
 ...

rule前面必须是以正则中的开始符号上箭头表示开始。

保留的state

TextFSM有两个保留的State,一个必须显示定义的Start,一个是隐式定义可以复写的EOF。

Start

FSM(有限状态自动机,或称状态机)必须以Start这个state开始,如果没Start会无法继续下去。

EOF

当输入(文本)读取到EOF的时候,FSM进入EOF状态,这也是一个保留的FSM状态,但是是隐式的,它看起来是这样的,注意是看起来是这样的,我个人觉得应该是^$$代表结束,默认最终EOF会执行一次记录后退出。

EOF
  ^.* -> Record

读取到EOF,将当前所解析的record进行一次记录,然后退出。大家可以按照实际情况去进行EOF状态的动作,覆盖掉上面这个隐式的EOF State,其实只有一种可操作的方式,显示的定义一个空的EOF状态如下。

EOF

这个状态的意思是停止解析退出,同时不会将最后一条的解析记录追加到已经解析的列表里。

演示

我们看个例子

Value Required intf_name (loopback\d+|Vlan\d+|Ethernet\d+/\d+)
Value status (up|down)

Start
  ^${intf_name}\s+is up -> Record
  ^${intf_name}\s+is down -> EOF

我的文本是一个show interface的,其中eth1/1-eth1/4是up的,1/5是down掉的,

如果按照上述的情况,前面的解析都会成功并记录,1/5会进入EOF,EOF State默认的是记录,所以最后的结果是1-5端口都记录无误。

右箭头代表一个action或者状态的转移,一会我们会细说。

但如果我改成下面这种

Value Required intf_name (loopback\d+|Vlan\d+|Ethernet\d+/\d+)
Value status (up|down)

Start
  ^${intf_name}\s+is up -> Record
  ^${intf_name}\s+is down -> EOF

EOF

EOF State被重写,不进行任何操作直接退出,所以解析的结果只有1-4.

EOF State可以被重写,但是只有上面这一种方式,如果EOF里有任何rule,直接报错。Textfsm底层代码会判断EOF是否为空,有rule就是非空,非空直接报错。

我们之前写的那个默认的EOF状态只是方便大家理解,是Textfsm底层执行的逻辑,如果你自己这样写一遍放最后,相信我,一定会报错,因为EOF非空了,它内含rule了。

EOF的设计理念

  • 我们什么都不操作,TextFSM读取到文本结束会自动进入EOF。这个是FSM必须有的一个终止的状态。
  • 显示的进入EOF,退出解析,这个根据实际解析场景去设计:
  • 比如我们读取到一条记录就可以了,后面再读取的会生成多条,反正我只取第一条,没有必须再解析,我们可以显示的调用EOF
  • 遇到一些特殊情况,比如show lldp,但是lldp 没有enable,我们可以直接退出,EOF,这样可以节省资源,不要再去读取并试图解析后续数据了。
  • 这个是我能想到的情景,实际使用中第二种我看到过例子。
  • EOF显示声明主要是为了提出不记录当前这条已经匹配的record。

Rules的定义

每个状态都定义1-N个rules(EOF如果复写必须为空,即没有rules)。

rule的定义包括正则匹配和action两部分,正则部分与action之间用->连接(有一个空格),action可以为空(其实是默认的action,正则之前咱们讲过,action分为三种,action可以相互组合。

Textfsm从文本读取一行然后会逐个去与每行rule进行匹配。如果rule与当前行文本相匹配,则执行对应的action,按此往复循环直到EOF(隐式和显式声明两种)。

rule的格式如下:

^regex [-> action]

regex中可以有0或者多个Value声明,必须以上箭头开始后跟regex。Value的声明可以有两种方式

或者{ValueName} ,后者是推荐的方式。实际运行中,TextFSM会自动的把Value定义的正则展开到rule中,匹配的时候会自动赋值到当前条记录。我们在表示行结束的时候需要用**双符号,这点和正则有所区别,我觉得可能是与ValueName这种声明方式区别开。

演示

定义一个模板

Value Interface (\S+)

Start
  ^Interface ${Interface} is up

Text FSM会把以上自动展开成如下的正则

^Interface (\S+) is up

当我们传入的行内容是Interface GigabitEthernet1/10 is up.时,与以上的正则匹配到,同时会提取出(\S+)的正则内容,赋值给声明的Interface。

value和rule的这种结合,可以很好的将字段与show出来的文本规律在一定程度上解绑。

Action

解析出来以后我们肯定要处理,这个处理就是action。

Action一定是当前文本行与rule是匹配的,这个是前提。

action分为三类:

  • Line Actions
  • Record Actions
  • New State Transition(新的状态转移)

我们分别来讲讲,这部分讲完,TextFSM基本也就说透了,基于状态机它具备如何灵活多变的能力,会比网上其他人的分享和简单的例子要稍微深入一些。但是TextFsm毕竟不是很复杂,我认为的核心代码,1000行出头。

action的格式如下

^rule -> LineAction.RecordAction AnotherState

其中每个Action都是按需填写的,Textfsm都可以灵活识别出来,默认的Action(rule后面什么都没)则使用默认的Next.NoRecord.

Line Actions

Action

Description

Next

Finish with the input line, read in the next and start matching again from the start of the state. This is the default behavior if no line action is specified.

Continue

Retain the current line and do not resume matching from the first rule of the state. Continue processing rules as if a match did not occur (value assignments still occur).

这个是原文。翻译下:

  • Next是默认的,如果rule后面没有LineAction的话。它会结束当前行文本,读取新的一行文本,并在state中从头开始再去每个rule尝试匹配。
  • Continue,它会保留当前行的文本,然后不是从state的第一个rule开始匹配,继续匹配下面的rules。简单点说是跳过了当前行文本的匹配,当前行文本继续尝试下一个rule。用处很多,比如在一行中拆开多段,分而治之。有时候是一些小技巧结合Record会产生很多功能。

演示

Next是默认的,我们就不演示了,我们演示一下continue

Continue的四种用法

1

只摘取一段rules

^\s+Hardware\s+is\s+${HARDWARE_TYPE} -> Continue
  ^.*BW\s+${BANDWIDTH},\s+DLY\s+${DELAY}

原始的文本

Hardware is i82540EM rev02, BW 1000 Mbps, DLY 10 usec

Textfsm都是逐行去匹配的,但是我们想重复使用一行,就可以使用continue。同时保证了rule的精细化,背后对应的可能是更高的兼容性,这几个字段在一行不在一行都匹配没有影响。

2

这个只是一种Continue的典型方法,更多时候是Continue.Record结合使用。

FSM Template:
Value Protocol (\S)
Value Type (\S\S)
Value Prefix (\S+)
Value Gateway (\S+)
Value Distance (\d+)
Value Metric (\d+)
Value LastChange (\S+)

Start
  ^.*----- -> Routes
  ^  \S \S\S -> Continue.Record
  ^  ${Protocol} ${Type} ${Prefix}\s+via ${Gateway}\s+${Distance}/${Metric}\s+${LastChange}
  ^\s+via ${Gateway}

针对的文本是

Destination        Gateway                      Dist/Metric Last Change
       -----------        -------                      ----------- -----------
  B EX 0.0.0.0/0          via 192.0.2.73                  20/100        4w0d
                          via 192.0.2.201
                          via 192.0.2.202
                          via 192.0.2.74
  B IN 192.0.2.76/30     via 203.0.113.183                200/100        4w2d
  B IN 192.0.2.204/30    via 203.0.113.183                200/100        4w2d
  B IN 192.0.2.80/30     via 203.0.113.183                200/100        4w2d
  B IN 192.0.2.208/30    via 203.0.113.183                200/100        4w2d

rule匹配到了路由的开始,我们首先记录一下已经收集到的record,然后将当前行continue匹配后续的路由明细。

第一个rule表明我们进入了一条新路由,开始为空,也就自然不会记录,但是后续循环开始了,我们会逐个解析,一旦进入一条新路由,立刻将解析的内容追加到返回值的列表中。继续将当前行去进入下一个路由解析的rule,因为逐个路由的开始中有我们的路由条目等详细信息。很巧妙的使用。

3

表示进入开始

Value VLAN (\S+)
Value MAC (\S+)
Value TYPE (\S+)
Value AGE (\S+)
Value SECURE ([TF])
Value NTFY ([TF])
Value PORTS (\S+)

Start
  ^VLAN\s+MAC\s+Address\s+Type\s+age\s+Secure\s+NTFY\s+Ports -> Continue
  ^.*\s${VLAN}\s+${MAC}\s+${TYPE}\s+${AGE}\s+${SECURE}\s+${NTFY}\s+${PORTS} -> Record

逐个表示进入了开始。比next减少一些资源开销,实际效果貌似无差异。

4

一行文本可能有多种表现形式也非常适合continue。

如下,我们就逐个去匹配,以保证我们匹配的准确一些,下面的会覆盖上面的。

Value Required VLAN_ID (\d+)
Value NAME (\S+)
Value STATUS (\S+)
Value List INTERFACES ([\w\./]+)

Start
  ^VLAN\s+Name\s+Status\s+Ports -> VLANS
  ^VLAN\s+Type\s+Vlan-mode
  ^Remote\s+SPAN\s+VLANs
  ^Primary\s+Secondary\s+Type\s+Ports

VLANS
  ^\d+ -> Continue.Record
  ^${VLAN_ID}\s+${NAME}\s+${STATUS}\s*$$
  ^${VLAN_ID}\s+${NAME}\s+${STATUS}\s+${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){3}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){4}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){5}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){6}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){7}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){8}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){9}${INTERFACES},* -> Continue
  ^\d+\s+(?:\S+\s+){10}${INTERFACES},* -> Continue

这段是不完整的,只是演示一下。

我看了这么多textfsm,基本就这么几种情况。限于作者本身水平,如有错误,还请谅解。

Record Actions

line的action之后是record的操作,这个record是指匹配到现在的信息提取出的一条记录(一个字典)。

Action

Description

NoRecord

Do nothing. This is the default behavior if no record action is specified.

Record

Record the values collected so far as a row in the return data. Non Filldown values are cleared. Note: No record will be output if there are any 'Required' values that are unassigned.

Clear

Clear non Filldown values.

Clearall

Clear all values.

  • NoRecord:默认的record action,不做任何操作
  • Record:很重要的一个动作,将当前的所提取的所有字段,打包成一个字典追加到返回值的列表中。其中Value的没标记Filldown的字段被清理掉。如果required的字段没有找到,此条record不会追加到返回列表中。
  • Clear:清理Value中定义的非Filldown的字段值
  • Clearall:清理所有的字段的值

Line和Record的action中间需要一个点,如果使用默认值则不需要这个点。

New State Transition

这三类我统称为Action,我觉得状态转移也是一个Action,或者你可以理解成状态之间只有一个action就是Transition。

转移的State必须是Start EOF 或者是我们自己定义的State。

rule与文本匹配,action是状态转移,则读取下一行文本进入下一个State的rules中从头开始逐个匹配。

Continue与状态转移互斥,这个是为了防止死循环。

State是FSM的一个概念,但是你可以简单理解是一种按情况讨论,分类讨论的思想,或者判断分支。

演示

Value INTERFACE ([\w+/]+)
Value VRF (\S+)
Value STATUS (up|down)
Value IP (\d+\.\d+\.\d+\.\d+)
Value SPEED (\S+)
Value MTU (\d+)
Value VLAN ([\d+--]+)
Value TYPE (\S+)
Value MODE (routed|access|trunk|pvlan|fabric)
Value REASON (\S+((\s\w+)+)?)
Value PORTCH (\S+)
Value DESCRIPTION (\S+((\s\w+)+)?)

Start
  ^Port\s+VRF\s+Status\s+IP\s+Address\s+Speed\s+MTU -> Management
  ^Interface\s+Ch\s+ -> Ethernet
  ^Interface\s+Status\s+Description -> Loopback
  ^Interface\s+Secondary\s+VLAN\(Type\)\s+Status\s+Reason -> VLAN

Management
  ^${INTERFACE}\s+${VRF}\s+${STATUS}\s+${IP}\s+${SPEED}\s+${MTU} -> Record
  ^Ethernet\s+VLAN\s+Type\s+Mode\s+Status\s+Reason\s+Speed\s+Port -> Start

Ethernet
  ^${INTERFACE}\s+${VLAN}\s+${TYPE}\s+${MODE}\s+${STATUS}\s+${REASON}\s+${SPEED}\s+${PORTCH} -> Record
  ^Interface\s+Status\s+Description -> Start

Loopback
  ^${INTERFACE}\s+${STATUS}\s+${DESCRIPTION} -> Record
  ^Interface\s+Secondary\s+VLAN\(Type\)\s+Status\s+Reason -> Start

VLAN
  ^${INTERFACE}\s+${TYPE}\s+${STATUS}\s+${REASON} -> Record

这是一个非常典型的状态转移的场景。根据表头特征的rule,将不同类型的端口放入了不同的state中,在每个state中精细匹配,然后记录下来,在适当的地方再通过状态转移返回到Start。Vlan一般在最后,进行记录后无需返回start。

总结

今天就讲这些,本文基于官方文档和ntc-templates的部分模板去讲解的,加入了个人的一些注解吧。应该是比你能看到中文的关于Textfsm的资料都会详细一些。但是限于篇幅,有些内容本次还是没讲清楚或者是讲明白,后续分享一些实战的模板编写,继续为大家深入了解Textfsm。对网络运维真的是非常有用的一款工具。

下期预告:可能是ansible 可能是textfsm相关。。。也可能划水~


举报

相关推荐

0 条评论