How to save output from multiple remote servers to a single dictionary?

Viewed 138

I'd like to store every return values to a single dict with key is hostname and value is output then find the maximum number.

hosts: bg-workers
  gather_facts: true
  become: yes

  tasks:
  - name: Find cpu idle
    shell: iostat -c | awk '/^ /{print $6}'
    register: cpuPerc_idle

  - name: Setting CPU facts
    set_fact:
      cpu_idle: "{{ cpuPerc_idle.stdout }}"

  - name: Print CPU idle
    ansible.builtin.debug:
      msg: "{{ cpu_idle }}"

Currently, the output are store independently

ok: [bg-1] => {
    "msg": [
        "48.30"
    ]
}
ok: [bg-2] => {
    "msg": [
        "47.98"
    ]
}
ok: [bg-3] => {
    "msg": [
        "45.03"
    ]
}
ok: [bg-4] => {
    "msg": [
        "77.94"
    ]
}

my desired pattern is

ansible_cpuPerc_idle={ bg-1:'48.30', bg-2:'47.98', bg-3: '45.03', bg-4: '77.94'} and then I can easily find the maximum from its value.

the closet that I can produce is

"cpus_idle": {
        "bg-1": "47.8",
        "bg-2": "47.8",
        "bg-3": "47.8",
        "bg-4": "47.8"
    }
}

from the following jinja loop

  - name: Print %cpu idle
    vars:
      cpus_idle: |
        {%- set o=dict() %}
        {%- for i in groups['bg-workers'] %}
          {%- set key=i %}
          {%- if key in o %}
            {%- set _dummy = o.update( {key: o[i]+1} ) %}
          {%- else %}
            {%- set _dummy = o.update( {key: cpu_idle }) %}
          {%- endif %}
        {%- endfor %}
        {{ o }}

which is always gives the same cpu_idle(result from 1st node) cause it doesn't loop with i variable. I've tried many ways such as {%- set _dummy = o.update( {key: i["cpu_idle"] }) %} for trying to access defined fact, it ends up with "AnsibleUndefined"

2 Answers

Something like this?

- name: Set fact
  set_fact:
    data: "{{ data | default({}) | combine({item:hostvars[item]['cpuPerc_idle']}) }}"
  loop: "{{ ansible_play_hosts }}"
  delegate_to: localhost
  run_once: true

- name: Print data
  debug:
    msg: "{{ data }}"
  delegate_to: localhost
  run_once: true

You need to create a temporary file (cpuReport.txt in this case) and manipulate the data then. You can use the "lineinfile" module for this purpose. Just take the output messages from each node and append it one by one in the temporary file. Rest of it is then just basic syntactical trimming.

You can go with the below solution -

- hosts: all
  gather_facts: true
  become: yes

  tasks:

                - name: Find cpu idle
                  shell: iostat -c | awk '/^ /{print $6}'
                  register: cpuPerc_idle


                - name: Setting CPU facts
                  set_fact:
                        ansible_cpuPerc_idle: "{{ cpuPerc_idle.stdout }}"


                - name: Create CPU idle file
                  lineinfile:
                       line: "{{ inventory_hostname }}: {{ ansible_cpuPerc_idle }},"
                       dest: /home/_admin/cpuReport.txt
                       insertafter: EOF
                       create: yes
                  delegate_to: localhost


                - name: Trim out the excess commas
                  command: "cat /home/_admin/cpuReport.txt"
                  register: ansible_cpuPerc_idle
                  delegate_to: localhost


                - name: Print the result
                  debug:
                        msg: "{{ ansible_cpuPerc_idle.stdout | regex_replace(',$', '') | regex_replace('\n', ' ') }}"
                  run_once: True
                  delegate_to: localhost
                  register: ansible_cpuPerc_idle

NOTE: The temporary file needs to be removed after every execution. You can add a task to check and remove the temporary file (cpuReport.txt) -

    - name: Remove the temporary file
      file:
         path: /home/_admin/cpuReport.txt
         state: absent

There might be other approaches to this, such as using jinja2 files but I tried to stick to this solution since jinja2 was not mentioned anywhere and in that case there would be 2 files to take care of.

Related