|
|
|
@ -27,16 +27,14 @@ from hashlib import sha1
|
|
|
|
|
from jinja2.exceptions import UndefinedError
|
|
|
|
|
|
|
|
|
|
from ansible import constants as C
|
|
|
|
|
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleFileNotFound, AnsibleAssertionError, AnsibleTemplateError
|
|
|
|
|
from ansible.errors import AnsibleError, AnsibleParserError, AnsibleUndefinedVariable, AnsibleFileNotFound, AnsibleAssertionError
|
|
|
|
|
from ansible.inventory.host import Host
|
|
|
|
|
from ansible.inventory.helpers import sort_groups, get_group_vars
|
|
|
|
|
from ansible.module_utils.common.text.converters import to_text
|
|
|
|
|
from ansible.module_utils.six import text_type, string_types
|
|
|
|
|
from ansible.plugins.loader import lookup_loader
|
|
|
|
|
from ansible.module_utils.six import text_type
|
|
|
|
|
from ansible.vars.fact_cache import FactCache
|
|
|
|
|
from ansible.template import Templar
|
|
|
|
|
from ansible.utils.display import Display
|
|
|
|
|
from ansible.utils.listify import listify_lookup_plugin_terms
|
|
|
|
|
from ansible.utils.vars import combine_vars, load_extra_vars, load_options_vars
|
|
|
|
|
from ansible.utils.unsafe_proxy import wrap_var
|
|
|
|
|
from ansible.vars.clean import namespace_facts, clean_facts
|
|
|
|
@ -161,6 +159,11 @@ class VariableManager:
|
|
|
|
|
on the functionality they provide. These arguments may be removed at a later date without a deprecation
|
|
|
|
|
period and without warning.
|
|
|
|
|
'''
|
|
|
|
|
if include_delegate_to:
|
|
|
|
|
display.deprecated(
|
|
|
|
|
"`VariableManager.get_vars`'s argument `include_delegate_to` has no longer any effect.",
|
|
|
|
|
version="2.19",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
display.debug("in VariableManager get_vars()")
|
|
|
|
|
|
|
|
|
@ -363,11 +366,6 @@ class VariableManager:
|
|
|
|
|
continue
|
|
|
|
|
except AnsibleParserError:
|
|
|
|
|
raise
|
|
|
|
|
else:
|
|
|
|
|
# if include_delegate_to is set to False or we don't have a host, we ignore the missing
|
|
|
|
|
# vars file here because we're working on a delegated host or require host vars, see NOTE above
|
|
|
|
|
if include_delegate_to and host:
|
|
|
|
|
raise AnsibleFileNotFound("vars file %s was not found" % vars_file_item)
|
|
|
|
|
except (UndefinedError, AnsibleUndefinedVariable):
|
|
|
|
|
if host is not None and self._fact_cache.get(host.name, dict()).get('module_setup') and task is not None:
|
|
|
|
|
raise AnsibleUndefinedVariable("an undefined variable was found when attempting to template the vars_files item '%s'"
|
|
|
|
@ -430,11 +428,6 @@ class VariableManager:
|
|
|
|
|
# has to be copy, otherwise recursive ref
|
|
|
|
|
all_vars['vars'] = all_vars.copy()
|
|
|
|
|
|
|
|
|
|
# if we have a host and task and we're delegating to another host,
|
|
|
|
|
# figure out the variables for that host now so we don't have to rely on host vars later
|
|
|
|
|
if task and host and task.delegate_to is not None and include_delegate_to:
|
|
|
|
|
all_vars['ansible_delegated_vars'], all_vars['_ansible_loop_cache'] = self._get_delegated_vars(play, task, all_vars)
|
|
|
|
|
|
|
|
|
|
display.debug("done with get_vars()")
|
|
|
|
|
if C.DEFAULT_DEBUG:
|
|
|
|
|
# Use VarsWithSources wrapper class to display var sources
|
|
|
|
@ -546,7 +539,6 @@ class VariableManager:
|
|
|
|
|
play=task.get_play(),
|
|
|
|
|
host=delegated_host,
|
|
|
|
|
task=task,
|
|
|
|
|
include_delegate_to=False,
|
|
|
|
|
include_hostvars=True,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
@ -554,141 +546,6 @@ class VariableManager:
|
|
|
|
|
|
|
|
|
|
return delegated_vars, delegated_host_name
|
|
|
|
|
|
|
|
|
|
def _get_delegated_vars(self, play, task, existing_variables):
|
|
|
|
|
# This method has a lot of code copied from ``TaskExecutor._get_loop_items``
|
|
|
|
|
# if this is failing, and ``TaskExecutor._get_loop_items`` is not
|
|
|
|
|
# then more will have to be copied here.
|
|
|
|
|
# TODO: dedupe code here and with ``TaskExecutor._get_loop_items``
|
|
|
|
|
# this may be possible once we move pre-processing pre fork
|
|
|
|
|
|
|
|
|
|
if not hasattr(task, 'loop'):
|
|
|
|
|
# This "task" is not a Task, so we need to skip it
|
|
|
|
|
return {}, None
|
|
|
|
|
|
|
|
|
|
display.deprecated(
|
|
|
|
|
'Getting delegated variables via get_vars is no longer used, and is handled within the TaskExecutor.',
|
|
|
|
|
version='2.18',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# we unfortunately need to template the delegate_to field here,
|
|
|
|
|
# as we're fetching vars before post_validate has been called on
|
|
|
|
|
# the task that has been passed in
|
|
|
|
|
vars_copy = existing_variables.copy()
|
|
|
|
|
|
|
|
|
|
# get search path for this task to pass to lookup plugins
|
|
|
|
|
vars_copy['ansible_search_path'] = task.get_search_path()
|
|
|
|
|
|
|
|
|
|
# ensure basedir is always in (dwim already searches here but we need to display it)
|
|
|
|
|
if self._loader.get_basedir() not in vars_copy['ansible_search_path']:
|
|
|
|
|
vars_copy['ansible_search_path'].append(self._loader.get_basedir())
|
|
|
|
|
|
|
|
|
|
templar = Templar(loader=self._loader, variables=vars_copy)
|
|
|
|
|
|
|
|
|
|
items = []
|
|
|
|
|
has_loop = True
|
|
|
|
|
if task.loop_with is not None:
|
|
|
|
|
if task.loop_with in lookup_loader:
|
|
|
|
|
fail = True
|
|
|
|
|
if task.loop_with == 'first_found':
|
|
|
|
|
# first_found loops are special. If the item is undefined then we want to fall through to the next
|
|
|
|
|
fail = False
|
|
|
|
|
try:
|
|
|
|
|
loop_terms = listify_lookup_plugin_terms(terms=task.loop, templar=templar, fail_on_undefined=fail, convert_bare=False)
|
|
|
|
|
|
|
|
|
|
if not fail:
|
|
|
|
|
loop_terms = [t for t in loop_terms if not templar.is_template(t)]
|
|
|
|
|
|
|
|
|
|
mylookup = lookup_loader.get(task.loop_with, loader=self._loader, templar=templar)
|
|
|
|
|
|
|
|
|
|
# give lookup task 'context' for subdir (mostly needed for first_found)
|
|
|
|
|
for subdir in ['template', 'var', 'file']: # TODO: move this to constants?
|
|
|
|
|
if subdir in task.action:
|
|
|
|
|
break
|
|
|
|
|
setattr(mylookup, '_subdir', subdir + 's')
|
|
|
|
|
|
|
|
|
|
items = wrap_var(mylookup.run(terms=loop_terms, variables=vars_copy))
|
|
|
|
|
|
|
|
|
|
except AnsibleTemplateError:
|
|
|
|
|
# This task will be skipped later due to this, so we just setup
|
|
|
|
|
# a dummy array for the later code so it doesn't fail
|
|
|
|
|
items = [None]
|
|
|
|
|
else:
|
|
|
|
|
raise AnsibleError("Failed to find the lookup named '%s' in the available lookup plugins" % task.loop_with)
|
|
|
|
|
elif task.loop is not None:
|
|
|
|
|
try:
|
|
|
|
|
items = templar.template(task.loop)
|
|
|
|
|
except AnsibleTemplateError:
|
|
|
|
|
# This task will be skipped later due to this, so we just setup
|
|
|
|
|
# a dummy array for the later code so it doesn't fail
|
|
|
|
|
items = [None]
|
|
|
|
|
else:
|
|
|
|
|
has_loop = False
|
|
|
|
|
items = [None]
|
|
|
|
|
|
|
|
|
|
# since host can change per loop, we keep dict per host name resolved
|
|
|
|
|
delegated_host_vars = dict()
|
|
|
|
|
item_var = getattr(task.loop_control, 'loop_var', 'item')
|
|
|
|
|
cache_items = False
|
|
|
|
|
for item in items:
|
|
|
|
|
# update the variables with the item value for templating, in case we need it
|
|
|
|
|
if item is not None:
|
|
|
|
|
vars_copy[item_var] = item
|
|
|
|
|
|
|
|
|
|
templar.available_variables = vars_copy
|
|
|
|
|
delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
|
|
|
|
|
if delegated_host_name != task.delegate_to:
|
|
|
|
|
cache_items = True
|
|
|
|
|
if delegated_host_name is None:
|
|
|
|
|
raise AnsibleError(message="Undefined delegate_to host for task:", obj=task._ds)
|
|
|
|
|
if not isinstance(delegated_host_name, string_types):
|
|
|
|
|
raise AnsibleError(message="the field 'delegate_to' has an invalid type (%s), and could not be"
|
|
|
|
|
" converted to a string type." % type(delegated_host_name), obj=task._ds)
|
|
|
|
|
|
|
|
|
|
if delegated_host_name in delegated_host_vars:
|
|
|
|
|
# no need to repeat ourselves, as the delegate_to value
|
|
|
|
|
# does not appear to be tied to the loop item variable
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
# now try to find the delegated-to host in inventory, or failing that,
|
|
|
|
|
# create a new host on the fly so we can fetch variables for it
|
|
|
|
|
delegated_host = None
|
|
|
|
|
if self._inventory is not None:
|
|
|
|
|
delegated_host = self._inventory.get_host(delegated_host_name)
|
|
|
|
|
# try looking it up based on the address field, and finally
|
|
|
|
|
# fall back to creating a host on the fly to use for the var lookup
|
|
|
|
|
if delegated_host is None:
|
|
|
|
|
for h in self._inventory.get_hosts(ignore_limits=True, ignore_restrictions=True):
|
|
|
|
|
# check if the address matches, or if both the delegated_to host
|
|
|
|
|
# and the current host are in the list of localhost aliases
|
|
|
|
|
if h.address == delegated_host_name:
|
|
|
|
|
delegated_host = h
|
|
|
|
|
break
|
|
|
|
|
else:
|
|
|
|
|
delegated_host = Host(name=delegated_host_name)
|
|
|
|
|
else:
|
|
|
|
|
delegated_host = Host(name=delegated_host_name)
|
|
|
|
|
|
|
|
|
|
# now we go fetch the vars for the delegated-to host and save them in our
|
|
|
|
|
# master dictionary of variables to be used later in the TaskExecutor/PlayContext
|
|
|
|
|
delegated_host_vars[delegated_host_name] = self.get_vars(
|
|
|
|
|
play=play,
|
|
|
|
|
host=delegated_host,
|
|
|
|
|
task=task,
|
|
|
|
|
include_delegate_to=False,
|
|
|
|
|
include_hostvars=True,
|
|
|
|
|
)
|
|
|
|
|
delegated_host_vars[delegated_host_name]['inventory_hostname'] = vars_copy.get('inventory_hostname')
|
|
|
|
|
|
|
|
|
|
_ansible_loop_cache = None
|
|
|
|
|
if has_loop and cache_items:
|
|
|
|
|
# delegate_to templating produced a change, so we will cache the templated items
|
|
|
|
|
# in a special private hostvar
|
|
|
|
|
# this ensures that delegate_to+loop doesn't produce different results than TaskExecutor
|
|
|
|
|
# which may reprocess the loop
|
|
|
|
|
_ansible_loop_cache = items
|
|
|
|
|
|
|
|
|
|
return delegated_host_vars, _ansible_loop_cache
|
|
|
|
|
|
|
|
|
|
def clear_facts(self, hostname):
|
|
|
|
|
'''
|
|
|
|
|
Clears the facts for a host
|
|
|
|
|