special dict hostvars

215 views
Skip to first unread message

Serge van Ginderachter

unread,
Feb 18, 2015, 1:46:26 PM2/18/15
to ansible...@googlegroups.com, Jan-Piet Mens
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:
       ....

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

Brian Coca

unread,
Feb 18, 2015, 2:07:56 PM2/18/15
to ansible...@googlegroups.com, Jan-Piet Mens
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.
--
Brian Coca

Serge van Ginderachter

unread,
Feb 18, 2015, 2:33:38 PM2/18/15
to ansible...@googlegroups.com, Jan-Piet Mens
On 18 February 2015 at 20:07, Brian Coca <bc...@ansible.com> wrote:
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

jpmen...@googlemail.com

unread,
Feb 18, 2015, 3:20:25 PM2/18/15
to ansible...@googlegroups.com
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

Brian Coca

unread,
Feb 18, 2015, 5:25:48 PM2/18/15
to ansible...@googlegroups.com
i have this from an old template:

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


--
Brian Coca

Jan-Piet Mens

unread,
Feb 19, 2015, 2:48:32 AM2/19/15
to ansible...@googlegroups.com
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

Serge van Ginderachter

unread,
Feb 19, 2015, 3:29:21 AM2/19/15
to ansible...@googlegroups.com

On 19 February 2015 at 08:48, Jan-Piet Mens <jpmen...@gmail.com> wrote:
Am I misunderstanding what hostvars _should_ contain?


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

Jan-Piet Mens

unread,
Feb 19, 2015, 4:29:17 AM2/19/15
to ansible...@googlegroups.com
> 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

Brian Coca

unread,
Feb 19, 2015, 8:11:01 AM2/19/15
to ansible...@googlegroups.com
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
Reply all
Reply to author
Forward
0 new messages