To parse outputs of commands in Ansible you can use TEXTFSM. You need to install textfsm with pip.
TEXTFSM is a template based parser for commandline outputs.
Install of the required packages#
python -m virtualenv venv
source venv/bin/activate
pip install ansible ansible-pylibssh textfsm
Example: TEXTFSM parser file inventory_cisco_ios.textfsm#
There are already ready-made templates for many network manufacturers such as Cisco, I have the example here from networktocode/ntc-templates (Github), as a fork directly from me lanbugs/ntc-templates: TextFSM templates for parsing show commands of network devices (github .com) on Github.
Value NAME (.*)
Value DESCR (.*)
Value PID (([\S+]+|.*))
Value VID (.*)
Value SN ([\w+\d+]+)
Start
^NAME:\s+"${NAME}",\s+DESCR:\s+"${DESCR}"
^PID:\s+${PID}.*,.*VID:\s+${VID},.*SN:\s+${SN} -> Record
^PID:\s+,.*VID:\s+${VID},.*SN: -> Record
^PID:\s+${PID}.*,.*VID:\s+${VID},.*SN: -> Record
^PID:\s+,.*VID:\s+${VID},.*SN:\s+${SN} -> Record
^PID:\s+${PID}.*,.*VID:\s+${VID}.*
^PID:\s+,.*VID:\s+${VID}.*
^.*SN:\s+${SN} -> Record
^.*SN: -> Record
# Capture time-stamp if vty line has command time-stamping turned on
^Load\s+for\s+
^Time\s+source\s+is
Example: Ansible playbook collect_inventory.yml#
The module ansible.utils.cli_parse is used and as a parser ansible.utils.textfsm. The textfsm file that can parse the output is specified as template_path.
---
- name: Collect invenory of device
hosts: testswitch
gather_facts: False
vars:
ansible_connection: network_cli
ansible_network_os: ios
connection: network_cli
tasks:
- name: Collect inventory
ansible.utils.cli_parse:
command: show inventory
parser:
name: ansible.utils.textfsm
template_path: inventory_cisco_ios.textfsm
register: inv
- name: Debug
debug:
msg: "{{ inv }}"
```
##### Example: inventory.ini
```ìni
[devices]
testswitch ansible_host=10.1.1.1 ansible_user=cisco ansible_ssh_pass=cisco
```
##### Output
```
(venv) lab@labhost:~$ ansible-playbook -i inventory.ini collect_inventory.yml
PLAY [Collect invenory of device] ********************************************************************************************************************************************
TASK [Gather invenory] ***************************************************************************************************************************************************
ok: [testswitch]
TASK [Debug] *************************************************************************************************************************************************************
ok: [testswitch] => {
"msg": {
"changed": false,
"failed": false,
"parsed": [
{
"DESCR": "WS-C3560CG-8PC-S",
"NAME": "1",
"PID": "WS-C3560CG-8PC-S",
"SN": "FOC1234A0ZZ",
"VID": "V03 "
}
],
"stdout": "NAME: \"1\", DESCR: \"WS-C3560CG-8PC-S\"\nPID: WS-C3560CG-8PC-S , VID: V03 , SN: FOC1234A0ZZ",
"stdout_lines": [
"NAME: \"1\", DESCR: \"WS-C3560CG-8PC-S\"",
"PID: WS-C3560CG-8PC-S , VID: V03 , SN: FOC1234A0ZZ"
]
}
}
PLAY RECAP ***************************************************************************************************************************************************************
testswitch : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
```
In inv.parsed is now a list of dictionaries with the parsed data. In inv.stdout_lines are the originals of the return.
Now you could e.g. write that in a CSV file, here is an addition for the playbook:
```bash
- name: Add to report
lineinfile:
insertafter: EOF
dest: inv_report.txt
line: "'{{ inventory_hostname }}','{{ item.NAME|default('no name') }}','{{ item.DESCR|default('no descr') }}','{{ item.PID|default('no pid') }}','{{ item.SN|default('no sn') }}','{{ item.VID|default('no vid') }}'"
with_list: "{{ inv.parsed }}"
when: inv.failed is false
delegate_to: localhost
```