1

I am trying to run a show command on a cisco router to find out the down interfaces.

I would like to print only the Ethernet Interface name(for eg:Eth1/3) for those are having the Status as down in the command output in a JSON fromat.

Cisco output(input.json)

[
    [
        [
            "--------------------------------------------------------------------------------",
            "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
            "Interface                                                                    Ch #",
            "--------------------------------------------------------------------------------",
            "Eth1/20         --      eth  routed down    XCVR not inserted        auto(D) --"
        ]
    ],
    [
        [
            "--------------------------------------------------------------------------------",
            "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
            "Interface                                                                    Ch #",
            "--------------------------------------------------------------------------------",
            "Eth1/21         --      eth  routed down    XCVR not inserted        auto(D) --"
        ]
    ],
    [
        [
            "--------------------------------------------------------------------------------",
            "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
            "Interface                                                                    Ch #",
            "--------------------------------------------------------------------------------",
            "Eth1/22         --      eth  routed down    XCVR not inserted        auto(D) --"
        ]
    ],
    [
        [
            "--------------------------------------------------------------------------------",
            "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
            "Interface                                                                    Ch #",
            "--------------------------------------------------------------------------------",
            "Eth1/23         --      eth  routed down    XCVR not inserted        auto(D) --"
        ]
    ]
]

Playbook

    - name: Set global device name
      set_fact:
        global_device_name: "{{ device_name }}"
  

    - name: Execute command on interfaces
      cisco.ios.ios_command:
        commands:
          -  "show interface {{ item }} brief"
      loop: "{{ hostvars[global_device_name]['ports'] }}"
      delegate_to: "{{ global_device_name }}"
      register: command_output
      changed_when: false
    
    - name: Create a list of interface details
      set_fact:
        interface_list: "{{ interface_list|default([]) + [item.stdout_lines] }}"
      loop: "{{ command_output.results }}"
      loop_control:
        label: "{{ item.item }}"
      
    - name: Save output to JSON file
      copy:
        content: "{{ interface_list  | to_nice_json}}"
        dest: output.json
    
    - name: Firewall Rule Create/Update variable
      set_fact: 
        command_output: "{{ lookup('file', 'output.json') }}"  
    
    - name: Firewall Rule Create/Update variable
      set_fact:   
        interface_details: "{{ command_output|map('flatten')}}"
    
    - name: Format output
      debug:
        msg: "{{ interface_details  }}"

Here the interface_details is the Cisco output

With the above playbook i am getting an empty array.

