Hi Stefan,
thanks for your answer. I busied myself with working around the issue
temporarily because I had to get a project forward.
On Tue, Apr 28, 2020 at 09:48:23AM +0200, Stefan Hornburg (Racke) wrote:
> On 4/27/20 10:12 AM, Marc Haber wrote:
> > Let's assume I have a service that can listen on different combinations
> > or host/port:
> >
>
> Hello Marc,
>
> nice to see you here :-). This is certainly doable:
>
> ---
> - hosts: all
>
> vars:
> host:
> service:
> hostname: "
google.com"
> ports:
> - "80"
> - "443"
> tasks:
> - name: Get DNS records
> set_fact:
> ip_addresses: "{{ ip_addresses | default([]) + lookup('dig', host.service.hostname + '/' + item, wantlist=True) }}"
> loop:
> - A
> - AAAA
> when: "'listen' not in host.service"
This sets a local fact to the list of IP addreses pulled from DNS.
Adding the IPv6 addresses is an easy enogh exercise.
> - name: Determine all combinations of IP address and ports
> set_fact:
> ips_ports: "{{ ip_addresses | product(host.service.ports) | list }}"
> when: "'listen' not in host.service"
That's magic. Cute, indeed. This returns a list, alternating between IP
adress and port, like [ ip1, port1, ip2, port2, ip3, port3 ]
> - name: Turn list members into dictionaries
> set_fact:
> host:
> service:
> listen: "{{ host.service.listen | default([]) + [{ 'ip': item[0], 'port': item[1] }] }}"
> loop: "{{ ips_ports }}"
> when: ips_ports is defined
This is where you're losing me. You're iterating over an array and get
array members, so item will be "ip1" in the first iteration, "port1" in
the second, "ip2" in the third. How can you index into an array
element???
Anyway, things have become a bit more complicated since my original
request. This is what I have now:
service:
sites:
default:
protocols:
- "http"
- "https"
listen:
- ip: "2a01:4f8:161:3541::32:100"
port: "80"
protocol: "http"
- ip: "2a01:4f8:161:3541::32:100"
port: "443"
protocol: "https"
The templates pull site configuration from the "sites" part of the
definition; the listen configuration from the "listen" part of the
definition. This is a strongly simplified example with reality being
much more complex, I just reduced this to the relevant parts. This is,
however, from a working setup (and yes, it's IPv6 only).
For better readability, I'd like to write this like:
service:
sites:
default:
protocols:
- "http"
- "https"
listen:
- ip: "2a01:4f8:161:3541::32:100"
With the possibility of defining explicit ports and protocols in the
listen clause. I think to massage one format into the other one in a
task is awfully hard, and to use local facts makes it impossible to
override the automatism by explicitly writing the desired result to
inventory proper.
Can a custom inventory plugin access what the previously running
inventory plugins have parsed, and can it augment structure that was
build by the predecessors? This way, I could have all processing power
and flexibility of imperative programming. I could think of a gazillion
of other places where this could be useful to simplify my templates
_AND_ my inventory.
Having this done inside ansible would allow me to take advantage of the
inventory reading logic that is already present in ansible. I could
write a preprocessor writing out the "augmented inventory" before
ansible is started, but I'd have to manually process the inventory file
-and- the contents of the host_vars and group_vars directories. I'd like
to avoid this.