Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

variables in hostvars are not templated #7844

Closed
ktosiek opened this issue Jun 19, 2014 · 16 comments
Closed

variables in hostvars are not templated #7844

ktosiek opened this issue Jun 19, 2014 · 16 comments
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release

Comments

@ktosiek
Copy link
Contributor

ktosiek commented Jun 19, 2014

Issue Type: Bug Report
Ansible Version: ansible 1.7 (devel d0dd4ac)
Environment: N/A
Summary:

Values comming from hostvars should go through similar variable expansion process as all the others.

Steps To Reproduce:

$ ansible-playbook -i vars_inv hostvars_example.yml

vars_inv:

localhost ansible_connection=local varA="{{ some_var }}"
otherhost ansible_connection=local varA="{{ varB }}" varB="{{ other_var }}"

hostvars_example.yml:

- hosts: all
  gather_facts: no
  vars:
    some_var: "some value"
    other_var: "other value"
  tasks:
  - debug: "msg='localhost: {{ hostvars['localhost'].varA }}'"
  - debug: "msg='otherhost: {{ hostvars['otherhost'].varA }}'"
Expected Results:
TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] => {
    "msg": "localhost: some value"
}
ok: [otherhost] => {
    "msg": "localhost: some value"
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] => {
    "msg": "otherhost: other value"
}
ok: [otherhost] => {
    "msg": "otherhost: other value"
}
Actual Results:
TASK: [debug msg='localhost: {{hostvars['localhost'].varA}}'] ***************** 
ok: [localhost] => {
    "msg": "localhost: {{some_var}}"
}
ok: [otherhost] => {
    "msg": "localhost: {{some_var}}"
}

TASK: [debug msg='otherhost: {{hostvars['otherhost'].varA}}'] ***************** 
ok: [localhost] => {
    "msg": "otherhost: {{varB}}"
}
ok: [otherhost] => {
    "msg": "otherhost: {{varB}}"
}
Other notes:

The expected result above was created with help of the patch below.
I'm afraid it's not usable as is, that's why I'm submitting this as an issue and not a PR - if you think otherwise I'll happily resubmit it as one

