ansible/lib/ansible/vars/hostvars.py
James Cammarata 81aa12eb1b Rework how the Conditional class deals with undefined vars
Previously, the Conditional class did a simple check when an
AnsibleUndefinedVariable error was raised to see if certain strings were
present. This patch tries to be smarter by evaluating the variable contained
in the error string and compared to the defined/not defined conditionals in
the conditional string.

This also modifies the UndefinedError message from HostVars slightly to
match the format returned jinja2 in general, making it easier to match the
error message in the Conditional code.

Fixes #18514
2016-11-22 08:29:58 -06:00

115 lines
4 KiB
Python

# (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
# Make coding more python3-ish
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import collections
from jinja2.exceptions import UndefinedError
from ansible import constants as C
from ansible.template import Templar
STATIC_VARS = [
'inventory_hostname', 'inventory_hostname_short',
'inventory_file', 'inventory_dir', 'playbook_dir',
'ansible_play_hosts', 'play_hosts', 'groups',
'ungrouped', 'group_names', 'ansible_version', 'omit', 'role_names'
]
try:
from hashlib import sha1
except ImportError:
from sha import sha as sha1
__all__ = ['HostVars']
# Note -- this is a Mapping, not a MutableMapping
class HostVars(collections.Mapping):
''' A special view of vars_cache that adds values from the inventory when needed. '''
def __init__(self, inventory, variable_manager, loader):
self._lookup = dict()
self._inventory = inventory
self._loader = loader
self._variable_manager = variable_manager
variable_manager._hostvars = self
self._cached_result = dict()
def set_variable_manager(self, variable_manager):
self._variable_manager = variable_manager
variable_manager._hostvars = self
def set_inventory(self, inventory):
self._inventory = inventory
def _find_host(self, host_name):
if host_name in C.LOCALHOST and self._inventory.localhost:
host = self._inventory.localhost
else:
host = self._inventory.get_host(host_name)
return host
def raw_get(self, host_name):
'''
Similar to __getitem__, however the returned data is not run through
the templating engine to expand variables in the hostvars.
'''
host = self._find_host(host_name)
if host is None:
raise UndefinedError("'hostvars[\"%s\"]' is undefined" % host_name)
return self._variable_manager.get_vars(loader=self._loader, host=host, include_hostvars=False)
def __getitem__(self, host_name):
data = self.raw_get(host_name)
sha1_hash = sha1(str(data).encode('utf-8')).hexdigest()
if sha1_hash in self._cached_result:
result = self._cached_result[sha1_hash]
else:
templar = Templar(variables=data, loader=self._loader)
result = templar.template(data, fail_on_undefined=False, static_vars=STATIC_VARS)
self._cached_result[sha1_hash] = result
return result
def set_host_variable(self, host, varname, value):
self._variable_manager.set_host_variable(host, varname, value)
def set_nonpersistent_facts(self, host, facts):
self._variable_manager.set_nonpersistent_facts(host, facts)
def set_host_facts(self, host, facts):
self._variable_manager.set_host_facts(host, facts)
def __contains__(self, host_name):
return self._find_host(host_name) is not None
def __iter__(self):
for host in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
yield host.name
def __len__(self):
return len(self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True))
def __repr__(self):
out = {}
for host in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
name = host.name
out[name] = self.get(name)
return repr(out)