using hostvars not resolving variables "calculated" from facts

Using Ansible 1.5.3

I’m having some trouble with a situation where I have a variable that I’d like to use in a template that uses “hostvars” but the variable in question is calculated from facts.

I have a template that looks like this:

{% for host in groups[‘uat4-webapp’]|sort %}

BalancerMember ajp://{{ hostvars[host].webapp_ip }}:8009 loadfactor=20

{% endfor %}

The “webapp_ip” variable is calculated like this in a group_vars file:

webapp_ip: “{{ ansible_eth0.ipv4.address }}”

When I compile the template above, it ends up looking like this:

BalancerMember ajp://{{ ansible_eth0.ipv4.address }}:8009 loadfactor=20

When I expected it to look like this:

BalancerMember ajp://10.10.1.36:8009 loadfactor=20

The reason for this “webapp_ip” variable as opposed to using the facts directly is because I need it set to different things in different places (one group of hosts has it at ansible_eth0.ipv4.address, another at ansible_bond0.ipv4.address, etc).

Is this something that should work, or should I be looking to accomplish this a different way?

thanks!
matt

Excerpts from Matt Coddington's message of 2014-03-24 22:58:50 -0400:

Using Ansible 1.5.3

I'm having some trouble with a situation where I have a variable that I'd
like to use in a template that uses "hostvars" but the variable in question
is calculated from facts.

I have a template that looks like this:

{% for host in groups['uat4-webapp']|sort %}
BalancerMember ajp://{{ hostvars[host].webapp_ip }}:8009 loadfactor=20
{% endfor %}

The "webapp_ip" variable is calculated like this in a group_vars file:

webapp_ip: "{{ ansible_eth0.ipv4.address }}"

Is this something that should work, or should I be looking to accomplish
this a different way?

Ansible seems to evaluate somewhat lazily, so "{{ansible_eth0.ipv4.address}}"
get evaluated in the context of the current host.

You'll probably need to be explicit:

    {{ hostvars[host].ansible_eth0.ipv4.address }}

Thanks Morgan,
That's what i was afraid of... you're correct that explicitly referencing
those does work, and I can do this in the template by having different
cases for the different sets of servers (e.g. "if prod, use
ansible_bond0.ipv4.address, if staging use ansible_eth0.ipv4.address,
etc"). I was just hoping there was a way to abstract that if/then logic
into a common variable outside the template.

thanks,
matt

Excerpts from Matt Coddington's message of 2014-03-25 14:15:18 -0400:

> > {% for host in groups['uat4-webapp']|sort %}
> > BalancerMember ajp://{{ hostvars[host].webapp_ip }}:8009 loadfactor=20
> > {% endfor %}
> >
> > The "webapp_ip" variable is calculated like this in a group_vars file:
> >
> > webapp_ip: "{{ ansible_eth0.ipv4.address }}"
> {{ hostvars[host].ansible_eth0.ipv4.address }}
That's what i was afraid of... you're correct that explicitly referencing
those does work, and I can do this in the template by having different
cases for the different sets of servers (e.g. "if prod, use
ansible_bond0.ipv4.address, if staging use ansible_eth0.ipv4.address,
etc"). I was just hoping there was a way to abstract that if/then logic
into a common variable outside the template.

Idea, untested:

In your template:

    {% for host in groups['uat-webapp']|sort %}
    BalancerMember ajp://{{ hostvars[host][hostvars[host][webapp_iface]].ipv4.address }}
    {% endfor %}

In your group_vars file:

    webapp_iface: "ansible_bond0"

Or what have you.

Or this might work:

Leave your template as is, and try putting in your group_vars file:

    webapp_ip: "{{ hostvars[inventory_hostname].ansible_eth0.ipv4.address }}"

Also, you might want to look into setting your own facts, as those are
per-host.

Hey Morgan,
Thanks so much for this… all very good ideas, I see how they may work outside of the current way I was thinking about this problem… will definitely try a few out and let the list know where I land :slight_smile:

matt

I would not expect you would need to do this above workaround if the current variable had an eth0 address.

If you are in fact seeing that, please file a ticket and we can take a look.

I would not expect you would need to do this above workaround if the
current variable had an eth0 address.

Thanks Michael... I don't understand what "the current variable having an
eth0 address" means... you mean the current host i'm referring to in
hostvars? it definitely has an eth0 address and if that should in fact
work i can submit a ticket with a pretty minimal setup to reproduce i think.

thanks,
matt

I mean if it’s the current host in the host loop, for which you are writing the template.

(You shouldn’t have to indirect through the hostvars structure unless you need the variables for another host).

gotcha, yeah i need the variables from another host so sounds like the indirect method is the way to go.

thanks!
matt

Right, looking back at the first post, I wouldn’t expect webapp_ip to be something in hostvars like that.

BalancerMember ajp://{{ hostvars[host][hostvars[host][webapp_iface]].ipv4.address }}

is the way to go

It can be shortened with a “set statement” in Jinja2 if you want, though, if you plan to reuse it throughout the file