mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
Make sure magic variables are available when templating vars_files
Also does some reorganization/cleanup on the magic vars/delegated variable generation portions of VariableManager to make the above possible. Fixes #12633
This commit is contained in:
parent
284bd118a0
commit
d91b3c6b9d
2 changed files with 119 additions and 102 deletions
|
@ -486,7 +486,7 @@ class TaskExecutor:
|
||||||
del variables[i]
|
del variables[i]
|
||||||
# now replace the interpreter values with those that may have come
|
# now replace the interpreter values with those that may have come
|
||||||
# from the delegated-to host
|
# from the delegated-to host
|
||||||
delegated_vars = variables.get('ansible_delegated_vars', dict())
|
delegated_vars = variables.get('ansible_delegated_vars', dict()).get(self._host.name, dict())
|
||||||
if isinstance(delegated_vars, dict):
|
if isinstance(delegated_vars, dict):
|
||||||
for i in delegated_vars:
|
for i in delegated_vars:
|
||||||
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
||||||
|
|
|
@ -188,6 +188,14 @@ class VariableManager:
|
||||||
return VARIABLE_CACHE[cache_entry]
|
return VARIABLE_CACHE[cache_entry]
|
||||||
|
|
||||||
all_vars = defaultdict(dict)
|
all_vars = defaultdict(dict)
|
||||||
|
magic_variables = self._get_magic_variables(
|
||||||
|
loader=loader,
|
||||||
|
play=play,
|
||||||
|
host=host,
|
||||||
|
task=task,
|
||||||
|
include_hostvars=include_hostvars,
|
||||||
|
include_delegate_to=include_delegate_to,
|
||||||
|
)
|
||||||
|
|
||||||
if play:
|
if play:
|
||||||
# first we compile any vars specified in defaults/main.yml
|
# first we compile any vars specified in defaults/main.yml
|
||||||
|
@ -247,9 +255,10 @@ class VariableManager:
|
||||||
all_vars = combine_vars(all_vars, play.get_vars())
|
all_vars = combine_vars(all_vars, play.get_vars())
|
||||||
|
|
||||||
for vars_file_item in play.get_vars_files():
|
for vars_file_item in play.get_vars_files():
|
||||||
# create a set of temporary vars here, which incorporate the
|
# create a set of temporary vars here, which incorporate the extra
|
||||||
# extra vars so we can properly template the vars_files entries
|
# and magic vars so we can properly template the vars_files entries
|
||||||
temp_vars = combine_vars(all_vars, self._extra_vars)
|
temp_vars = combine_vars(all_vars, self._extra_vars)
|
||||||
|
temp_vars = combine_vars(all_vars, magic_variables)
|
||||||
templar = Templar(loader=loader, variables=temp_vars)
|
templar = Templar(loader=loader, variables=temp_vars)
|
||||||
|
|
||||||
# we assume each item in the list is itself a list, as we
|
# we assume each item in the list is itself a list, as we
|
||||||
|
@ -302,19 +311,34 @@ class VariableManager:
|
||||||
all_vars = combine_vars(all_vars, self._nonpersistent_fact_cache.get(host.name, dict()))
|
all_vars = combine_vars(all_vars, self._nonpersistent_fact_cache.get(host.name, dict()))
|
||||||
|
|
||||||
all_vars = combine_vars(all_vars, self._extra_vars)
|
all_vars = combine_vars(all_vars, self._extra_vars)
|
||||||
|
all_vars = combine_vars(all_vars, magic_variables)
|
||||||
|
|
||||||
# FIXME: make sure all special vars are here
|
# if we have a task and we're delegating to another host, figure out the
|
||||||
# Finally, we create special vars
|
# variables for that host now so we don't have to rely on hostvars later
|
||||||
|
if task and task.delegate_to is not None and include_delegate_to:
|
||||||
|
all_vars['ansible_delegated_vars'] = self._get_delegated_vars(loader, play, task, all_vars)
|
||||||
|
|
||||||
all_vars['playbook_dir'] = loader.get_basedir()
|
#VARIABLE_CACHE[cache_entry] = all_vars
|
||||||
|
|
||||||
|
debug("done with get_vars()")
|
||||||
|
return all_vars
|
||||||
|
|
||||||
|
def _get_magic_variables(self, loader, play, host, task, include_hostvars, include_delegate_to):
|
||||||
|
'''
|
||||||
|
Returns a dictionary of so-called "magic" variables in Ansible,
|
||||||
|
which are special variables we set internally for use.
|
||||||
|
'''
|
||||||
|
|
||||||
|
variables = dict()
|
||||||
|
variables['playbook_dir'] = loader.get_basedir()
|
||||||
|
|
||||||
if host:
|
if host:
|
||||||
all_vars['group_names'] = [group.name for group in host.get_groups()]
|
variables['group_names'] = [group.name for group in host.get_groups()]
|
||||||
|
|
||||||
if self._inventory is not None:
|
if self._inventory is not None:
|
||||||
all_vars['groups'] = dict()
|
variables['groups'] = dict()
|
||||||
for (group_name, group) in iteritems(self._inventory.groups):
|
for (group_name, group) in iteritems(self._inventory.groups):
|
||||||
all_vars['groups'][group_name] = [h.name for h in group.get_hosts()]
|
variables['groups'][group_name] = [h.name for h in group.get_hosts()]
|
||||||
|
|
||||||
if include_hostvars:
|
if include_hostvars:
|
||||||
hostvars_cache_entry = self._get_cache_entry(play=play)
|
hostvars_cache_entry = self._get_cache_entry(play=play)
|
||||||
|
@ -323,115 +347,108 @@ class VariableManager:
|
||||||
else:
|
else:
|
||||||
hostvars = HostVars(play=play, inventory=self._inventory, loader=loader, variable_manager=self)
|
hostvars = HostVars(play=play, inventory=self._inventory, loader=loader, variable_manager=self)
|
||||||
HOSTVARS_CACHE[hostvars_cache_entry] = hostvars
|
HOSTVARS_CACHE[hostvars_cache_entry] = hostvars
|
||||||
all_vars['hostvars'] = hostvars
|
variables['hostvars'] = hostvars
|
||||||
|
variables['vars'] = hostvars[host.get_name()]
|
||||||
|
|
||||||
if task:
|
if task:
|
||||||
if task._role:
|
if task._role:
|
||||||
all_vars['role_path'] = task._role._role_path
|
variables['role_path'] = task._role._role_path
|
||||||
|
|
||||||
# if we have a task and we're delegating to another host, figure out the
|
|
||||||
# variables for that host now so we don't have to rely on hostvars later
|
|
||||||
if task.delegate_to is not None and include_delegate_to:
|
|
||||||
# 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
|
|
||||||
templar = Templar(loader=loader, variables=all_vars)
|
|
||||||
|
|
||||||
items = []
|
|
||||||
if task.loop is not None:
|
|
||||||
if task.loop in lookup_loader:
|
|
||||||
#TODO: remove convert_bare true and deprecate this in with_
|
|
||||||
try:
|
|
||||||
loop_terms = listify_lookup_plugin_terms(terms=task.loop_args, templar=templar, loader=loader, fail_on_undefined=True, convert_bare=True)
|
|
||||||
except AnsibleUndefinedVariable as e:
|
|
||||||
if 'has no attribute' in str(e):
|
|
||||||
loop_terms = []
|
|
||||||
self._display.deprecated("Skipping task due to undefined attribute, in the future this will be a fatal error.")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
items = lookup_loader.get(task.loop, loader=loader, templar=templar).run(terms=loop_terms, variables=all_vars)
|
|
||||||
else:
|
|
||||||
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % task.loop)
|
|
||||||
else:
|
|
||||||
items = [None]
|
|
||||||
|
|
||||||
vars_copy = all_vars.copy()
|
|
||||||
delegated_host_vars = dict()
|
|
||||||
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'] = item
|
|
||||||
|
|
||||||
templar.set_available_variables(vars_copy)
|
|
||||||
delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
|
|
||||||
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
|
|
||||||
|
|
||||||
# a dictionary of variables to use if we have to create a new host below
|
|
||||||
new_delegated_host_vars = dict(
|
|
||||||
ansible_host=delegated_host_name,
|
|
||||||
ansible_user=C.DEFAULT_REMOTE_USER,
|
|
||||||
ansible_connection=C.DEFAULT_TRANSPORT,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 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_and_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 or h.name in C.LOCALHOST and delegated_host_name in C.LOCALHOST:
|
|
||||||
delegated_host = h
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
delegated_host = Host(name=delegated_host_name)
|
|
||||||
delegated_host.vars.update(new_delegated_host_vars)
|
|
||||||
else:
|
|
||||||
delegated_host = Host(name=delegated_host_name)
|
|
||||||
delegated_host.vars.update(new_delegated_host_vars)
|
|
||||||
|
|
||||||
# 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(
|
|
||||||
loader=loader,
|
|
||||||
play=play,
|
|
||||||
host=delegated_host,
|
|
||||||
task=task,
|
|
||||||
include_delegate_to=False,
|
|
||||||
include_hostvars=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
all_vars['ansible_delegated_vars'] = delegated_host_vars
|
|
||||||
|
|
||||||
if self._inventory is not None:
|
if self._inventory is not None:
|
||||||
all_vars['inventory_dir'] = self._inventory.basedir()
|
variables['inventory_dir'] = self._inventory.basedir()
|
||||||
if play:
|
if play:
|
||||||
# add the list of hosts in the play, as adjusted for limit/filters
|
# add the list of hosts in the play, as adjusted for limit/filters
|
||||||
# DEPRECATED: play_hosts should be deprecated in favor of ansible_play_hosts,
|
# DEPRECATED: play_hosts should be deprecated in favor of ansible_play_hosts,
|
||||||
# however this would take work in the templating engine, so for now
|
# however this would take work in the templating engine, so for now
|
||||||
# we'll add both so we can give users something transitional to use
|
# we'll add both so we can give users something transitional to use
|
||||||
host_list = [x.name for x in self._inventory.get_hosts()]
|
host_list = [x.name for x in self._inventory.get_hosts()]
|
||||||
all_vars['play_hosts'] = host_list
|
variables['play_hosts'] = host_list
|
||||||
all_vars['ansible_play_hosts'] = host_list
|
variables['ansible_play_hosts'] = host_list
|
||||||
|
|
||||||
# the 'omit' value alows params to be left out if the variable they are based on is undefined
|
# the 'omit' value alows params to be left out if the variable they are based on is undefined
|
||||||
all_vars['omit'] = self._omit_token
|
variables['omit'] = self._omit_token
|
||||||
all_vars['ansible_version'] = CLI.version_info(gitinfo=False)
|
variables['ansible_version'] = CLI.version_info(gitinfo=False)
|
||||||
|
|
||||||
if 'hostvars' in all_vars and host:
|
return variables
|
||||||
all_vars['vars'] = all_vars['hostvars'][host.get_name()]
|
|
||||||
|
|
||||||
#VARIABLE_CACHE[cache_entry] = all_vars
|
def _get_delegated_vars(self, loader, play, task, existing_variables):
|
||||||
|
# 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()
|
||||||
|
templar = Templar(loader=loader, variables=vars_copy)
|
||||||
|
|
||||||
debug("done with get_vars()")
|
items = []
|
||||||
return all_vars
|
if task.loop is not None:
|
||||||
|
if task.loop in lookup_loader:
|
||||||
|
#TODO: remove convert_bare true and deprecate this in with_
|
||||||
|
try:
|
||||||
|
loop_terms = listify_lookup_plugin_terms(terms=task.loop_args, templar=templar, loader=loader, fail_on_undefined=True, convert_bare=True)
|
||||||
|
except AnsibleUndefinedVariable as e:
|
||||||
|
if 'has no attribute' in str(e):
|
||||||
|
loop_terms = []
|
||||||
|
self._display.deprecated("Skipping task due to undefined attribute, in the future this will be a fatal error.")
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
items = lookup_loader.get(task.loop, loader=loader, templar=templar).run(terms=loop_terms, variables=vars_copy)
|
||||||
|
else:
|
||||||
|
raise AnsibleError("Unexpected failure in finding the lookup named '%s' in the available lookup plugins" % task.loop)
|
||||||
|
else:
|
||||||
|
items = [None]
|
||||||
|
|
||||||
|
delegated_host_vars = dict()
|
||||||
|
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'] = item
|
||||||
|
|
||||||
|
templar.set_available_variables(vars_copy)
|
||||||
|
delegated_host_name = templar.template(task.delegate_to, fail_on_undefined=False)
|
||||||
|
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
|
||||||
|
|
||||||
|
# a dictionary of variables to use if we have to create a new host below
|
||||||
|
new_delegated_host_vars = dict(
|
||||||
|
ansible_host=delegated_host_name,
|
||||||
|
ansible_user=C.DEFAULT_REMOTE_USER,
|
||||||
|
ansible_connection=C.DEFAULT_TRANSPORT,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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_and_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 or h.name in C.LOCALHOST and delegated_host_name in C.LOCALHOST:
|
||||||
|
delegated_host = h
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
delegated_host = Host(name=delegated_host_name)
|
||||||
|
delegated_host.vars.update(new_delegated_host_vars)
|
||||||
|
else:
|
||||||
|
delegated_host = Host(name=delegated_host_name)
|
||||||
|
delegated_host.vars.update(new_delegated_host_vars)
|
||||||
|
|
||||||
|
# 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(
|
||||||
|
loader=loader,
|
||||||
|
play=play,
|
||||||
|
host=delegated_host,
|
||||||
|
task=task,
|
||||||
|
include_delegate_to=False,
|
||||||
|
include_hostvars=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
return delegated_host_vars
|
||||||
|
|
||||||
def _get_inventory_basename(self, path):
|
def _get_inventory_basename(self, path):
|
||||||
'''
|
'''
|
||||||
|
|
Loading…
Reference in a new issue