前两天做了一个 Jinja2 的小笔记《使用 Jinja2 模版生成网络设备配置文件》,又发现一个 Jinja2 的“反向”工具 TTP - Text Template Parser。它和 Jinja2 的作用相反,是通过包含变量的模版从文本文件中提取 Key-Value 并生成结构化的数据。
其实,可以提供类似功能的工具有很多,Ansible 已经帮我们找出来了:
最直接的方法就是用正则表达式。但是对于传统的网工,写正则表达式是一种折磨。
最简单的方法是直接用 json 或者 xml 输出结果。但并不是所有的网络设备都能像 Cisco 的 NXOS 那么友好,也不是所有的网络设备都能提供完备的 API。
pyATS 虽然是 Cisco 写的,支持 IOS/IOSXE/IOSXR/NXOS/ASA,但 Cisco 的产品线显然并不只有这些。而且,友商咋办呢?
至于 ntc_template,TextFSM,还是要大量依赖正则表达式。前些天我写了一个程序自动化监控 Cisco WLC 无线控制器上的 AP 数据,发现 TextFSM 太难用了。我写两个正则表达式直接处理就完事了,反正就 2 个,何苦浪费时间去写专门的 TextFSM 模版呢。
但 TTP 可以很好滴帮助我偷懒。只有特殊情况才需要写表达式,多数情况下直接定义变量就行了。
下面是 WLC 的 show ap summary 的输出结果,我只选出 3 个有代表性的 AP 信息:
AP Summary
Number of APs.................................... 358
Global AP User Name.............................. Not Configured
Global AP Dot1x User Name........................ Not Configured
AP Name Slots AP Model Ethernet MAC Location Country IP Address Clients DSE Location
------------------------------ ----- -------------------- ----------------- -------------------- ---------- --------------- ------- --------------
SZ-S-30F08 2 AIR-AP3802I-H-K9 68:3b:78:8f:7e:ea default location CN 10.80.5.42 2 [0 ,0 ,0 ]
SZ-S-33F17 2 AIR-AP3802I-H-K9 24:e9:b3:ec:82:93 XXX-6F-JLS CN 10.80.5.112 0 [0 ,0 ,0 ]
SZ-S-29F17 2 AIR-AP3802I-H-K9 f8:c2:88:41:33:88 SZ-XXX-5 meeting room CN 10.80.4.72 3 [0 ,0 ,0 ]
这份 ap summary 有几个特点:
分成了两部分:上半部分是单纯的文本 line;下半部分是 table
下半部分 table 的每一列的宽度大致是固定的,对齐的
Location 列的数据比较复杂,有不带空格的,有 1 个空格的,还有 2 个空格的
DSE Location 也比较复杂,是一个 list
但使用 TTP 就很简单。我们把列名原封不动地复制到一个空白的文本文件中。不要删除列名之间的空格,因为 TTP 需要用这些空格来判断列宽。对于有空格的列名,例如 “AP Name”,可以用下划线替换中间的空格,改写成 “AP_Name”。然后在后面加上 {{ _headers_ }},就完成了我们的模版。
AP_Name Slots AP_Model Ethernet_MAC Location Country IP_Address Clients DSE_Location {{ _headers_ }}
接下来就是从源数据中提取 Key-Value 了。当然可以写一个 Python,但我们做 demo,先简单一点,直接在 terminal 运行 ttp,先输出一个 json:
-d:源数据文件
-t:模版文件
-o:输出格式
% ttp -d ./show_ap_summary.txt -t ./ap_sum.ttp.txt -o json 21-03-08 - 22:28:14
[
[
[
{
"AP_Model": "--------------------",
"AP_Name": "------------------------------",
"Clients": "-------",
"Country": "----------",
"DSE_Location": "--------------",
"Ethernet_MAC": "-----------------",
"IP_Address": "---------------",
"Location": "--------------------",
"Slots": "-----"
},
{
"AP_Model": "AIR-AP3802I-H-K9",
"AP_Name": "SZ-S-30F08",
"Clients": "2",
"Country": "CN",
"DSE_Location": "[0 ,0 ,0 ]",
"Ethernet_MAC": "68:3b:78:8f:7e:ea",
"IP_Address": "10.80.5.42",
"Location": "default location",
"Slots": "2"
},
{
"AP_Model": "AIR-AP3802I-H-K9",
"AP_Name": "SZ-S-33F17",
"Clients": "0",
"Country": "CN",
"DSE_Location": "[0 ,0 ,0 ]",
"Ethernet_MAC": "24:e9:b3:ec:82:93",
"IP_Address": "10.80.5.112",
"Location": "XXX-6F-JLS",
"Slots": "2"
},
{
"AP_Model": "AIR-AP3802I-H-K9",
"AP_Name": "SZ-S-29F17",
"Clients": "3",
"Country": "CN",
"DSE_Location": "[0 ,0 ,0 ]",
"Ethernet_MAC": "f8:c2:88:41:33:88",
"IP_Address": "10.80.4.72",
"Location": "SZ-XXX-5 meeting room",
"Slots": "2"
}
]
]
]
怎样?是不是超级简单?
但还不完美,因为它把 Header 下面用来分割的横线作为 Value 给提取出来了。因为 _headers_ 方法仅比较宽度,而不考虑数据的内容。我们换另外一种方式写模版。
首先处理复杂的 Location 列和 DSE Location 列。TTP 内置了很多 Regex Patterns,其中一个是 “ORPHRASE”,可以匹配连续的多个 word,只要这些 word 中间有且只有 1 个空格。所以在变量之后加上 “| ORPHRASE”,就可以匹配上面的 3 种 location,也可以匹配 DSE 列表。
然后,我们再增加一个匹配。TTP 内置的 Regex Patterns 还包括 MAC 地址,IP 地址,IPv6 地址,prefix 等等。其实我们只需要为 1 个变量增加 Regex Pattern 就可以,但是 demo 嘛,咱就多加几个:
{{ ap_name }} {{ slots | DIGIT }} {{ ap_model }} {{ eth_mac | MAC }} {{ location | ORPHRASE }} {{ country }} {{ ip_addr | IP }} {{ clients | DIGIT }} {{ dse_loc | ORPHRASE }}
然后再一次运行 ttp,这一次输出 YAML 格式:
% ttp -d ./show_ap_summary.txt -t ./ap_sum.ttp2.txt -o yaml 21-03-08 - 22:44:22
- - - ap_model: AIR-AP3802I-H-K9
ap_name: SZ-S-30F08
clients: '2'
country: CN
dse_loc: '[0 ,0 ,0 ]'
eth_mac: 68:3b:78:8f:7e:ea
ip_addr: 10.80.5.42
location: default location
slots: '2'
- ap_model: AIR-AP3802I-H-K9
ap_name: SZ-S-33F17
clients: '0'
country: CN
dse_loc: '[0 ,0 ,0 ]'
eth_mac: 24:e9:b3:ec:82:93
ip_addr: 10.80.5.112
location: XXX-6F-JLS
slots: '2'
- ap_model: AIR-AP3802I-H-K9
ap_name: SZ-S-29F17
clients: '3'
country: CN
dse_loc: '[0 ,0 ,0 ]'
eth_mac: f8:c2:88:41:33:88
ip_addr: 10.80.4.72
location: SZ-XXX-5 meeting room
slots: '2'
今天先到这儿,改天继续。