Um Outputs von Kommandos z.B. von Cisco zu parsen in Ansible kann man TEXTFSM verwenden. Dazu muss neben Ansible das textfsm Modul mit pip installiert werden.
TEXTFSM ist ein Template basierender Parser für Outputs.
Installation der benötigten Pakete:
python -m virtualenv venv source venv/bin/activate pip install ansible ansible-pylibssh textfsm
Beispiel TEXTFSM Parser Datei inventory_cisco_ios.textfsm:
Für viele Netzwerk Hersteller wie z.B. Cisco gibt es bereits fertige Templates, ich habe das Beispiel hier von networktocode/ntc-templates (Github), als Fork direkt bei mir lanbugs/ntc-templates: TextFSM templates for parsing show commands of network devices (github.com) in 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
Beispiel Ansible-Playbook collect_inventory.yml:
Verwendet wird das Modul ansible.utils.cli_parse und als parser ansible.utils.textfsm. Als template_path wird die textfsm Datei angegeben welche den Output parsen kann.
--- - 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 }}"
Beispiel Ansible Inventory inventory.ini:
[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 ist jetzt eine Liste von Dictionarys mit den geparsten Daten. In inv.stdout_lines sind die originalen der Rückgabe.
Jetzt könnte man z.B. das in ein CSV File schreiben, hier eine Ergänzung für das Playbook:
- 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