Dynamically set /etc/hosts using ansible_interfaces

I’m trying to dynamically set /etc/hosts based on the ipv4 address assigned to all the interfaces on a host… my template reads as such

127.0.0.1 localhost.localdomain localhost

{% for item in ansible_interfaces if item != ‘lo’ %}

{{ ‘ansible_’ + item + ‘.ipv4.address’ }} {{ lookup(‘dig’, ‘ansible_’ + item + ‘.ipv4.address’ + ‘/PTR’) }}

{% endfor %}

which results in :

127.0.0.1 localhost.localdomain localhost

ansible_eth1.ipv4.address NXDOMAIN

ansible_eth0.ipv4.address NXDOMAIN

I’ve tried several iterations, but I’m struggling to find the proper syntax to get the file to write as expected… is this possible? Any suggestions greatly appreciated.

You seem to expect that the resulting string of {{ 'ansible_' + item +
'.ipv4.address' }} will then be 'retemplated', that won't happen

Also, don't use 'item' as that is reserved for ansible loops, the
following is the say to 'dynamically' access host facts:

{% for interface in ansible_interfaces if item != 'lo' %}
{{ hostvars[inventory_hostname]['ansible_' + interface+ '.ipv4.address'] }} ...

Changing to this format results in:

FAILED! => {“changed”: false, “failed”: true, “msg”: “AnsibleUndefinedVariable: ‘dict object’ has no attribute u’ansible_eth1.ipv4.address’”}

yet if I check ansible_eth1 I see:

{u’macaddress’: u’00:21:f6:f2:c1:21’, u’pciid’: u’vif-0’, u’module’: u’xen_netfront’, u’mtu’: 1500, u’device’: u’eth1’, u’promisc’: False, u’ipv4’: {u’broadcast’: u’10.224.103.255’, u’netmask’: u’255.255.248.0’, u’network’: u’10.224.96.0’, u’address’: u’10.224.97.202’}, u’active’: True, u’type’: u’ether’}

So the current template looks like this:

{% for interface in ansible_interfaces if interface != ‘lo’ %}
{{ hostvars[inventory_hostname][‘ansible_’ + interface + ‘.ipv4.address’] }} {{ lookup(‘dig’, hostvars[inventory_hostname][‘ansible_’ + interface + ‘.ipv4.address’] + ‘/PTR’) }}
{% endfor %}

I can’t see anything wrong here, and the address is there and available…any other thoughts?

cannot have . inside

hostvars[inventory_hostname]['ansible_' + interface]['ipv4.address']

Ah…that makes sense, but unfortunately it still doesn’t work for me…

template:

{% for interface in ansible_interfaces if interface != ‘lo’ %}
{{ hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4.address’] }} {{ lookup(‘dig’, hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4.address’] + ‘/PTR’) }}
{% endfor %}

results of ansible-playbook run:

FAILED! => {“changed”: false, “failed”: true, “msg”: “AnsibleUndefinedVariable: ‘dict object’ has no attribute ‘ipv4.address’”}

Thanks for the quick replies…

missed a dot ['ipv4.address'] => ['ipv4']['address']

Ah…perfect…thanks! Yes, that works - although it does have a trailing ‘.’ at the end of the line and blank lines between the entries, which I’ll have to figure out how to strip.

Thanks again

Final solution:

{% for interface in ansible_interfaces if interface != ‘lo’ %}{{ hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4’][‘address’] }} {{ lookup(‘dig’, hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4’][‘address’] + ‘/PTR’).rstrip(‘.’) }} {{ lookup(‘dig’, hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4’][‘address’] + ‘/PTR’).split(‘.’)[0] }}
{% endfor %}

results in :
1.2.3.4 hostname.mydomain.com hostname
2.3.4.5 hostname-alt.otherdomain.com hostname-alt

This of course assumes that DNS has been properly set beforehand…

Thanks Dayton for you final solution.
If I use it, I notice that the privat IP wasn’t set correctly; like this:

192.168.1.1
1.2.3.4 hostname hostname.domain.com

So I use this solution
{% for interface in ansible_interfaces if interface != ‘lo’ %}
{{ hostvars[inventory_hostname][‘ansible_’ + interface][‘ipv4’][‘address’] }} {{ inventory_hostname_short }} {{ inventory_hostname }}
{% endfor %}

and now it’s ok for me

192.168.1.1 hostname hostname.domain.com
1.2.3.4 hostname hostname.domain.com

thanks