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!
CodePudding user response:
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
CodePudding user response:
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}}"
CodePudding user response:
... 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