1

I've been struggling with this for a while. I have a list of dict like this which is created by parsing the Cisco CLI output with neighbors_raw.stdout[0] | ansible.netcommon.parse_cli_textfsm('templates/cisco_ios_show_cdp_neighbors.textfsm'):

[
        {
            "CAPABILITY": "R S I",
            "LOCAL_INTERFACE": "Ten 1/1/1",
            "NEIGHBOR": "switch1.mydomain.com",
            "NEIGHBOR_INTERFACE": "Ten 1/0/4",
            "PLATFORM": "C9500-16X"
        },
        {
            "CAPABILITY": "S I",
            "LOCAL_INTERFACE": "Gig 1/1/2",
            "NEIGHBOR": "switch2.mydomain.com",
            "NEIGHBOR_INTERFACE": "Ten 1/1/1",
            "PLATFORM": "C9200L-48"
        }
]

I want to convert the interface names from Ten 1/1/1 to Te1/1/1 or Gig 1/1/2 to Gi1/1/2.

I have tried the following:

- name: PARSE NEIGHBORS
  debug:
    msg: "{{ neighbors_raw.stdout[0] | ansible.netcommon.parse_cli_textfsm('templates/cisco_ios_show_cdp_neighbors.textfsm') |
      regex_replace('Ten ', 'Te') |
      regex_replace('Gig ', 'Gi') }}"

The output looks correct:

[
        {
            "CAPABILITY": "R S I",
            "LOCAL_INTERFACE": "Te1/1/1",
            "NEIGHBOR": "switch1.mydomain.com",
            "NEIGHBOR_INTERFACE": "Te1/0/4",
            "PLATFORM": "C9500-16X"
        },
        {
            "CAPABILITY": "S I",
            "LOCAL_INTERFACE": "Gi1/1/2",
            "NEIGHBOR": "switch2.mydomain.com",
            "NEIGHBOR_INTERFACE": "Te1/1/1",
            "PLATFORM": "C9200L-48"
        }
]

However, if I pipe this output into any other command it treats it like it is a character array:

- name: PARSE NEIGHBORS
  debug:
    msg: "{{ neighbors_raw.stdout[0] | ansible.netcommon.parse_cli_textfsm('templates/cisco_ios_show_cdp_neighbors.textfsm') |
      regex_replace('Ten ', 'Te') |
      regex_replace('Gig ', 'Gi') | list }}"

Output:

[
        "[",
        "{",
        "'",
        "N",
        "E",
        "I",
        "G",
        "H",
        "B",
        "O",
        "R",
        "'",
        ":",
        " ",
        "'",

The same weird output happens if I use zip instead of list.

I have also tried map('regex_replace', 'Ten (.*)', 'Te\\1') but it only replaces one occurrence and turns each dict in to a string:

- name: PARSE NEIGHBORS
  debug:
    msg: "{{ neighbors_raw.stdout[0] | ansible.netcommon.parse_cli_textfsm('templates/cisco_ios_show_cdp_neighbors.textfsm') |
      map('regex_replace', 'Ten (.*)', 'Te\\1') }}"

Output:

[
        "{'NEIGHBOR': 'switch1.mydomain.com', 'LOCAL_INTERFACE': 'Te1/1/1', 'CAPABILITY': 'R S I', 'PLATFORM': 'C9500-16X', 'NEIGHBOR_INTERFACE': 'Ten 1/0/4'}",
        "{'NEIGHBOR': 'switch2.mydomain.com', 'LOCAL_INTERFACE': 'Gig 1/1/2', 'CAPABILITY': 'S I', 'PLATFORM': 'C9200L-48', 'NEIGHBOR_INTERFACE': 'Te1/1/1'}"
]

I am also concerned about regex_replace on the entire dict treating it like a string. I really only want to apply the conversion to the specific value for LOCAL_INTERFACE and NEIGHBOR_INTERFACE.

How can I go about achieving this?

2 Answers 2

2

Given the data

  neighbors:
    - CAPABILITY: R S I
      LOCAL_INTERFACE: Ten 1/1/1
      NEIGHBOR: switch1.mydomain.com
      NEIGHBOR_INTERFACE: Ten 1/0/4
      PLATFORM: C9500-16X
    - CAPABILITY: S I
      LOCAL_INTERFACE: Gig 1/1/2
      NEIGHBOR: switch2.mydomain.com
      NEIGHBOR_INTERFACE: Ten 1/1/1
      PLATFORM: C9200L-48