diff --git a/lib/ansible/utils/template.py b/lib/ansible/utils/template.py
index 7848dd1..47772c6 100644
--- a/lib/ansible/utils/template.py
+++ b/lib/ansible/utils/template.py
@@ -139,6 +139,42 @@ def template(basedir, varname, vars, lookup_fatal=True, depth=0, expand_lists=Tr
             return varname


+class _jinja2_host_vars(object):
+    '''Wrapper around HostVars to make sure they are templated properly'''
+    def __init__(self, hostvars, vars):
+        self.vars = vars
+        self.hostvars = hostvars
+
+    def __contains__(self, k):
+        return k in self.hostvars
+
+    def __getitem__(self, hostname):
+        return _jinja2_vars(
+            self.vars.basedir,
+            chained_dicts(self.hostvars[hostname], self.vars),
+            self.vars.globals,
+            self.vars.fail_on_undefined)
+
+
+class chained_dicts(object):
+    def __init__(self, *dicts):
+        self.dicts = dicts
+
+    def __contains__(self, k):
+        return any(k in d for d in self.dicts)
+
+    def __getitem__(self, k):
+        for d in self.dicts[:-1]:
+            try:
+                return d[k]
+            except KeyError:
+                pass
+        return self.dicts[-1][k]
+
+    def keys(self):
+        return sorted({k for d in self.dicts for k in d.keys()})
+
+
 class _jinja2_vars(object):
     '''
     Helper class to template all variable content before jinja2 sees it.
@@ -179,7 +215,7 @@ class _jinja2_vars(object):
         var = self.vars[varname]
         # HostVars is special, return it as-is
         if isinstance(var, dict) and type(var) != dict:
-            return var
+            return _jinja2_host_vars(var, self)
         else:
             return template(self.basedir, var, self.vars, fail_on_undefined=self.fail_on_undefined)
@jimi-c jimi-c added P3 labels Jun 19, 2014
@jimi-c
Copy link
Member

jimi-c commented Jun 19, 2014

Inventory variables are for simple variables, if you want to do more complex things like this you should put the variables in host_vars/group_vars, where this will work as expected. If you have any further questions regarding this, please let us know.

@jimi-c jimi-c closed this as completed Jun 19, 2014
@ktosiek
Copy link
Contributor Author

ktosiek commented Jun 19, 2014

This issue is not about inventory variables.
It doesn't matter if I put them in host_vars, group_vars or in the inventory (and I did check with host_vars just now) - it's about the way they are accessed.

My point is that the magic variable named "hostvars" should act like any other dictionary - it should run the values through jinja2 templating. At the moment it doesn't.
I think I understand why it gets special treatment - forcing all variables for all hosts each time something in hostvars is accessed wouldn't scale too well. But I think it shouldn't be THAT special, as it renders preparing data for collection in one place pretty hard.

@dorkmatt
Copy link

Would love to see this addressed, as I ran into the same problem (where I populated host_vars/group_vars files with complex yaml). Again, variables are interpolated with templates but not via the hostvars[] call.

@hughsaunders
Copy link
Contributor

I have also come across this problem, simple example here: https://gist.github.com/hughsaunders/517b7a1867cb5e87e7ad

I think it would be great if accessing a variable directly always gave the same result as accessing it via hostvars. Would you mind having another look @jimi-c ?

@jimi-c jimi-c reopened this Sep 1, 2014
@jimi-c jimi-c added P2 and removed P3 labels Sep 1, 2014
@jimi-c
Copy link
Member

jimi-c commented Sep 1, 2014

We'll take another look at this.

@jimi-c
Copy link
Member

jimi-c commented Sep 11, 2014

@hughsaunders / @ktosiek I've just pushed the above commit to my personal repo, if either of you'd like to give it a try. My approach was to template the variables directly in the HostVars class when fetching the item (where it is cached too) so hopefully that will reduce the impact of templating. This also includes some refactoring of the runner code where we build the injectable variables for the task.

@hughsaunders
Copy link
Contributor

@jimi-c Thanks so much for working on this, sadly your patch didn't work for me, see inline comment jimi-c@128cb3b#commitcomment-7766169

jimi-c added a commit to jimi-c/ansible that referenced this issue Sep 15, 2014
@jimi-c
Copy link
Member

jimi-c commented Sep 15, 2014

@hughsaunders I've updated the commit per your note and re-pushed, if you'd like to retest it now. Beyond that typo, was everything else working for you?

@hughsaunders
Copy link
Contributor

@jimi-c 7914989 works for me, thanks!

@jimi-c jimi-c closed this as completed in 4a9cf3f Sep 16, 2014
@jimi-c
Copy link
Member

jimi-c commented Sep 16, 2014

Closing This Ticket

Hi!

We believe the above commit should resolve this problem for you. This will also be included in the next major release.

If you continue seeing any problems related to this issue, or if you have any further questions, please let us know by stopping by one of the two mailing lists, as appropriate:

Because this project is very active, we're unlikely to see comments made on closed tickets, but the mailing list is a great way to ask questions, or post if you don't think this particular issue is resolved.

Thank you!

@hughsaunders
Copy link
Contributor

There was a regression shortly after the fix for this issue was landed. Hostvars are expanded correctly in 4a9cf3f, but expansion is broken from e9229cf. @jimi-c any chance we can get this issue reopened?

Example:

In the examples, note the abbreviated SHAs in [].

Working, y is defined as {{x}} which is correctly exapanded to value_of_x:

cb 0 (.venv) [(4a9cf3f...) 4a9cf3f] ~/git/ansible/test-playbook
$ ansible-playbook -i inventory test.yml

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [debug ] ****************************************************************
ok: [localhost] => {
    "hostvars['localhost']['y']": "value_of_x"
}

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

Not working, {{x}} is not expanded:

cb 0 (.venv) [(e9229cf...) e9229cf] ~/git/ansible/test-playbook
$ ansible-playbook -i inventory test.yml

PLAY [localhost] **************************************************************

GATHERING FACTS ***************************************************************
ok: [localhost]

TASK: [debug ] ****************************************************************
ok: [localhost] => {
    "hostvars['localhost']['y']": "{{x}}"
}

PLAY RECAP ********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0

inventory/group_vars/g1.yml:

---
x: "value_of_x"
y: "{{x}}"

playbook:

---
- hosts: localhost
  tasks:
    - debug:
        var: hostvars['localhost']['y']

@jimi-c
Copy link
Member

jimi-c commented Nov 25, 2014

@hughsaunders if you're still seeing this, could you please open a new issue (if you have not done so already)? Thanks!

@hughsaunders
Copy link
Contributor

continuing this discussion in #8213

openstack-gerrit pushed a commit to openstack/openstack-ansible that referenced this issue Dec 5, 2014
Cinder requires temporary working space to convert images. This patch
exposes cinder_volume_lv_size_gb to the user config file, so the user
can decide how large the cinder volumes container should be based on
available space and the size of images that will need to be converted.

cinder_volume_lv_size_gb is used to override container_lvm_fssize in
group_vars/cinder_volume. Simple enough but doesn't work because
templated variables (or indirect variables) are not expanded when
accessed via hostvars[] see: ansible/ansible#7844. In order to work
around that, I have eliminated hostvars[] usage from the container
creation mechanism. This may have positive speed implications as the
limit of container creation parallelism is now forks rather than number
of hosts. However it does make this change larger than a small bug fix.

Also note that this patch makes use of delegate_to, so specific ansible
versions must be used to avoid ansible/ansible#8705. Our requirements
file currently specifies a version before this bug was introduced.

There are two commits in this PR as one is the actual bugfix, the other
is infrastructure changes required for that bugfix to work. Also only
the bugfix may be needed if the upstream bugs are fixed.

Closes-Bug: #1399427
Change-Id: I2b5c5e692d3d72b603fdd6298475cb76c52c66df
jimi-c added a commit that referenced this issue Jun 28, 2015
Also adds play information into the hostvars creation, to assure the
variable manager used there has access to vars and vars_files

Fixes #9501
Fixes #8213
Fixes #7844
@jimi-c
Copy link
Member

jimi-c commented Jun 28, 2015

Closing This Ticket

Hi!

We believe the above commit should resolve this problem for you. This will also be included in the next major release.

If you continue seeing any problems related to this issue, or if you have any further questions, please let us know by stopping by one of the two mailing lists, as appropriate:

Because this project is very active, we're unlikely to see comments made on closed tickets, but the mailing list is a great way to ask questions, or post if you don't think this particular issue is resolved.

Thank you!

@michaelw
Copy link
Contributor

still seeing the same behavior: hostvars are not template expanded:

PLAY [all] *********************************************************************

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": "localhost: {{ some_var }}"
}
ok: [otherhost] => {
    "msg": "localhost: {{ some_var }}"
}

TASK [debug] *******************************************************************
ok: [localhost] => {
    "msg": "otherhost: {{ varB }}"
}
ok: [otherhost] => {
    "msg": "otherhost: {{ varB }}"
}

PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=0    unreachable=0    failed=0   
otherhost                  : ok=2    changed=0    unreachable=0    failed=0   

Playbook run took 0 days, 0 hours, 0 minutes, 0 seconds
[60726]% ansible --version
ansible 2.1.0.0 (instart/master fa59d7fa45) last updated 2016/06/26 09:45:25 (GMT -700)

@jimi-c
Copy link
Member

jimi-c commented Aug 31, 2016

@michaelw this fix was included in 2.1.1.

@ansibot ansibot added bug This issue/PR relates to a bug. and removed bug_report labels Mar 6, 2018
@ansible ansible locked and limited conversation to collaborators Apr 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug This issue/PR relates to a bug. P2 Priority 2 - Issue Blocks Release
Projects
None yet
Development

No branches or pull requests

6 participants