variables and their (multiple layer) resolution

161 views
Skip to first unread message

Dmitry Makovey

unread,
Jun 13, 2014, 4:45:09 PM6/13/14
to ansible...@googlegroups.com
I have a playbook, and in one of the templates I need to walk over all the hosts in certain groups grabbing their hostvars etc.

I followed http://docs.ansible.com/faq.html and everything seems to work as expected but with one quirk:

In template I have:

     {% for host in groups['nova'] %}
      {% set myvars=hostvars[host] %}
      <service name="nova" type="compute" disabled="False" description="" order="1">
          <user name="{{ myvars.service_user }}" password="{{ myvars.service_user_password }}" email="{{ myvars.service_user_email }}"/>
          <mysql user="{{ myvars.service_mysql_user }}" password="{{ myvars.service_mysql_user_password }}"/>
          <host name="{{ host }}"/>
          <endpoint>
            <address type="admin" host="{{ myvars.service_admin_ip }}" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s" />
            <address type="public" host="{{ myvars.service_public_ip }}" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s"/>
            <address type="internal" host="{{ myvars.service_internal_ip }}" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s"/>
          </endpoint>
          <region name="{{ myvars.service_region }}"/>
      </service>
     {% endfor %}

in group_vars/nova.yml I have:

...
service_admin_ip: 192.168.0.138
service_public_ip: "{{ ansible_default_ipv4.address }}"
service_internal_ip: "{{ ansible_default_ipv4.address }}"
service_region: RegionOne
...

so as a result I get:

       <service name="nova" type="compute" disabled="False" description="" order="1">
          <user name="nova" password="nova" email="no...@example.com"/>
          <mysql user="nova" password="nova"/>
          <host name="192.168.0.138"/>
          <endpoint>
            <address type="admin" host="192.168.0.138" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s" />
            <address type="public" host="{{ansible_default_ipv4.address}}" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s"/>
            <address type="internal" host="{{ansible_default_ipv4.address}}" uri_suff=":$(compute_port)s/v1.1/$(tenant_id)s"/>
          </endpoint>
          <region name="RegionOne"/>

Note that service_public_ip did not get resolved as expected (even though facts were collected for that host already), and service_admin_ip works fine when I hardcode it and don't use facts. Is there a way to get around this issue? I do not want to drop ansible_default_ipv4 stuff into the role's playbook but prefer to keep it "configurable" with some sane default (default_ipv4). What am I missing in this?

P.S.
I have some other plays where group_vars/all.yml defines vars in similar fashion (referencing some of the facts) and those work just fine.

Dmitry Makovey

unread,
Jun 16, 2014, 12:05:26 PM6/16/14
to ansible...@googlegroups.com
Shall I assume that below is a bug?

Dmitry Makovey

unread,
Jun 16, 2014, 7:12:00 PM6/16/14
to ansible...@googlegroups.com
Looks like it's some sort of problem with templates, consider this simplified playbook:

roles/keystone/tasks/main.yml:

---

- name: test template
  template: src=template.j2 dest=/tmp/template.txt

- name: test simple var
  debug: var=simple_var

- name: test nested var
  debug: var=nested_var

- name: test fact var
  debug: var=fact_var

and roles/keystone/templates/templatej.j2:
-------
{{ simple_var }}
{{ nested_var }}
{{ fact_var }}

Var extraction:

{% for h in groups['keystone'] %}
 {% set myvars=hostvars[h] %}
  {{ myvars.simple_var }}
  {{ myvars.nested_var }}
  {{ myvars.fact_var }}
{% endfor %}

-------

the playbook output is as expected - vars all rendered properly , however templates are not so clean:
-------
Simple var
Simple var
192.168.0.138

Var extraction:

   Simple var
  {{simple_var}}
  {{ansible_default_ipv4.address}}

-------

as you can see when iterating over hosts from the group for some reason vars are not being resolved as expected. 

Tomasz Kontusz

unread,
Jun 17, 2014, 1:46:35 AM6/17/14
to ansible...@googlegroups.com


Dmitry Makovey <droop...@gmail.com> napisał:
The reason is: multiple layers of resolution are implemented as a magic dictionary that renders the variables on access. For some reason it explicitly ignores fancy dictionaries, and hostvars is one of those (it has some magic for caching).
I think it's a bug, but that should be decided by devs. Still, filling an issue should be OK :-)

--
Wysłane za pomocą K-9 Mail.

Michael DeHaan

unread,
Jun 17, 2014, 8:48:58 AM6/17/14
to ansible...@googlegroups.com
There was a ticket raised (possibly by you) recently that recursion in Jinja2 templates does not appear to be a thing.   Since I think that's filed already, we should be good to go, but I would consider this as something that should work.

As an alternative, if you put the variable name directly in the template, or use the "set_fact" module to assign the variable to a new name, you should be ok.


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/ansible-project/1f44b0bd-5b90-49e4-8156-30684931a0c7%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dmitry Makovey

unread,
Jun 17, 2014, 11:08:30 AM6/17/14
to ansible...@googlegroups.com


On Tuesday, June 17, 2014 6:48:58 AM UTC-6, Michael DeHaan wrote:
There was a ticket raised (possibly by you) recently that recursion in Jinja2 templates does not appear to be a thing.   Since I think that's filed already, we should be good to go, but I would consider this as something that should work.

yes, I did file a ticket ( https://github.com/ansible/ansible/issues/7799 ) as I was digging deeper into this issue it felt more and more like a bug, but I was hoping it was just me {ab,mis}using it. 
 
As an alternative, if you put the variable name directly in the template, or use the "set_fact" module to assign the variable to a new name, you should be ok.

thanks for the tip - I did test this workaround and it works, albeit it puts quite a few extra items in a playbook to work around the issue.  Just for posterity trick like that worked for me so far:

# prepend to site.yml ...
- hosts: keystone
  tasks:
  - set_fact: nested_var={{ nested_var }}
...

makes variable resolution inside Jinja template consistent with resolution in playbook. Ain't pretty but it works. 

Bonus question: can I write above statement using "with_items" so at least I don't have to copy-paste entire stanza and only have to enumerate var names? (my guess is "no" but hey, maybe I'm wrong?)

Michael DeHaan

unread,
Jun 17, 2014, 3:00:33 PM6/17/14
to ansible...@googlegroups.com
"Bonus question: can I write above statement using "with_items" so at least I don't have to copy-paste entire stanza and only have to enumerate var names? (my guess is "no" but hey, maybe I'm wrong?)"

This seems to be another topic, so let's start a new thread on that, though seems easy for you to try first and maybe post if it's not :)




--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.

Michael DeHaan

unread,
Jun 17, 2014, 3:08:01 PM6/17/14
to ansible...@googlegroups.com
It was discussed recently that in templates variables were not being recursive as they should be.

This is something we will look into.


--
You received this message because you are subscribed to the Google Groups "Ansible Project" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ansible-proje...@googlegroups.com.
To post to this group, send email to ansible...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages