Reputation: 37750
I have a static variable that holds IP addresses of all hosts in my inventory (how to obtain this dynamically is a separate question) like this:
server_ips:
www1.example.com:
ipv4:
- 192.168.0.10
- 192.168.0.11
ipv6:
- '2a00:abcd:1234::100'
- '2a00:abcd:1234::101'
www2.example.com:
ipv4:
ipv6:
- '2a00:abcd:1234::200'
- '2a00:abcd:1234::201'
db1.example.com:
ipv4:
- 192.168.1.2
ipv6:
These names align with hosts in my inventory:
[webservers]
www1.example.com
www2.example.com
[dbservers]
db1.example.com
On a task that's run on the dbservers
group, I need a list of all IPs from the webserver
group (this makes querying facts directly tricky as facts may not have been gathered for those hosts) - in this case it would need to extract:
- 192.168.0.10
- 192.168.0.11
- '2a00:abcd:1234::100'
- '2a00:abcd:1234::101'
- '2a00:abcd:1234::200'
- '2a00:abcd:1234::201'
The tasks will do things like configure firewall and DB access, along the lines of:
- name: Allow web server access to DB server
ufw:
rule: allow
name: mysql
from_ip: "{{ item }}"
loop: "{{ <loop expression goes here> }}"
It's what's in the loop
expression that I'm having trouble with.
There are two parts to the query: extract the list of hosts, and then gather the ip addresses - doing it separately for ipv4 and ipv6 is fine.
I can get part way there with expressions like:
{{ server_ips | map('extract', groups['webservers']) }}
{{ server_ips | intersect(groups['webservers']) }}
However, both of these appear to flatten the result so though they find the right items, the ipv4
and ipv6
list elements are not there, so I can't proceed to the next step. Swapping the lists in these didn't help either.
The subelements
lookup seems a good way to get the IPs parts (though I actually need sub-subelements) and skip empty entries, but I can't see how to get to that point.
How should I do this lookup?
Upvotes: 0
Views: 1379
Reputation: 23866
You try to reinvent functionality, Ansible provides already. You define your DIY inventory although Ansible has already an inventory. And you define your DIY inventory iteration although Ansible knows how to iterate over its inventory.
If you want to assign data to individual hosts use the host_vars
directory as shown in the Best Practices.
host_vars/www1.example.com.yml
:
ipv4:
- 192.168.0.10
- 192.168.0.11
ipv6:
- '2a00:abcd:1234::100'
- '2a00:abcd:1234::101'
host_vars/www2.example.com.yml
:
ipv4:
ipv6:
- '2a00:abcd:1234::200'
- '2a00:abcd:1234::201'
Then you define a task for each host and use the {{ipv4}}
or {{ipv6}}
lists for anything you want to do.
If you need to execute actions on a different host like a firewall, use Ansible's delegation.
This extracts all IP addresses from your server_ips
dictionary:
- hosts: localhost
connection: local
gather_facts: no
vars:
server_ips:
www1.example.com:
ipv4:
- 192.168.0.10
- 192.168.0.11
ipv6:
- '2a00:abcd:1234::100'
- '2a00:abcd:1234::101'
www2.example.com:
ipv4:
ipv6:
- '2a00:abcd:1234::200'
- '2a00:abcd:1234::201'
xyz.example.com:
ipv4:
- 192.168.1.2
ipv6:
ipv4: >-
{% set ipv4 = [] -%}
{% for ips in server_ips.values() | selectattr ('ipv4') | map (attribute='ipv4') | list -%}
{% for ip in ips -%}
{% set _ = ipv4.append(ip) -%}
{% endfor -%}
{% endfor -%}
{{ ipv4 }}
ipv6: >-
{% set ipv6 = [] -%}
{% for ips in server_ips.values() | selectattr ('ipv6') | map (attribute='ipv6') | list -%}
{% for ip in ips -%}
{% set _ = ipv6.append(ip) -%}
{% endfor -%}
{% endfor -%}
{{ ipv6 }}
ips: >-
{{ ipv4 + ipv6 }}
tasks:
- debug: var=server_ips
- debug: var=ipv4
- debug: var=ipv6
- debug: var=ips
But in order to build firewall rules you have to build a cross product. You have to iterate for each destination over all sources to get all rules.
Upvotes: 1