skip to Main Content

once again I’m trying to accomplish something with Ansible.

I built a custom dict with variables for each server that looks like this.

- name: Create and Add items to server_list
  set_fact: 
    server_list: "{{ server_list | default({}) | combine ({ item.key : item.value }) }}"
  with_items:
    - { 'key': 'Servername' , 'value': "{{ ansible_hostname }}"}
    - { 'key': 'IP-Adresse' , 'value': "{{ ansible_default_ipv4.address }}"}
    - { 'key': 'OS' , 'value': "{{ ansible_os_family }} {{ ansible_distribution_major_version }}"}
    - { 'key': 'Plattform' , 'value': "{{ ansible_system }}"}

- name: DEBUG server_list
  debug:
    var=server_list

That’s working. But now ansible outputs this for every server like this:

ok: [XXX] => {
    "server_list": {
        "IP-Adresse": "x.x.x.x",
        "OS": "Debian 11",
        "Plattform": "Linux",
        "Servername": "XXX"
    }
}
ok: [YYY] => {
    "server_list": {
        "IP-Adresse": "x.x.x.y",
        "OS": "Debian 11",
        "Plattform": "Linux",
        "Servername": "YYY"
    }
}

...

What I want to have now is a complete and "global" dict with every server. I think that would look like this.
Example of what I want as output (here named as server_list_dict):

{
    "server_list_dict": {
        "0": {
            "Servername": "XXX",
            "IP-Adresse": "x.x.x.x",
            "OS": "Debian 11",
            "Plattform": "Linux"
        },
        "1": {
            "Servername": "YYY",
            "IP-Adresse": "x.x.x.y",
            "OS": "Debian 11",
            "Plattform": "Linux"
        }
    }
}

Only for clarification for what I want to achieve

What I want to do later is create a html table that should output something like this.
Example html that I want to create:

<table style="width:50%">
    <!-- table header -->
    <tr>
        <th>Servername</th>
        <th>IP</th>
        <th>OS</th>
        <th>Platform</th>
    </tr>  
    <!-- table rows -->
    <tr>
        <td>Hostname1</td>
        <td>X.X.X.X</td>
        <td>Debian</td>
        <td>Linux</td>
    </tr>
    <tr>
        <td>Hostname2</td>
        <td>X.X.X.Y</td>
        <td>Debian</td>
        <td>Linux</td>
    </tr>
</table>

I already have a template to use.
This will go through my "global" dict with every server and use all of the keys and values that are in there. (hopefully, couldnt test it, because idk how to merge my dicts)

<table style="width:100%">
  <!-- table header -->
  {% if server_list_dict %}
  <tr>
     {% for key in server_list_dict[0] %}
     <th> {{ key }} </th>
     {% endfor %}
  </tr>
  {% endif %}

  <!-- table rows -->
  {% for dict_item in server_list_dict %}
  <tr>
     {% for value in dict_item.values() %}
     <td> {{ value }} </td>
     {% endfor %}
  </tr>
  {% endfor %}
</table>

Does anybody know how to merge dicts with the same keys in a complete dict? I couldn’t find anything useful. Thanks everyone in advance!

3

Answers


  1. … Ansible outputs this for every server … I have mutliple dicts now (each for every server because of the set_fact) …

    This is since gather_facts module – Gathers facts about remote hosts and set_fact module – Set host variable(s) and fact(s) are running distributed on the Remote Node(s) and the expected behavior.

    What I want to have now is a complete and "global" dict with every server.

    I understand your question that you like to delegate facts first to one of the hosts, the Control Node (Deployment Host or localhost) where you can than create your file or use your template for further distribution. There, all the information would be aggregated in one data structure (dictionary) already.

    An other approach might be to use caching facts via cache plugins.

    Documentation

    Login or Signup to reply.
  2. Extract the list of the variables server_list

    server_lsts: "{{ ansible_play_hosts|
                     map('extract', hostvars, 'server_list')|list }}"
    

    gives

    server_lsts:
      - IP-Adresse: x.x.x.x
        OS: Debian 11
        Plattform: Linux
        Servername: XXX
      - IP-Adresse: x.x.x.y
        OS: Debian 11
        Plattform: Linux
        Servername: YYY
    

    and create the dictionary

    server_dict: "{{ dict(ansible_play_hosts|zip(server_lsts)) }}"
    

    gives

    server_dict:
      XXX:
        IP-Adresse: x.x.x.x
        OS: Debian 11
        Plattform: Linux
        Servername: XXX
      YYY:
        IP-Adresse: x.x.x.y
        OS: Debian 11
        Plattform: Linux
        Servername: YYY
    

    • Example of a complete playbook
    - hosts: all
      vars:
        server_lsts: "{{ ansible_play_hosts|
                         map('extract', hostvars, 'server_list')|list }}"
        server_dict: "{{ dict(ansible_play_hosts|zip(server_lsts)) }}"
      tasks:
        - debug:
            var: server_dict
          run_once: true
    
    • Put the declaration of the dictionary into the group_vars. For example
    shell> cat group_vars/all.yml
    server_list:
      Servername: "{{ ansible_hostname }}"
      IP-Adresses: "{{ ansible_all_ipv4_addresses }}"
      OS: "{{ ansible_os_family }} {{ ansible_distribution_major_version }}"
      Plattform: "{{ ansible_system }}"
    

    The playbook

    - hosts: test_11:test_12
      vars:
        server_lsts: "{{ ansible_play_hosts|
                         map('extract', hostvars, 'server_list')|list }}"
        server_dict: "{{ dict(ansible_play_hosts|zip(server_lsts)) }}"
      tasks:
        - debug:
            var: server_dict
          run_once: true
    

    gives

    server_dict:
      test_11:
        IP-Adresses:
          - 10.1.0.61
        OS: FreeBSD 13
        Plattform: FreeBSD
        Servername: test_11
      test_12:
        IP-Adresses:
          - 10.1.0.62
        OS: FreeBSD 13
        Plattform: FreeBSD
        Servername: test_12
    
    • If you want to index the dictionaries create the sequence. For example
    shell> cat pb.yml
    - hosts: test_11:test_12
      vars:
        server_lsts: "{{ ansible_play_hosts|
                         map('extract', hostvars, 'server_list')|list }}"
        params: "start=0 count={{ ansible_play_hosts|length }}"
        server_dict: "{{ dict(q('sequence', params)|zip(server_lsts)) }}"
      tasks:
        - debug:
            var: server_dict
          run_once: true
    

    gives

    server_dict:
      '0':
        IP-Adresses:
          - 10.1.0.61
        OS: FreeBSD 13
        Plattform: FreeBSD
        Servername: test_11
      '1':
        IP-Adresses:
          - 10.1.0.62
        OS: FreeBSD 13
        Plattform: FreeBSD
        Servername: test_12
    

    Login or Signup to reply.
  3. you could directly use jinja to create your var as expected:

      tasks:
        - name: Create and Add items to server_list
          set_fact: 
            server_list: >- 
                  {%- set result = {} -%}
                  {%- for server in ansible_play_hosts -%}
                  {%- set idx = loop.index0 -%}
                  {%- set name = hostvars[server].ansible_hostname -%}
                  {%- set ip = hostvars[server].ansible_default_ipv4.address -%}
                  {%- set os = hostvars[server].ansible_os_family -%}
                  {%- set pf = hostvars[server].ansible_system -%}
                  {%- set _= result.update({idx: {"Servername": name, "IP": ip, "OS": os, "Platform": pf} }) -%}
                  {%- endfor -%}
                  {{ result }}
          
        - debug:
            msg: "{{server_list}}" 
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search