使用 Ansible 解析半结构化文本
cli_parse 模块将半结构化数据(如网络配置)解析为结构化数据,以允许对该设备的數據进行编程使用。您可以在一个剧本中从网络设备提取信息并更新 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 事实模块已经返回了所需的数据。
存在用于设备和资源配置管理的 Ansible 网络资源模块。
解析 CLI
cli_parse
模块包含以下 cli_parsing 插件
本机
内置于 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 自动化平台订阅。Red Hat Ansible 自动化平台订阅支持仅限于使用 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
模块在模板目录中查找模板,名称为{{ 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
事实。主要为了剧本一致性而提供 JSON 支持。
注意
使用 ansible.netcommon.json
在 Red Hat Ansible Automation Platform 订阅中得到完全支持
使用 ntc_templates 解析
python 库 ntc_templates
包含用于解析各种网络设备命令输出的预定义 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
,转换为 pyATS 的nxos
。或者,您也可以使用parser/os
选项设置操作系统。通过结合使用命令和操作系统,pyATS 选择以下解析器:https://pubhub.devnetcloud.com/media/genie-feature-browser/docs/#/parsers/show%2520interface。
cli_parse
模块将cisco.ios.ios
设置为 pyATS 的iosxe
。您可以使用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
选项提供操作系统。textfsm 模板名称默认设置为
templates/nxos_show_interface.textfsm
,它使用主机的ansible_network_os
和提供的command
。或者,您也可以使用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 模板语法很熟悉。
以下是一个示例 TTP 模板,存储为 templates/nxos_show_interface.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 自动化平台订阅支持仅限于使用文档中记录的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
提供与运行命令不同的解析器命令
使用 command
的子选项为 parser
配置解析器期望的命令,如果它与 cli_parse
运行的命令不同。
- 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
来解析在剧本中更早收集的文本。
# 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
另请参阅