Skip to content

Commit

Permalink
Only template each hostvars var on-demand (fixes ansible#33259)
Browse files Browse the repository at this point in the history
  • Loading branch information
agaffney authored and bcoca committed Feb 15, 2018
1 parent 0f89302 commit dae737c
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 12 deletions.
7 changes: 6 additions & 1 deletion lib/ansible/parsing/yaml/dumper.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from ansible.module_utils.six import PY3
from ansible.parsing.yaml.objects import AnsibleUnicode, AnsibleSequence, AnsibleMapping, AnsibleVaultEncryptedUnicode
from ansible.utils.unsafe_proxy import AnsibleUnsafeText
from ansible.vars.hostvars import HostVars
from ansible.vars.hostvars import HostVars, HostVarsVars


class AnsibleDumper(yaml.SafeDumper):
Expand Down Expand Up @@ -63,6 +63,11 @@ def represent_vault_encrypted_unicode(self, data):
represent_hostvars,
)

AnsibleDumper.add_representer(
HostVarsVars,
represent_hostvars,
)

AnsibleDumper.add_representer(
AnsibleSequence,
yaml.representer.SafeRepresenter.represent_list,
Expand Down
4 changes: 2 additions & 2 deletions lib/ansible/plugins/filter/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
from ansible.utils.hashing import md5s, checksum_s
from ansible.utils.unicode import unicode_wrap
from ansible.utils.vars import merge_hash
from ansible.vars.hostvars import HostVars
from ansible.vars.hostvars import HostVars, HostVarsVars


UUID_NAMESPACE_ANSIBLE = uuid.UUID('361E6D51-FAEC-444A-9079-341386DA8E2E')
Expand All @@ -67,7 +67,7 @@ class AnsibleJSONEncoder(json.JSONEncoder):
types like HostVars
'''
def default(self, o):
if isinstance(o, HostVars):
if isinstance(o, (HostVars, HostVarsVars)):
return dict(o)
elif isinstance(o, (datetime.date, datetime.datetime)):
return o.isoformat()
Expand Down
6 changes: 3 additions & 3 deletions lib/ansible/template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import re
import time

from collections import Sequence
from collections import Sequence, Mapping
from functools import wraps
from io import StringIO
from numbers import Number
Expand Down Expand Up @@ -356,7 +356,7 @@ def _clean_data(self, orig_data):
clean_list.append(self._clean_data(list_item))
ret = clean_list

elif isinstance(orig_data, dict):
elif isinstance(orig_data, (dict, Mapping)):
clean_dict = {}
for k in orig_data:
clean_dict[self._clean_data(k)] = self._clean_data(orig_data[k])
Expand Down Expand Up @@ -509,7 +509,7 @@ def template(self, variable, convert_bare=False, preserve_trailing_newlines=True
overrides=overrides,
disable_lookups=disable_lookups,
) for v in variable]
elif isinstance(variable, dict):
elif isinstance(variable, (dict, Mapping)):
d = {}
# we don't use iteritems() here to avoid problems if the underlying dict
# changes sizes due to the templating, which can happen with hostvars
Expand Down
35 changes: 29 additions & 6 deletions lib/ansible/vars/hostvars.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
except ImportError:
from sha import sha as sha1

__all__ = ['HostVars']
__all__ = ['HostVars', 'HostVarsVars']


# Note -- this is a Mapping, not a MutableMapping
Expand Down Expand Up @@ -86,11 +86,9 @@ def raw_get(self, host_name):

def __getitem__(self, host_name):
data = self.raw_get(host_name)
sha1_hash = sha1(to_bytes(data)).hexdigest()
if sha1_hash not in self._cached_result:
templar = Templar(variables=data, loader=self._loader)
self._cached_result[sha1_hash] = templar.template(data, fail_on_undefined=False, static_vars=STATIC_VARS)
return self._cached_result[sha1_hash]
if isinstance(data, Undefined):
return data
return HostVarsVars(data, loader=self._loader)

def set_host_variable(self, host, varname, value):
self._variable_manager.set_host_variable(host, varname, value)
Expand All @@ -117,3 +115,28 @@ def __repr__(self):
for host in self._inventory.hosts:
out[host] = self.get(host)
return repr(out)


class HostVarsVars(collections.Mapping):

def __init__(self, variables, loader):
self._vars = variables
self._loader = loader

def __getitem__(self, var):
templar = Templar(variables=self._vars, loader=self._loader)
foo = templar.template(self._vars[var], fail_on_undefined=False, static_vars=STATIC_VARS)
return foo

def __contains__(self, var):
return (var in self._vars)

def __iter__(self):
for var in self._vars.keys():
yield var

def __len__(self):
return len(self._vars.keys())

def __repr__(self):
return repr(self._vars)

0 comments on commit dae737c

Please sign in to comment.