Ansible append dict results for all hosts

假装没事ソ 提交于 2021-01-27 18:10:28

问题


I am attempting to use Ansible to send a command to several hosts that returns a dict. I then want to append the the dict results of each host to accumulate the results of all the hosts. Finally, I want to print the dict of accumulated results for later processing or to write to a file. It appears I am failing to combine the dicts due to results showing as string. Is there a way to remedy this? Also, is there a more Ansible efficient way to accomplish this?

Example Playbook:

---
  - hosts: myhosts
    gather_facts: False
    vars:
      mydict: {}
    tasks:
    - name: Get dict result
      shell: "cat /path/dict_output"
      register: output
    - set_fact:
       result_dict="{{ output.stdout}}"

    - debug: var=result_dict

Debug Output:

TASK [debug] ****************************************************************************************************************************************************************
ok: [host-a] => {
    "result_dict": {
        "host_b": [
            {
                "ip-1": {
                    "port": "22", 
                    "service": "ssh"
                }
            }, 
            {
                "ip-2": {
                    "port": "21", 
                    "service": "ftp"
                }
            }
        ]
    }
}
ok: [host-b] => {
    "result_dict": {
        "host_a": [
            {
                "ip-1": {
                    "port": "22", 
                    "service": "ssh"
                }
            }, 
            {
                "ip-2": {
                    "port": "21", 
                    "service": "ftp"
                }
            }
        ]
    }
}

My Attempt to combine results of each host:

- set_fact:
   mydict: "{{ mydict | combine(output.stdout) }}"
- debug: var=mydict

Failed Result:

TASK [set_fact] *************************************************************************************************************************************************************
    fatal: [host-b]: FAILED! => {"msg": "|combine expects dictionaries, got u\"{'host_b': [{'ip-1': {'service': 'ssh', 'port': '22'}}, {'ip-2': {'service': 'ftp', 'port': '21'}}]}\""}
    fatal: [host-a]: FAILED! => {"msg": "|combine expects dictionaries, got u\"{'host_a': [{'ip-1': {'service': 'ssh', 'port': '22'}}, {'ip-2': {'service': 'ftp', 'port': '21'}}]}\""}

Desired output of accumulated results:

{'host_a': [{'ip-1': {'port': '22', 'service': 'ssh'}},
            {'ip-2': {'port': '21', 'service': 'ftp'}}],
 'host_b': [{'ip-1': {'port': '22', 'service': 'ssh'}},
            {'ip-2': {'port': '21', 'service': 'ftp'}}]}

回答1:


You can create that hashmap in a single task running on localhost after you gathered all the info on all the hosts.

You can browse facts from any hosts in the hostvars hashmap, and access a list of all machines in a group through `groups['name_of_group'].

Knowing those 2 info, the basic idea is:

  • Extract all hostvars for the machines in your group and make sure we get a list out of that => groups["myhosts"] | map("extract", hostvars) | list
  • Filter that result to retain only the result_dict. We can do this through json_query => json_query("[].result_dict"). We are already very close to what your are looking for, it will be a list of hashmaps (one element for each host). But you are looking for a single hashmap, so....
  • Loop on this result to create a single hashmap with an entry for each host.

The following play ran after your other tasks should meet your requirements:

- name: consolidate and display my result
  hosts: localhost

  tasks:
    - name: Consolidate result in a single hashmap
      set_fact:
        my_final_map: "{{ my_final_map | default({}) | combine(item) }}"
      loop: >-
        {{
          groups["myhosts"]
          | map("extract", hostvars)
          | list
          | json_query("[].result_dict")
        }}

    - name: Display consolidated result
      debug:
        var: my_final_map

Note after comment: if using json_query is a problem, it is not absolutely mandatory in this case. We can select the needed attribute with map again. The basics are the same, this is the modified task:

    - name: Consolidate result in a single hashmap
      set_fact:
        my_final_map: "{{ my_final_map | default({}) | combine(item) }}"
      loop: >-
        {{
          groups["myhosts"]
          | map("extract", hostvars)
          | map(attribute="result_dict")
          | list
        }}


来源:https://stackoverflow.com/questions/59325431/ansible-append-dict-results-for-all-hosts

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!