Create the lists of the updated attributes

  lifc: "{{ neighbors |
            map(attribute='LOCAL_INTERFACE') |
            map('regex_replace', 'Ten ', 'Te') |
            map('regex_replace', 'Gig ', 'Gi') |
            map('community.general.dict_kv', 'LOCAL_INTERFACE') }}"
  nifc: "{{ neighbors |
            map(attribute='NEIGHBOR_INTERFACE') |
            map('regex_replace', 'Ten ', 'Te') |
            map('regex_replace', 'Gig ', 'Gi') |
            map('community.general.dict_kv', 'NEIGHBOR_INTERFACE') }}"

gives

  lifc:
  - LOCAL_INTERFACE: Te1/1/1
  - LOCAL_INTERFACE: Gi1/1/2

  nifc:
  - NEIGHBOR_INTERFACE: Te1/0/4
  - NEIGHBOR_INTERFACE: Te1/1/1

zip the lists and combine items

  update: "{{ lifc | zip(nifc) | map('combine') }}"

gives

  update:
  - LOCAL_INTERFACE: Te1/1/1
    NEIGHBOR_INTERFACE: Te1/0/4
  - LOCAL_INTERFACE: Gi1/1/2
    NEIGHBOR_INTERFACE: Te1/1/1

zip the update and combine items

  result: "{{ neighbors | zip(update) | map('combine') }}"

gives what you want

  result:
  - CAPABILITY: R S I
    LOCAL_INTERFACE: Te1/1/1
    NEIGHBOR: switch1.mydomain.com
    NEIGHBOR_INTERFACE: Te1/0/4
    PLATFORM: C9500-16X
  - CAPABILITY: S I
    LOCAL_INTERFACE: Gi1/1/2
    NEIGHBOR: switch2.mydomain.com
    NEIGHBOR_INTERFACE: Te1/1/1
    PLATFORM: C9200L-48

Example of a complete playbook for testing

- hosts: localhost

  vars:

    neighbors:
    - CAPABILITY: R S I
      LOCAL_INTERFACE: Ten 1/1/1
      NEIGHBOR: switch1.mydomain.com
      NEIGHBOR_INTERFACE: Ten 1/0/4
      PLATFORM: C9500-16X
    - CAPABILITY: S I
      LOCAL_INTERFACE: Gig 1/1/2
      NEIGHBOR: switch2.mydomain.com
      NEIGHBOR_INTERFACE: Ten 1/1/1
      PLATFORM: C9200L-48

    lifc: "{{ neighbors |
              map(attribute='LOCAL_INTERFACE') |
              map('regex_replace', 'Ten ', 'Te') |
              map('regex_replace', 'Gig ', 'Gi') |
              map('community.general.dict_kv', 'LOCAL_INTERFACE') }}"
    nifc: "{{ neighbors |
              map(attribute='NEIGHBOR_INTERFACE') |
              map('regex_replace', 'Ten ', 'Te') |
              map('regex_replace', 'Gig ', 'Gi') |
              map('community.general.dict_kv', 'NEIGHBOR_INTERFACE') }}"

    update: "{{ lifc | zip(nifc) | map('combine') }}"
    result: "{{ neighbors | zip(update) | map('combine') }}"

  tasks:

    - debug:
        var: lifc
    - debug:
        var: nifc
    - debug:
        var: update
    - debug:
        var: result
Sign up to request clarification or add additional context in comments.

Comments

0

I marked the accepted answer, but I had actually moved forward using Jinja loops...

- name: PARSE NEIGHBORS
  ansible.builtin.set_fact:
    neighbors: "{{ neighbors_raw.stdout[0] | ansible.netcommon.parse_cli_textfsm('templates/cisco_ios_show_cdp_neighbors.textfsm') }}"

- name: TRANSFORM NEIGHBORS
  ansible.builtin.set_fact:
    neighbors: |
      {% for neighbor in neighbors %}
      {{ neighbor['LOCAL_INTERFACE'] | regex_replace('Ten ', 'Te') | regex_replace('Gig ', 'Gi') }}:
      {% for k, v in neighbor.items() %}
      {% if k == 'LOCAL_INTERFACE' or k == 'NEIGHBOR_INTERFACE' %}
        {{ k }}: {{ v | regex_replace('Ten ', 'Te') | regex_replace('Gig ', 'Gi') }}
      {% else %}
        {{ k }}: {{ v }}
      {% endif %}
      {% endfor %}
      {% endfor %}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.