Ansible: Outputs parsen mit TEXTFSM (z.B. von Cisco)

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

Schreibe einen Kommentar

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.