Ansible how to sort output of command from multiple nodes

Viewed 869

I have an Ansible playbook that runs some command on all my nodes to generate a list of available images. Then on my ansible controller, I want to generate a combined, sorted list with all duplicates removed. I have a playbook:

- hosts: kube_cluster
  tasks:
  - command: crictl images
    become: yes
    register: cri_images

  - name: save results
    delegate_to: localhost
    run_once: yes
    become: no
    copy:
      dest: cri_images.txt
      content: |
            {% for h in ansible_play_hosts %}
              {{ hostvars[h].cri_images.stdout  }}
            {% endfor %}

that does generate the file, but I fail to get the sorting of the combined output.

I would get, for each of the nodes, output like:

docker.io/bitnami/contour                          1.17.1-debian-10-r0    5be5c048ac5e2       132MB
docker.io/bitnami/envoy                            1.17.3-debian-10-r62   7ff8d931d11c7       150MB
docker.io/calico/cni                               v3.19.1                5749e8b276f9b       146MB
docker.io/calico/node                              v3.19.1                c4d75af7e098e       171MB

available in the 'stdout_lines'. Some lines are duplicated across some of the nodes.

I fail to adapt the jinja code to sort, and remove duplicates. Probably need some form of nested loop, or a means to flatten the the hostvars[h].cri_images.stdout. But I could not figure out how to do that. Most I got were syntax/template errors.

As an extra, I'd also like to:

  • remove the header line of the output
  • have the option to select only few of the columns of the combined output.

In a shell script this could be achieved easily by a

cat cri_images.txt | grep -v ^IMAGES | awk '{print $1 ":" $2}' | sort | uniq

but requirement is that it would have to run in Ansible.

anyone have a hint? thx.

1 Answers

Q: "Some command on all my nodes to generate a list of available images ... generate a combined, sorted list with all duplicates removed."

A: For testing, let's split the hostnames, instead of generating the lists of available images, e.g.

- hosts: test_11,test_12,test_13
  tasks:
    - set_fact:
        cri_images: "{{ {}|combine({'stdout_lines': 
                                    inventory_hostname.split('_')}) }}"
    - debug:
        var: cri_images.stdout_lines

gives

ok: [test_12] => 
  cri_images.stdout_lines:
  - test
  - '12'
ok: [test_11] => 
  cri_images.stdout_lines:
  - test
  - '11'
ok: [test_13] => 
  cri_images.stdout_lines:
  - test
  - '13'

Then, generate a combined, sorted list with all duplicates removed

    - copy:
        dest: cri_images.txt
        content: "{{ _content | join('\n') }}"
      vars:
        _content: "{{ ansible_play_hosts|
                      map('extract', hostvars, ['cri_images', 'stdout_lines'])|
                      flatten|unique|sort }}"
      delegate_to: localhost
      run_once: true

gives

shell> cat cri_images.txt
11
12
13
test
Related