special dict hostvars

Hi,

We’re used to do things like

hostvars[inventory_hostname].somevar

Now, I wanted to debug someting and did

ansible all -m debug -a var=hostvars

I expected this to return something like

{ ‘hostvars’:

‘host1’:

‘somevar’ : somevalue

‘host2’:


}

Instead I got

host1:

{ ‘somevar’ : somevalue

}

host2:

Another example is at https://p.6core.net/p/RCJAYyK1gzBuEcwZa8Tgx4S5

Now i know ‘hostvars’ is a special kind of object (some kind of dict that caches things), but I’m confused on this difference, and have no explanation for it.

Serge

In code the difference is mostly that hostvars is a 'cache' object
that internally implements most (but not all, it seems) the methods of
a dictionary. The default cache is an actual in memory dictionary but
the object still acts as an intermediary.

There is probably a way to make it behave exactly the same in this
case, but I would need to look into it deeper.

In code the difference is mostly that hostvars is a 'cache' object
that internally implements most (but not all, it seems) the methods of
a dictionary. The default cache is an actual in memory dictionary but
the object still acts as an intermediary.

​Yes, that's what special about hostvars in the code. But as to what the
'hostvars' var is in ansible playbooks,
we're still missing ​the difference between two things in your assertion
here.

It sounds like something weird though.

There is probably a way to make it behave exactly the same in this

case, but I would need to look into it deeper.

​Given time I'll try to have a look myself. Perhaps this is part a thing
for -devel, as it seems to me

BTW, another example of how this is weird, just run these commands from an
empty dir:

serge@cyberlab:~/tmp$ ansible all -i localhost, -c local -m debug -a

var=hostvars[inventory_hostname]

localhost | success >> {

    "hostvars[inventory_hostname]": {
        "group_names": ,
        "inventory_hostname": "localhost",
        "inventory_hostname_short": "localhost"
    }
}

serge@cyberlab:~/tmp$ ansible all -i localhost, -c local -m debug -a
var=hostvars
localhost | success >> {
    "hostvars": {
        "group_names": ,
        "inventory_hostname": "localhost",
        "inventory_hostname_short": "localhost"
    }
}

Thanks,

   Serge

The question then is, how do I loop over the hostvars for hosts in a template?
The original question popped up today, because I was trying something which
used to work:

{% for host, v in hostvars.iteritems() %}
{{ host }} == {{ v[‘ansible_distribution_version’] }} … etc.
{% endfor %}

I know this worked for hosts because I copied it from an old presentation I did.
Nowadays (1.8.2) it no longer works: because of the extra keys in hostvars.

Regards,

-JP

i have this from an old template:

{% for host in groups['all'] %}
{% for k in hostvars[host] %} ....

Thanks Brian, but that also no longer works, at least not for me.

I've put up an example here: https://p.6core.net/p/yIT8nVBZXlOJyZERHoxA3Rmz

Am I misunderstanding what hostvars _should_ contain?

Regards,

        -JP

AFAIK, hostvars should be a dict of dicts, where the keys are the
inventory_hostnames.
The fact that host variables are put directly in hostvars, is a bug, I
think.​

Looking at the code, I think there was confusion between 'hostvars' as we
know to use it in playbooks and templates, which is a variable in the
'inject',
and hostvars, a host specific vari containg the hosts' variables, and used
in the Runner() object.

This might fix it:

diff --git a/lib/ansible/runner/__init__.py b/lib/ansible/runner/__init__.py
index 79a167c..501dc41 100644
--- a/lib/ansible/runner/__init__.py
+++ b/lib/ansible/runner/__init__.py
@@ -684,7 +684,8 @@ class Runner(object):

         # and we save the HostVars in the injected dictionary so they
         # may be referenced from playbooks/templates
- inject['hostvars'] = hostvars
+ inject['hostvars'] = {}
+ inject['hostvars'][host] = hostvars

         host_connection = inject.get('ansible_connection', self.transport)
         if host_connection in [ 'paramiko', 'ssh', 'accelerate' ]:

You can check out my branch here to test that:

  https://github.com/srvg/ansible/tree/fix_inject_hostvars

Serge

You can check out my branch here to test that:

As mentioned to you privately, this reports KeyError '127.0.0.1' on a
template which contains

        {% for host in hostvars %}
        {{ host }}
        {% endfor %}

Regards,

        -JP

I just tested this and it seems to work fine in current devel:

----dump.j2
{% for host in groups['all'] %}

{{host}}:
{% for k in hostvars[host] %}
    {{k}}: {{ hostvars[host][k]}}
{% endfor %}
{% endfor %}
--- play.yml

- hosts: localhost
  tasks:
    - template: src=templates/dump.j2 dest=/tmp/dump.txt
      connection: local