使用 Ansible 解析半结构化文本
cli_parse 模块将半结构化数据(例如网络配置)解析为结构化数据,以便能够以编程方式使用来自该设备的数据。您可以在一个 playbook 中从网络设备提取信息并更新 CMDB。用例包括自动化故障排除、创建动态文档、更新 IPAM(IP 地址管理)工具等。
了解 CLI 解析器
ansible.utils 集合 1.0.0 或更高版本包含 cli_parse 模块,该模块可以运行 CLI 命令并解析半结构化文本输出。您可以在仅支持命令行界面且发出的命令返回半结构化文本的设备、主机或平台上使用 cli_parse
模块。 cli_parse
模块可以运行设备上的 CLI 命令并返回解析结果,也可以简单地解析任何文本文档。 cli_parse
模块包含 cli_parser 插件以与各种解析引擎交互。
为什么要解析文本?
将半结构化数据(例如网络配置)解析为结构化数据,以便能够以编程方式使用来自该设备的数据。用例包括自动化故障排除、创建动态文档、更新 IPAM(IP 地址管理)工具等。您可能更喜欢使用 Ansible 原生方式来利用 Ansible 原生结构,例如
when
子句,以有条件地运行其他任务或角色assert
模块,以检查配置和操作状态合规性template
模块,以生成有关配置和操作状态信息的报告模板和
command
或config
模块,以生成主机、设备或平台命令或配置当前平台
facts
模块,以补充原生事实信息
通过将半结构化文本解析为 Ansible 原生数据结构,您可以充分利用 Ansible 的网络模块和插件。
何时不解析文本
在以下情况下,您不应该解析半结构化文本:
设备、主机或平台具有 RESTAPI 并返回 JSON。
现有的 Ansible facts 模块已返回所需数据。
存在用于设备和资源配置管理的 Ansible 网络资源模块。
解析 CLI
cli_parse
模块包含以下 cli_parsing 插件:
native
内置于 Ansible 的原生解析引擎,不需要额外的 python 库
xml
将 XML 转换为 Ansible 原生数据结构
textfsm
一个 python 模块,它实现了一个基于模板的状态机,用于解析半格式化文本
ntc_templates
预定义的
textfsm
模板包,支持各种平台和命令ttp
一个使用模板进行半结构化文本解析的库,并增加了简化流程的功能
pyats
使用 Cisco 测试自动化和验证解决方案中包含的解析器
jc
一个 python 模块,它将数十个流行的 Linux/UNIX/macOS/Windows 命令和文件类型的输出转换为 python 字典或字典列表。注意:此过滤器插件可以在
community.general
集合中找到。json
将 CLI 中的 JSON 输出转换为 Ansible 原生数据结构
尽管 Ansible 包含许多可以将 XML 转换为 Ansible 原生数据结构的插件,但 cli_parse
模块在返回 XML 的设备上运行命令,并在单个任务中返回转换后的数据。
因为 cli_parse
使用基于插件的架构,所以它可以使用来自任何 Ansible 集合的其他解析引擎。
注意
ansible.netcommon.native
和 ansible.utils.json
解析引擎完全支持 Red Hat Ansible Automation Platform 订阅。Red Hat Ansible Automation Platform 订阅支持仅限于使用 ntc_templates
、pyATS、textfsm
、xmltodict
、公共 API,如文档中所述。
使用原生解析引擎解析
原生解析引擎包含在 cli_parse
模块中。它使用使用正则表达式捕获的数据来填充解析的数据结构。原生解析引擎需要一个 YAML 模板文件来解析命令输出。
网络示例
此示例使用网络设备命令的输出,并应用原生模板以生成 Ansible 结构化数据格式的输出。
来自网络设备的 show interface
命令输出如下所示:
Ethernet1/1 is up
admin state is up, Dedicated Interface
Hardware: 100/1000/10000 Ethernet, address: 5254.005a.f8bd (bia 5254.005a.f8bd)
MTU 1500 bytes, BW 1000000 Kbit, DLY 10 usec
reliability 255/255, txload 1/255, rxload 1/255
Encapsulation ARPA, medium is broadcast
Port mode is access
full-duplex, auto-speed
Beacon is turned off
Auto-Negotiation is turned on FEC mode is Auto
Input flow-control is off, output flow-control is off
Auto-mdix is turned off
Switchport monitor is off
EtherType is 0x8100
EEE (efficient-ethernet) : n/a
Last link flapped 4week(s) 6day(s)
Last clearing of "show interface" counters never
<...>
创建原生模板以匹配此输出,并将其存储为 templates/nxos_show_interface.yaml
---
- example: Ethernet1/1 is up
getval: '(?P<name>\S+) is (?P<oper_state>\S+)'
result:
"{{ name }}":
name: "{{ name }}"
state:
operating: "{{ oper_state }}"
shared: true
- example: admin state is up, Dedicated Interface
getval: 'admin state is (?P<admin_state>\S+),'
result:
"{{ name }}":
name: "{{ name }}"
state:
admin: "{{ admin_state }}"
- example: " Hardware: Ethernet, address: 5254.005a.f8b5 (bia 5254.005a.f8b5)"
getval: '\s+Hardware: (?P<hardware>.*), address: (?P<mac>\S+)'
result:
"{{ name }}":
hardware: "{{ hardware }}"
mac_address: "{{ mac }}"
此原生解析器模板的结构为解析器列表,每个解析器包含以下键值对:
example
- 要解析的文本行的示例行getval
- 使用命名捕获组的正则表达式来存储提取的数据result
- 从解析的数据中填充为模板的数据树shared
- (可选)共享键使解析的值可用于其余的解析器条目,直到再次匹配。
以下示例任务使用 cli_parse
与原生解析器和上面的示例模板来解析来自 Cisco NXOS 设备的 show interface
命令:
- name: "Run command and parse with native"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.netcommon.native
set_fact: interfaces
深入探讨此任务
command
选项提供您想要在设备或主机上运行的命令。或者,您可以使用text
选项提供来自先前命令的文本。parser
选项提供特定于解析引擎的信息。name
子选项提供解析引擎的完全限定集合名称 (FQCN)(ansible.netcommon.native
)。默认情况下,
cli_parse
模块会在 templates 目录中查找名为{{ short_os }}_{{ command }}.yaml
的模板。模板文件名中的
short_os
来自主机ansible_network_os
或ansible_distribution
。网络或主机命令中的空格会在模板文件名
command
部分替换为_
。例如,网络 CLI 命令show interfaces
在文件名中变为show_interfaces
。
注意
ansible.netcommon.native
解析引擎在订阅 Red Hat Ansible Automation Platform 后得到完全支持。
最后,在此任务中,set_fact
选项根据 cli_parse
返回的结构化数据,为设备设置以下 interfaces
事实。
Ethernet1/1:
hardware: 100/1000/10000 Ethernet
mac_address: 5254.005a.f8bd
name: Ethernet1/1
state:
admin: up
operating: up
Ethernet1/10:
hardware: 100/1000/10000 Ethernet
mac_address: 5254.005a.f8c6
<...>
Linux 示例
您还可以使用原生解析器运行命令并解析来自 Linux 主机的输出。
Linux 命令 (ip addr show
) 的示例输出如下所示
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: enp0s31f6: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000
link/ether x2:6a:64:9d:84:19 brd ff:ff:ff:ff:ff:ff
3: wlp2s0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether x6:c2:44:f7:41:e0 brd ff:ff:ff:ff:ff:ff permaddr d8:f2:ca:99:5c:82
创建与该输出匹配的原生模板,并将其存储为 templates/fedora_ip_addr_show.yaml
。
---
- example: '1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000'
getval: |
(?x) # free-spacing
\d+:\s # the interface index
(?P<name>\S+):\s # the name
<(?P<properties>\S+)> # the properties
\smtu\s(?P<mtu>\d+) # the mtu
.* # gunk
state\s(?P<state>\S+) # the state of the interface
result:
"{{ name }}":
name: "{{ name }}"
loopback: "{{ 'LOOPBACK' in stats.split(',') }}"
up: "{{ 'UP' in properties.split(',') }}"
carrier: "{{ not 'NO-CARRIER' in properties.split(',') }}"
broadcast: "{{ 'BROADCAST' in properties.split(',') }}"
multicast: "{{ 'MULTICAST' in properties.split(',') }}"
state: "{{ state|lower() }}"
mtu: "{{ mtu }}"
shared: True
- example: 'inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0'
getval: |
(?x) # free-spacing
\s+inet\s(?P<inet>([0-9]{1,3}\.){3}[0-9]{1,3}) # the ip address
/(?P<bits>\d{1,2}) # the mask bits
result:
"{{ name }}":
ip_address: "{{ inet }}"
mask_bits: "{{ bits }}"
注意
解析器模板中的 shared
键允许在后续解析器条目中使用接口名称。使用示例和正则表达式的自由空格模式使模板更易于阅读。
以下示例任务使用 cli_parse
和上述原生解析器以及示例模板来解析 Linux 输出。
- name: Run command and parse
ansible.utils.cli_parse:
command: ip addr show
parser:
name: ansible.netcommon.native
set_fact: interfaces
此任务假设您之前已收集事实以确定查找模板所需的 ansible_distribution
。或者,您可以在 parser/template_path
选项中提供路径。
最后,在此任务中,set_fact
选项根据 cli_parse
返回的结构化数据,为主机设置以下 interfaces
事实。
lo:
broadcast: false
carrier: true
ip_address: 127.0.0.1
mask_bits: 8
mtu: 65536
multicast: false
name: lo
state: unknown
up: true
enp64s0u1:
broadcast: true
carrier: true
ip_address: 192.168.86.83
mask_bits: 24
mtu: 1500
multicast: true
name: enp64s0u1
state: up
up: true
<...>
解析 JSON
尽管 Ansible 会在识别到序列化 JSON 时将其本地转换为 Ansible 本地数据,但您也可以使用 cli_parse
模块进行此转换。
示例任务
- name: "Run command and parse as json"
ansible.utils.cli_parse:
command: show interface | json
parser:
name: ansible.utils.json
register: interfaces
深入探讨此任务
在设备上发出
show interface | json
命令。输出被设置为设备的
interfaces
事实。主要出于 playbook 一致性考虑而提供 JSON 支持。
注意
在订阅 Red Hat Ansible Automation Platform 后,ansible.netcommon.json
得到完全支持。
使用 ntc_templates 解析
ntc_templates
Python 库包含用于解析各种网络设备命令输出的预定义 textfsm
模板。
示例任务
- name: "Run command and parse with ntc_templates"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.netcommon.ntc_templates
set_fact: interfaces
深入探讨此任务
设备的
ansible_network_os
将转换为 ntc_template 格式cisco_nxos
。或者,您可以使用parser/os
选项提供os
。包含在
ntc_templates
包中的cisco_nxos_show_interface.textfsm
模板解析输出。有关
ntc_templates
Python 库的更多信息,请参阅 ntc_templates 自述文件。
注意
Red Hat Ansible Automation Platform 订阅支持仅限于使用如文档中所述的 ntc_templates
公共 API。
此任务和预定义模板将以下事实设置为主机的 interfaces
事实。
interfaces:
- address: 5254.005a.f8b5
admin_state: up
bandwidth: 1000000 Kbit
bia: 5254.005a.f8b5
delay: 10 usec
description: ''
duplex: full-duplex
encapsulation: ARPA
hardware_type: Ethernet
input_errors: ''
input_packets: ''
interface: mgmt0
ip_address: 192.168.101.14/24
last_link_flapped: ''
link_status: up
mode: ''
mtu: '1500'
output_errors: ''
output_packets: ''
speed: 1000 Mb/s
- address: 5254.005a.f8bd
admin_state: up
bandwidth: 1000000 Kbit
bia: 5254.005a.f8bd
delay: 10 usec
使用 pyATS 解析
pyATS
是 Cisco 测试自动化和验证解决方案的一部分。它包含许多针对多个网络平台和命令的预定义解析器。您可以将 pyATS
包中包含的预定义解析器与 cli_parse
模块一起使用。
示例任务
- name: "Run command and parse with pyats"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.netcommon.pyats
set_fact: interfaces
深入探讨此任务
cli_parse
模块会自动转换ansible_network_os
(在此示例中,ansible_network_os
设置为cisco.nxos.nxos
,转换为nxos
以用于 pyATS)。或者,您可以使用parser/os
选项设置 OS。通过结合使用命令和 OS,pyATS 选择以下解析器:https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/parsers/show%2520interface。
cli_parse
模块将cisco.ios.ios
设置为iosxe
以用于 pyATS。您可以使用parser/os
选项覆盖此设置。cli_parse
仅使用 pyATS 中的预定义解析器。请参阅 pyATS 文档 和 pyATS 包含的解析器的完整列表。
注意
Red Hat Ansible Automation Platform 订阅支持仅限于使用如文档中所述的 pyATS 公共 API。
此任务将以下事实设置为主机的 interfaces
事实。
mgmt0:
admin_state: up
auto_mdix: 'off'
auto_negotiate: true
bandwidth: 1000000
counters:
in_broadcast_pkts: 3
in_multicast_pkts: 1652395
in_octets: 556155103
in_pkts: 2236713
in_unicast_pkts: 584259
rate:
in_rate: 320
in_rate_pkts: 0
load_interval: 1
out_rate: 48
out_rate_pkts: 0
rx: true
tx: true
delay: 10
duplex_mode: full
enabled: true
encapsulations:
encapsulation: arpa
ethertype: '0x0000'
ipv4:
192.168.101.14/24:
ip: 192.168.101.14
prefix_length: '24'
link_state: up
<...>
使用 textfsm 解析
textfsm
是一个 Python 模块,它实现了一个基于模板的状态机,用于解析半格式化文本。
以下示例 textfsm
模板存储为 templates/nxos_show_interface.textfsm
。
Value Required INTERFACE (\S+)
Value LINK_STATUS (.+?)
Value ADMIN_STATE (.+?)
Value HARDWARE_TYPE (.\*)
Value ADDRESS ([a-zA-Z0-9]+.[a-zA-Z0-9]+.[a-zA-Z0-9]+)
Value BIA ([a-zA-Z0-9]+.[a-zA-Z0-9]+.[a-zA-Z0-9]+)
Value DESCRIPTION (.\*)
Value IP_ADDRESS (\d+\.\d+\.\d+\.\d+\/\d+)
Value MTU (\d+)
Value MODE (\S+)
Value DUPLEX (.+duplex?)
Value SPEED (.+?)
Value INPUT_PACKETS (\d+)
Value OUTPUT_PACKETS (\d+)
Value INPUT_ERRORS (\d+)
Value OUTPUT_ERRORS (\d+)
Value BANDWIDTH (\d+\s+\w+)
Value DELAY (\d+\s+\w+)
Value ENCAPSULATION (\w+)
Value LAST_LINK_FLAPPED (.+?)
Start
^\S+\s+is.+ -> Continue.Record
^${INTERFACE}\s+is\s+${LINK_STATUS},\sline\sprotocol\sis\s${ADMIN_STATE}$$
^${INTERFACE}\s+is\s+${LINK_STATUS}$$
^admin\s+state\s+is\s+${ADMIN_STATE},
^\s+Hardware(:|\s+is)\s+${HARDWARE_TYPE},\s+address(:|\s+is)\s+${ADDRESS}(.*bia\s+${BIA})*
^\s+Description:\s+${DESCRIPTION}
^\s+Internet\s+Address\s+is\s+${IP_ADDRESS}
^\s+Port\s+mode\s+is\s+${MODE}
^\s+${DUPLEX}, ${SPEED}(,|$$)
^\s+MTU\s+${MTU}.\*BW\s+${BANDWIDTH}.\*DLY\s+${DELAY}
^\s+Encapsulation\s+${ENCAPSULATION}
^\s+${INPUT_PACKETS}\s+input\s+packets\s+\d+\s+bytes\s\*$$
^\s+${INPUT_ERRORS}\s+input\s+error\s+\d+\s+short\s+frame\s+\d+\s+overrun\s+\d+\s+underrun\s+\d+\s+ignored\s\*$$
^\s+${OUTPUT_PACKETS}\s+output\s+packets\s+\d+\s+bytes\s\*$$
^\s+${OUTPUT_ERRORS}\s+output\s+error\s+\d+\s+collision\s+\d+\s+deferred\s+\d+\s+late\s+collision\s\*$$
^\s+Last\s+link\s+flapped\s+${LAST_LINK_FLAPPED}\s\*$$
以下任务使用示例模板将 textfsm
与 cli_parse
模块一起使用。
- name: "Run command and parse with textfsm"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.utils.textfsm
set_fact: interfaces
深入探讨此任务
设备的
ansible_network_os
(cisco.nxos.nxos
) 将转换为nxos
。或者,您可以使用parser/os
选项提供 OS。textfsm 模板名称默认为
templates/nxos_show_interface.textfsm
,它使用 OS 和运行的命令的组合生成。或者,您可以使用parser/template_path
选项覆盖生成的模板路径。有关详细信息,请参阅 textfsm 自述文件。
textfsm
以前可用作过滤器插件。Ansible 用户应迁移到cli_parse
模块。
注意
Red Hat Ansible Automation Platform 订阅支持仅限于使用如文档中所述的 textfsm
公共 API。
此任务将以下事实设置为主机的 interfaces
事实。
- ADDRESS: X254.005a.f8b5
ADMIN_STATE: up
BANDWIDTH: 1000000 Kbit
BIA: X254.005a.f8b5
DELAY: 10 usec
DESCRIPTION: ''
DUPLEX: full-duplex
ENCAPSULATION: ARPA
HARDWARE_TYPE: Ethernet
INPUT_ERRORS: ''
INPUT_PACKETS: ''
INTERFACE: mgmt0
IP_ADDRESS: 192.168.101.14/24
LAST_LINK_FLAPPED: ''
LINK_STATUS: up
MODE: ''
MTU: '1500'
OUTPUT_ERRORS: ''
OUTPUT_PACKETS: ''
SPEED: 1000 Mb/s
- ADDRESS: X254.005a.f8bd
ADMIN_STATE: up
BANDWIDTH: 1000000 Kbit
BIA: X254.005a.f8bd
使用 TTP 解析
TTP 是一个 Python 库,用于使用模板解析半结构化文本。TTP 使用类似 jinja 的语法来减少对正则表达式的需求。熟悉 jinja 模板的用户可能会发现 TTP 模板语法很熟悉。
以下是以 templates/nxos_show_interface.ttp
存储的 TTP 模板示例。
{{ interface }} is {{ state }}
admin state is {{ admin_state }}{{ ignore(".\*") }}
以下任务使用此模板解析 show interface
命令输出。
- name: "Run command and parse with ttp"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.utils.ttp
set_fact: interfaces
深入研究此任务
默认模板路径
templates/nxos_show_interface.ttp
是使用主机的ansible_network_os
和提供的command
生成的。TTP 支持几个将传递给解析器的其他变量。这些包括
parser/vars/ttp_init
- 初始化解析器时传递的其他参数。parser/vars/ttp_results
- 用于影响解析器输出的其他参数。parser/vars/ttp_vars
- 在模板中可用的其他变量。
有关详细信息,请参阅 TTP 文档。
该任务将以下事实设置为主机的 interfaces
事实。
- admin_state: up,
interface: mgmt0
state: up
- admin_state: up,
interface: Ethernet1/1
state: up
- admin_state: up,
interface: Ethernet1/2
state: up
使用 JC 解析
JC 是一个 Python 库,用于将数十种常见 Linux/UNIX/macOS/Windows 命令行工具和文件类型的输出转换为 Python 字典或字典列表,以便更容易地解析。JC 可作为 community.general
集合中的过滤器插件使用。
以下是一个使用 JC 解析 dig
命令输出的示例。
- name: "Run dig command and parse with jc"
hosts: ubuntu
tasks:
- shell: dig example.com
register: result
- set_fact:
myvar: "{{ result.stdout | community.general.jc('dig') }}"
- debug:
msg: "The IP is: {{ myvar[0].answer[0].data }}"
转换 XML
尽管 Ansible 包含许多可以将 XML 转换为 Ansible 原生数据结构的插件,但 cli_parse
模块在返回 XML 的设备上运行命令,并在单个任务中返回转换后的数据。
此示例任务运行 show interface
命令并将输出解析为 XML。
- name: "Run command and parse as xml"
ansible.utils.cli_parse:
command: show interface | xml
parser:
name: ansible.utils.xml
set_fact: interfaces
注意
Red Hat Ansible Automation Platform 订阅支持仅限于使用如文档中所述的 xmltodict
公共 API。
此任务根据返回的输出设置主机上的 interfaces
事实。
nf:rpc-reply:
'@xmlns': http://www.cisco.com/nxos:1.0:if_manager
'@xmlns:nf': urn:ietf:params:xml:ns:netconf:base:1.0
nf:data:
show:
interface:
__XML__OPT_Cmd_show_interface_quick:
__XML__OPT_Cmd_show_interface___readonly__:
__readonly__:
TABLE_interface:
ROW_interface:
- admin_state: up
encapsulation: ARPA
eth_autoneg: 'on'
eth_bia_addr: x254.005a.f8b5
eth_bw: '1000000'
高级用例
cli_parse
模块支持多个功能以支持更复杂的用例。
提供完整的模板路径
使用 template_path
选项覆盖任务中的默认模板路径。
- name: "Run command and parse with native"
ansible.utils.cli_parse:
command: show interface
parser:
name: ansible.netcommon.native
template_path: /home/user/templates/filename.yaml
提供与运行的命令不同的解析器命令
如果解析器期望的命令与 cli_parse
运行的命令不同,则使用 parser
的 command
子选项配置该命令。
- name: "Run command and parse with native"
ansible.utils.cli_parse:
command: sho int
parser:
name: ansible.netcommon.native
command: show interface
提供自定义操作系统值
使用解析器的 os
子选项直接设置操作系统,而不是使用 ansible_network_os
或 ansible_distribution
来生成模板路径或使用指定的解析器引擎。
- name: Use ios instead of iosxe for pyats
ansible.utils.cli_parse:
command: show something
parser:
name: ansible.netcommon.pyats
os: ios
- name: Use linux instead of fedora from ansible_distribution
ansible.utils.cli_parse:
command: ps -ef
parser:
name: ansible.netcommon.native
os: linux
解析现有文本
使用 text
选项而不是 command
来解析在 playbook 中较早收集的文本。
# using /home/user/templates/filename.yaml
- name: "Parse text from previous task"
ansible.utils.cli_parse:
text: "{{ output['stdout'] }}"
parser:
name: ansible.netcommon.native
template_path: /home/user/templates/filename.yaml
# using /home/user/templates/filename.yaml
- name: "Parse text from file"
ansible.utils.cli_parse:
text: "{{ lookup('file', 'path/to/file.txt') }}"
parser:
name: ansible.netcommon.native
template_path: /home/user/templates/filename.yaml
# using templates/nxos_show_version.yaml
- name: "Parse text from previous task"
ansible.utils.cli_parse:
text: "{{ sho_version['stdout'] }}"
parser:
name: ansible.netcommon.native
os: nxos
command: show version
另请参阅