Vinny
  • 302
  • 2
  • 11
  • Have you confirmed using the `state: gathered` of the [purposed module](https://docs.ansible.com/ansible/latest/collections/cisco/ios/ios_interfaces_module.html)? This way you'd get an easy to parse JSON as return. – β.εηοιτ.βε Jun 13 '23 at 13:37
  • @β.εηοιτ.βε I am using ios_commands module to get a show output. So is it possible to use state: gathered along with that? Your suggestion would be helpful. Updating some more info about the playbook – Vinny Jun 13 '23 at 13:52

1 Answers1

1

Given the file command_output.yml for testing

shell> cat command_output.yml
[
  [
    [
      "--------------------------------------------------------------------------------",
      "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
      "Interface                                                                    Ch #",
      "--------------------------------------------------------------------------------",
      "Eth1/1          1       eth  trunk  down    XCVR not inserted        auto(D) --"
    ]
  ],
  [
    [
      "--------------------------------------------------------------------------------",
      "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
      "Interface                                                                    Ch #",
      "--------------------------------------------------------------------------------",
      "Eth1/2          1       eth  trunk  down    XCVR not inserted        auto(D) --"
    ]
  ],
  [
    [
       "--------------------------------------------------------------------------------",
      "Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port",
      "Interface                                                                    Ch #",
      "--------------------------------------------------------------------------------",
      "Eth1/3          1       eth  trunk  down    XCVR not inserted        auto(D) --"
    ]
  ]
]

Read the file and flatten the lists

  command_output: "{{ lookup('file', 'command_output.yml') }}"
  interface_details: "{{ command_output|map('flatten') }}"

gives

  interface_details:
  - - '--------------------------------------------------------------------------------'
    - Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port
    - 'Interface                                                                    Ch #'
    - '--------------------------------------------------------------------------------'
    - Eth1/1          1       eth  trunk  down    XCVR not inserted        auto(D) --
  - - '--------------------------------------------------------------------------------'
    - Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port
    - 'Interface                                                                    Ch #'
    - '--------------------------------------------------------------------------------'
    - Eth1/2          1       eth  trunk  down    XCVR not inserted        auto(D) --
  - - '--------------------------------------------------------------------------------'
    - Ethernet        VLAN    Type Mode   Status  Reason                 Speed     Port
    - 'Interface                                                                    Ch #'
    - '--------------------------------------------------------------------------------'
    - Eth1/3          1       eth  trunk  down    XCVR not inserted        auto(D) --

Get the dictionary interface_status

  interface_status: |
    {% filter from_yaml %}
    {% for i in interface_details %}
    {% set val=i.4|split() %}
    {{ val.0 }}: {{ val.4 }}
    {% endfor %}
    {% endfilter %}

gives

  interface_status:
    Eth1/1: down
    Eth1/2: down
    Eth1/3: down

Optionally, the declarations below give the same result

  interface_values: "{{ interface_details|
                        map(attribute='4')|
                        map('split') }}"
  interface_status: "{{ dict(interface_values|map(attribute='0')|
                             zip(interface_values|map(attribute='4'))) }}"

and select(filter) the the interfaces

  interface_filtered: "{{ interface_status|dict2items|
                          selectattr('value', 'eq','down')|
                          map(attribute='key') }}"

gives

  interface_filtered:
  - Eth1/1
  - Eth1/2
  - Eth1/3

Example of a complete playbook for testing

- hosts: localhost

  vars:

    command_output: "{{ lookup('file', 'command_output.yml') }}"
    interface_details: "{{ command_output|map('flatten') }}"

    interface_status: |
      {% filter from_yaml %}
      {% for i in interface_details %}
      {% set val=i.4|split() %}
      {{ val.0 }}: {{ val.4 }}
      {% endfor %}
      {% endfilter %}
    interface_values: "{{ interface_details|
                          map(attribute='4')|
                          map('split') }}"
    interface_statu2: "{{ dict(interface_values|map(attribute='0')|
                               zip(interface_values|map(attribute='4'))) }}"
    interface_filtered: "{{ interface_status|dict2items|
                            selectattr('value', 'eq','down')|
                            map(attribute='key') }}"

  tasks:

    - debug:
        var: interface_details
    - debug:
        var: interface_status
    - debug:
        var: interface_statu2
    - debug:
        var: interface_filtered
Vladimir Botka
  • 58,131
  • 4
  • 32
  • 63
  • Thanks for the solution. I am facing some difficulty in doing the flatten of that list. i am using this "{{ interface_details | flatten | to_nice_yaml}}" to do the flatten, but it is not proper as you have mentioned in your solution above. Could you please let me know how to do that? – Vinny Jun 13 '23 at 20:14
  • You have to *map* the filter *flatter* `command_output | map('flatten')`. I fixed the code. – Vladimir Botka Jun 13 '23 at 21:41
  • Thanks for the details. Not sure what mistake i am doing. The provided input is in JSON format and i think you have considered that as a yml file. I have even tried by saving the input file as yml and still not getting the expected output as shown in your example. Let me paste the complete playbook here to get an overview. – Vinny Jun 15 '23 at 05:35
  • 1
    JSON is a subset of YAML. In other words, JSON is valid YAML. – Vladimir Botka Jun 15 '23 at 06:25