TextFSM List & Continue.Record

# List & Continue.Record

Introduce List and Continue.Record fuction

> Keyword List: 表示对应的字段获取的值附加到列表中 
Action Continue: 当前行仍然去匹配下一条 rule
Action Record: 返回之前获取的值
Action Continue.Record: 表示当前行去匹配下一条规则并且返回之前获取的值
> 

## RAW TEXT

```
R1#sh ip route ospf
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       + - replicated route, % - next hop override

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 10 subnets, 2 masks
O        10.1.1.0/24 [110/20] via 10.0.12.2, 1w2d, Ethernet0/1
O        10.2.2.0/24 [110/20] via 10.0.13.3, 1w2d, Ethernet0/2
O        10.3.3.3/32 [110/11] via 10.0.12.2, 1w2d, Ethernet0/1
O        10.4.4.4/32 [110/11] via 10.0.13.3, 1w2d, Ethernet0/2
                     [120/12] via 10.0.14.4, 1w2d, Ethernet0/3 
                     # 这里 120/12 只是演示, 实际设备上结果是 110/11
O        10.5.5.5/32 [110/21] via 10.0.13.3, 1w2d, Ethernet0/2
                     [110/21] via 10.0.12.2, 1w2d, Ethernet0/1
                     [110/21] via 10.0.14.4, 1w2d, Ethernet0/3
O        10.6.6.0/24 [110/20] via 10.0.13.3, 1w2d, Ethernet0/2
```

需要将路由的相关信息提取出来

## TextFSM Template

```jsx
Value network (\S+)
Value mask (\d+)
Value distance (\d+)
Value metric (\d+)
Value List nexthop (\S+)

Start
  ^O -> Continue.Record # rule 1
  ^O +${network}/${mask}\s\[${distance}/${metric}\]\svia\s${nexthop}, # rule 2
  ^\s+\[${distance}/${metric}\]\svia\s${nexthop}, # rule 3
```

## Match Order

从实际的路由 `O` 开始匹配, 前面的内容全部忽略

1. 文本移动到第 1 行

`O        10.1.1.0/24 [110/20] via 10.0.12.2, 1w2d, Ethernet0/1`

- rule 1: 没有定义任何 Value, 因此 Record 没有任何值可以返回, 同时 Continue 第 1 行继续匹配 rule 2
- rule 2: 这条 rule 定义了 value, 提取出对应的 value, 然后匹配默认的 Action Next, 即文本移动到第 2 行
- rule 3: 不会匹配上

2. 文本移动到第 2 行

`O        10.2.2.0/24 [110/20] via 10.0.13.3, 1w2d, Ethernet0/2`

- rule 1：没有定义任何 Value, 但是此时存储了第1 行内容获取的 value, 因此 record 有值可以返回(返回第 1 行匹配的值)
- rule 2: 处理逻辑同上
- rule 3: 不会匹配上

3. 文本移动到第 3 行， 处理逻辑同上, 返回第 2 行匹配的值

4. 文本移动到第 4 行，处理逻辑同上, 返回第 3 行匹配的值

`O        10.4.4.4/32 [110/11] via 10.0.13.3, 1w2d, Ethernet0/2`

此时记录的字典的值

```json
{
	'network': '10.4.4.4',
    'mask': '32',
	'distance': '110',
  	'metric': '11',
  	'nexthop': ['10.0.13.3'] # nexthop 定义为 List 类型
}
```

5. 文本移动到第 5 行

- rule 1: 无法匹配
- rule 2: 无法匹配
- rule 3: 可以匹配并提取出对应的值, 此时字典的值如下：

```json
{
	'network': '10.4.4.4',
	'mask': '32', 
	'distance': '120', # 被新的值覆盖
  	'metric': '12', # 被新的值覆盖
  	'nexthop': ['10.0.13.3', '10.0.14.4'] # 新的 nexthop append 到 list 
}
```

6. 文本移动到 6 行

- rule 1: 没有定义任何 Value, 因此 Record 返回第 4 行和第 5 行提取的值
- rule 2: 逻辑同上
- rule 3: 不会被匹配到

7. 后面几行按相同的方式继续处理

### Result

```
network      mask    distance    metric  nexthop
---------  ------  ----------  --------  ---------------------------------------
10.1.1.0       24         110        20  ['10.0.12.2']
10.2.2.0       24         110        20  ['10.0.13.3']
10.3.3.3       32         110        11  ['10.0.12.2']
10.4.4.4       32         120        12  ['10.0.13.3', '10.0.14.4']
10.5.5.5       32         110        21  ['10.0.13.3', '10.0.12.2', '10.0.14.4']
10.6.6.0       24         110        20  ['10.0.13.3']
```

可以看到提取的结果，按照正确的 subnet 和 nexthop 进行提取

## Python Code

```python
from pprint import pprint
from textfsm import TextFSM
from tabulate import tabulate

output = """
R1#sh ip route ospf
Codes: L - local, C - connected, S - static, R - RIP, M - mobile, B - BGP
       D - EIGRP, EX - EIGRP external, O - OSPF, IA - OSPF inter area
       N1 - OSPF NSSA external type 1, N2 - OSPF NSSA external type 2
       E1 - OSPF external type 1, E2 - OSPF external type 2
       i - IS-IS, su - IS-IS summary, L1 - IS-IS level-1, L2 - IS-IS level-2
       ia - IS-IS inter area, * - candidate default, U - per-user static route
       o - ODR, P - periodic downloaded static route, H - NHRP, l - LISP
       + - replicated route, % - next hop override

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 10 subnets, 2 masks
O        10.1.1.0/24 [110/20] via 10.0.12.2, 1w2d, Ethernet0/1
O        10.2.2.0/24 [110/20] via 10.0.13.3, 1w2d, Ethernet0/2
O        10.3.3.3/32 [110/11] via 10.0.12.2, 1w2d, Ethernet0/1
O        10.4.4.4/32 [110/11] via 10.0.13.3, 1w2d, Ethernet0/2
                     [120/12] via 10.0.14.4, 1w2d, Ethernet0/3
O        10.5.5.5/32 [110/21] via 10.0.13.3, 1w2d, Ethernet0/2
                     [110/21] via 10.0.12.2, 1w2d, Ethernet0/1
                     [110/21] via 10.0.14.4, 1w2d, Ethernet0/3
O        10.6.6.0/24 [110/20] via 10.0.13.3, 1w2d, Ethernet0/2
"""

f = open('show_ip_route.textfsm')
template = TextFSM(f)
# print(template.header)
# pprint(template.ParseText(output))  # type list
# pprint(template.ParseTextToDicts(output))  # type dict
print(tabulate(template.ParseText(output), headers=template.header))
```