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 这个是原文。翻译下:

  • 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是指匹配到现在的信息提取出的一条记录(一个字典)。

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相关。。。也可能划水~

欢迎点赞、分享、在看、收藏、推荐。

欢迎关注本公众号:NetDevOps加油站

欢迎关注知乎专栏:NetDevOps加油站

也有同名头条号。。。