Sum and combine keys from dictionary in Ansible

Viewed 326

I have an Ansible fact like (it might have many hypervisors):

"combined_data": {
    "hypervisor-01": [
        {
            "disk": 150,
            "id": "okd-service-01"
        },
        {
            "disk": 25,
            "id": "okd-master-02"
        },
        {
            "disk": 25,
            "id": "okd-master-01"
        }
    ],
    "hypervisor-02": [
        {
            "disk": 20,
            "id": "okd-bootstrap-01"
        },
        {
            "disk": 30,
            "id": "okd-worker-01"
        },
        {
            "disk": 25,
            "id": "okd-master-03"
        }
    ]
}

And I need to create another fact with the data summarized as:

"result": [
    "hypervisor-01": 200,
    "hypervisor-02": 75
]

I tried with map and combine but didn't get anything working so far. Any idea?

3 Answers

Just in case people are interested, the following set_fact will do the trick:

- set_fact:
    data_sum: "{{ data_sum|default([]) + [{item: my_attribute}] }}"
  vars:
    my_attribute: "{{ kubeinit_validations_libvirt_combined_disk_usage[item] | map(attribute='disk') | list | sum }}"
  with_items: "{{ kubeinit_validations_libvirt_combined_disk_usage.keys() | list }}"

Here's a way to do it which can be used via a task/play/group var and doesn't involve using a slow task loop:

{%- set result = [] %}
{%- for host, hostdata in combined_data.items() %}
{%- set _ = result.append({host: hostdata | json_query('sum([*].disk)')}) %}
{%- endfor %}
{{- result }}

It's useful to use of jmespath to select and sum the disk values because it will also ensure that if the values don't exist, it won't error out.

A dictionary might be more convenient. For example

    - set_fact:
        result: "{{ dict(keys|zip(vals)) }}"
      vars:
        keys: "{{ combined_data.keys()|list }}"
        vals: "{{ combined_data.values()|map('sum', attribute='disk')|list }}"

gives

  result:
    hypervisor-01: 200
    hypervisor-02: 75
Related