Ansible - loops and verbose output

While using loops in Ansible, by default, output contains the entire content of the item being processed. This can result in a great amount of verbosity if the item is a dictionary or is otherwise long.

For example, when looping through the output of the "show ip bgp sum", we want to use just an IP of the peer as an input in another task. When "Get routes for each of the BGP peers" task executes Ansible will display the entire data structure linked to that peer.

Task with the loop:

  - name: Get routes for each of the BGP peers
    eos_command:
     commands:
      - show ip bgp neighbors {{ item.key }} routes | json
    with_dict: "{{ bgp_sum.stdout.0.vrfs.default.peers }}"

Output of the playbook run:

[przemek@quasar blog]$ ansible-playbook loop_label_off.yml

PLAY [Output for the task with a loop - no label] ********************************************************************************

TASK [Get list of the BGP peers] *************************************************************************************************
ok: [vEOS-01]

TASK [Get routes for each of the BGP peers] **************************************************************************************
ok: [vEOS-01] => (item={'key': u'10.0.13.2', 'value': {u'version': 4, u'msgSent': 125, u'inMsgQueue': 0, u'prefixReceived': 7, u'upDownTime': 1513802338.414279, u'underMaintenance': False, u'msgReceived': 123, u'prefixAccepted': 7, u'peerState': u'Established', u'outMsgQueue': 0, u'asn': 65001}})
ok: [vEOS-01] => (item={'key': u'10.0.12.2', 'value': {u'version': 4, u'msgSent': 125, u'inMsgQueue': 0, u'prefixReceived': 2, u'upDownTime': 1513802337.414012, u'underMaintenance': False, u'msgReceived': 123, u'prefixAccepted': 2, u'peerState': u'Established', u'outMsgQueue': 0, u'asn': 65001}})
ok: [vEOS-01] => (item={'key': u'10.1.11.2', 'value': {u'version': 4, u'msgSent': 0, u'inMsgQueue': 0, u'peerStateIdleReason': u'NoInterface', u'prefixReceived': 0, u'upDownTime': 1513802330.414448, u'underMaintenance': False, u'msgReceived': 0, u'prefixAccepted': 0, u'peerState': u'Idle', u'outMsgQueue': 0, u'asn': 65101}})

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=2    changed=0    unreachable=0    failed=0

Needless to say, this is too verbose even though our structure is not too complex. With a larger data set or more complex structures, the output will get difficult to read very quickly.

Luckily, there is a way to make Ansible change what is displayed as the value of the item variable. Starting with Ansible 2.2 we can use the label directive of the loop_control option and specify what we want to be shown in the output. This value can be taken from the variable we're looping over, like item.key, or can be a dummy string, e.g. dummy_label. I'll show below both of these options.

First, let's set the value of the item to the IP address of the peer.

Modified task:

  - name: Get routes for each of the BGP peers
    eos_command:
     commands:
      - show ip bgp neighbors {{ item.key }} routes | json
    with_dict: "{{ bgp_sum.stdout.0.vrfs.default.peers }}"
    loop_control:
     label: "{{ item.key }}"

Output for the playbook with the modified task:

[przemek@quasar blog]$ ansible-playbook loop_label_on_var.yml

PLAY [Output for the task with a loop - variable for label] **********************************************************************

TASK [Get list of the BGP peers] *************************************************************************************************
ok: [vEOS-01]

TASK [Get routes for each of the BGP peers] **************************************************************************************
ok: [vEOS-01] => (item=10.0.13.2)
ok: [vEOS-01] => (item=10.0.12.2)
ok: [vEOS-01] => (item=10.1.11.2)

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=2    changed=0    unreachable=0    failed=0

Much better, only the IP address for each of the peers is shown when we looped over our data structure.

Now, we will set the value of the item to a static string, i.e. it will stay the same for all items.

Modified task:

  - name: Get routes for each of the BGP peers
    eos_command:
     commands:
      - show ip bgp neighbors {{ item.key }} routes | json
    with_dict: "{{ bgp_sum.stdout.0.vrfs.default.peers }}"
    loop_control:
     label: "dummy_label"

The output:

[przemek@quasar blog]$ ansible-playbook loop_label_on_str.yml

PLAY [Output for the task with a loop - string for label] ************************************************************************

TASK [Get list of the BGP peers] *************************************************************************************************
ok: [vEOS-01]

TASK [Get routes for each of the BGP peers] **************************************************************************************
ok: [vEOS-01] => (item=dummy_label)
ok: [vEOS-01] => (item=dummy_label)
ok: [vEOS-01] => (item=dummy_label)

PLAY RECAP ***********************************************************************************************************************
vEOS-01                    : ok=2    changed=0    unreachable=0    failed=0

Now the label is the same for each of the iterations of the loop.

Personally, I prefer to use a variable taken from my data structure as a label, as it allows me to see all of the items being processed. Static string could come handy if we just want to get rid of the verbose output.

You can get full listings of the playbooks from my github repository:
https://github.com/progala/ttl255.com/tree/master/ansible/loops-verbose-output