Issue 1:
Issue Type: “Bug Report”

Ansible Version: ansible 1.7.2

Environment: Fedora/Centos

Summary:

In order to configure two servers (backend & frontend) I need to expose the backend variables in order to configure the frontend playbook. After that ansible-playbook is run and no variables defined in group_vars, it doesn't expand the nested variables.

Steps To Reproduce:

Create group_vars/all

backend_version: 2.0.0

Create group_vars/backend

backend_version: 2.3.0
backend_foldername: application
backend_name: "{{ backend_foldername }}-{{ backend_version }}"

Create hosts

[frontend]
test-server1 backend_server=test-server2
[backend]
test-server2

Create site.yml

---
- name: Configure and deploy Frontend
hosts: frontend
roles:
- frontend

- name: Configure and deploy Backend
hosts: backend
roles:
- backend

Create role/frontend/tasks/main.yml

---
- name: Does expand properties
debug: msg={{ hostvars[item]['backend_version'] }}
with_items: backend_server

- name: Does not expand nested properties
debug: msg={{ hostvars[item]['backend_name'] }}
with_items: backend_server

Results:

PLAY [Configure and deploy Frontend] ****************

GATHERING FACTS ***************************************************************
ok: [test-server1]

TASK: [frontend | Does expand properties] *****************************************************
ok: [test-server1] => (item=test-server2) => {
"item": "test-server2",
"msg": " 2.3.0"
}

TASK: [frontend | Does not expand nested properties] *****************************************************
ok: [test-server1] => (item=test-server2) => {
"item": "test-server2",
"msg": "{{adaptersuite_foldername}}-{{adaptersuite_version}}"
}

Expected Results:

PLAY [Configure and deploy Frontend] ****************

GATHERING FACTS ***************************************************************
ok: [test-server1]

TASK: [frontend | Does expand properties] *****************************************************
ok: [test-server1] => (item=test-server2) => {
"item": "test-server2",
"msg": " 2.3.0"
}

TASK: [frontend | Does not expand nested properties] *****************************************************
ok: [test-server1] => (item=test-server2) => {
"item": "test-server2",
"msg": " 2.3.0-application"
}

It doesn't expand those nested properties, likely because they aren't part of the visibility of the test-server1. But I guess just using {{ hostvars[item]['backend_name'] }} it will expand those properties...

Any ideas?

Issue 2:
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)