mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-04-24 11:21:25 -07:00
Refactoring delegate_to code
Now, instead of relying on hostvars on the executor side, we compile the vars for the delegated to host in a special internal variable and have the PlayContext object look for things there when applying task/ var overrides, which is much cleaner and takes advantage of the code already dealing with all of the magic variable variations. Fixes #12127 Fixes #12079
This commit is contained in:
parent
fa69e8ebb8
commit
1f5584aa5b
3 changed files with 55 additions and 77 deletions
|
@ -474,7 +474,18 @@ class TaskExecutor:
|
||||||
self._play_context.remote_addr = self._host.address
|
self._play_context.remote_addr = self._host.address
|
||||||
|
|
||||||
if self._task.delegate_to is not None:
|
if self._task.delegate_to is not None:
|
||||||
self._compute_delegate(variables)
|
# since we're delegating, we don't want to use interpreter values
|
||||||
|
# which would have been set for the original target host
|
||||||
|
for i in variables.keys():
|
||||||
|
if i.startswith('ansible_') and i.endswith('_interpreter'):
|
||||||
|
del variables[i]
|
||||||
|
# now replace the interpreter values with those that may have come
|
||||||
|
# from the delegated-to host
|
||||||
|
delegated_vars = variables.get('ansible_delegated_vars', dict())
|
||||||
|
if isinstance(delegated_vars, dict):
|
||||||
|
for i in delegated_vars:
|
||||||
|
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
||||||
|
variables[i] = delegated_vars[i]
|
||||||
|
|
||||||
conn_type = self._play_context.connection
|
conn_type = self._play_context.connection
|
||||||
if conn_type == 'smart':
|
if conn_type == 'smart':
|
||||||
|
@ -529,74 +540,3 @@ class TaskExecutor:
|
||||||
|
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
def _compute_delegate(self, variables):
|
|
||||||
|
|
||||||
# get the vars for the delegate by its name
|
|
||||||
try:
|
|
||||||
self._display.debug("Delegating to %s" % self._task.delegate_to)
|
|
||||||
if self._task.delegate_to in C.LOCALHOST and self._task.delegate_to not in variables['hostvars']:
|
|
||||||
this_info = dict(ansible_connection="local")
|
|
||||||
for alt_local in C.LOCALHOST:
|
|
||||||
if alt_local in variables['hostvars']:
|
|
||||||
this_info = variables['hostvars'][self._task.delegate_to]
|
|
||||||
if this_info == Undefined:
|
|
||||||
this_info = dict(ansible_connection="local")
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
this_info = variables['hostvars'][self._task.delegate_to]
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
# make sure the inject is empty for non-inventory hosts
|
|
||||||
this_info = {}
|
|
||||||
self._display.debug("Delegate to lookup failed due to: %s" % str(e))
|
|
||||||
|
|
||||||
conn = this_info.get('ansible_connection')
|
|
||||||
if conn:
|
|
||||||
self._play_context.connection = conn
|
|
||||||
if conn in ('smart', 'paramiko'):
|
|
||||||
# smart and paramiko connections will be using some kind of ssh,
|
|
||||||
# so use 'ssh' for the string to check connection variables
|
|
||||||
conn_test = 'ssh'
|
|
||||||
else:
|
|
||||||
conn_test = conn
|
|
||||||
else:
|
|
||||||
# default to ssh for the connection variable test, as
|
|
||||||
# that's the historical default
|
|
||||||
conn_test = 'ssh'
|
|
||||||
|
|
||||||
# get the real ssh_address for the delegate and allow ansible_ssh_host to be templated
|
|
||||||
self._play_context.remote_addr = this_info.get('ansible_%s_host' % conn_test, this_info.get('ansible_host', self._task.delegate_to))
|
|
||||||
self._play_context.remote_user = this_info.get('ansible_%s_user' % conn_test, this_info.get('ansible_user', self._play_context.remote_user))
|
|
||||||
self._play_context.port = this_info.get('ansible_%s_port' % conn_test, this_info.get('ansible_port', self._play_context.port))
|
|
||||||
self._play_context.password = this_info.get('ansible_%s_pass' % conn_test, this_info.get('ansible_pass', self._play_context.password))
|
|
||||||
self._play_context.private_key_file = this_info.get('ansible_%s_private_key_file' % conn_test, self._play_context.private_key_file)
|
|
||||||
|
|
||||||
# because of the switch from su/sudo -> become, the become pass for the
|
|
||||||
# delegated-to host may be in one of several fields, so try each until
|
|
||||||
# (maybe) one is found.
|
|
||||||
for become_pass in ('ansible_become_password', 'ansible_become_pass', 'ansible_sudo_password', 'ansible_sudo_pass'):
|
|
||||||
if become_pass in this_info:
|
|
||||||
self._play_context.become_pass = this_info[become_pass]
|
|
||||||
break
|
|
||||||
|
|
||||||
# Last chance to get private_key_file from global variables.
|
|
||||||
# this is useful if delegated host is not defined in the inventory
|
|
||||||
if self._play_context.private_key_file is None:
|
|
||||||
self._play_context.private_key_file = this_info.get('ansible_ssh_private_key_file', None)
|
|
||||||
|
|
||||||
if self._play_context.private_key_file is None:
|
|
||||||
key = this_info.get('private_key_file', None)
|
|
||||||
if key:
|
|
||||||
self._play_context.private_key_file = os.path.expanduser(key)
|
|
||||||
|
|
||||||
# since we're delegating, we don't want to use interpreter values
|
|
||||||
# which would have been set for the original target host
|
|
||||||
for i in variables.keys():
|
|
||||||
if i.startswith('ansible_') and i.endswith('_interpreter'):
|
|
||||||
del variables[i]
|
|
||||||
# now replace the interpreter values with those that may have come
|
|
||||||
# from the delegated-to host
|
|
||||||
for i in this_info:
|
|
||||||
if i.startswith("ansible_") and i.endswith("_interpreter"):
|
|
||||||
variables[i] = this_info[i]
|
|
||||||
|
|
||||||
|
|
|
@ -279,10 +279,16 @@ class PlayContext(Base):
|
||||||
setattr(new_info, attr, attr_val)
|
setattr(new_info, attr, attr_val)
|
||||||
|
|
||||||
# next, use the MAGIC_VARIABLE_MAPPING dictionary to update this
|
# next, use the MAGIC_VARIABLE_MAPPING dictionary to update this
|
||||||
# connection info object with 'magic' variables from the variable list
|
# connection info object with 'magic' variables from the variable list.
|
||||||
|
# If the value 'ansible_delegated_vars' is in the variables, it means
|
||||||
|
# we have a delegated-to host, so we check there first before looking
|
||||||
|
# at the variables in general
|
||||||
|
delegated_vars = variables.get('ansible_delegated_vars', dict())
|
||||||
for (attr, variable_names) in iteritems(MAGIC_VARIABLE_MAPPING):
|
for (attr, variable_names) in iteritems(MAGIC_VARIABLE_MAPPING):
|
||||||
for variable_name in variable_names:
|
for variable_name in variable_names:
|
||||||
if variable_name in variables:
|
if isinstance(delegated_vars, dict) and variable_name in delegated_vars:
|
||||||
|
setattr(new_info, attr, delegated_vars[variable_name])
|
||||||
|
elif variable_name in variables:
|
||||||
setattr(new_info, attr, variables[variable_name])
|
setattr(new_info, attr, variables[variable_name])
|
||||||
|
|
||||||
# make sure we get port defaults if needed
|
# make sure we get port defaults if needed
|
||||||
|
@ -296,6 +302,7 @@ class PlayContext(Base):
|
||||||
elif new_info.become_method == 'su' and new_info.su_pass:
|
elif new_info.become_method == 'su' and new_info.su_pass:
|
||||||
setattr(new_info, 'become_pass', new_info.su_pass)
|
setattr(new_info, 'become_pass', new_info.su_pass)
|
||||||
|
|
||||||
|
|
||||||
# finally, in the special instance that the task was specified
|
# finally, in the special instance that the task was specified
|
||||||
# as a local action, override the connection in case it was changed
|
# as a local action, override the connection in case it was changed
|
||||||
# during some other step in the process
|
# during some other step in the process
|
||||||
|
|
|
@ -34,6 +34,7 @@ except ImportError:
|
||||||
from ansible import constants as C
|
from ansible import constants as C
|
||||||
from ansible.cli import CLI
|
from ansible.cli import CLI
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
|
from ansible.inventory.host import Host
|
||||||
from ansible.parsing import DataLoader
|
from ansible.parsing import DataLoader
|
||||||
from ansible.plugins.cache import FactCache
|
from ansible.plugins.cache import FactCache
|
||||||
from ansible.template import Templar
|
from ansible.template import Templar
|
||||||
|
@ -127,7 +128,7 @@ class VariableManager:
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_vars(self, loader, play=None, host=None, task=None, include_hostvars=True, use_cache=True):
|
def get_vars(self, loader, play=None, host=None, task=None, include_hostvars=True, include_delegate_to=True, use_cache=True):
|
||||||
'''
|
'''
|
||||||
Returns the variables, with optional "context" given via the parameters
|
Returns the variables, with optional "context" given via the parameters
|
||||||
for the play, host, and task (which could possibly result in different
|
for the play, host, and task (which could possibly result in different
|
||||||
|
@ -276,6 +277,38 @@ class VariableManager:
|
||||||
if task._role:
|
if task._role:
|
||||||
all_vars['role_path'] = task._role._role_path
|
all_vars['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)
|
||||||
|
delegated_host_name = templar.template(task.delegate_to)
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
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
|
||||||
|
all_vars['ansible_delegated_vars'] = self.get_vars(loader=loader, play=play, host=delegated_host, task=task, include_delegate_to=False, include_hostvars=False)
|
||||||
|
|
||||||
if self._inventory is not None:
|
if self._inventory is not None:
|
||||||
all_vars['inventory_dir'] = self._inventory.basedir()
|
all_vars['inventory_dir'] = self._inventory.basedir()
|
||||||
if play:
|
if play:
|
||||||
|
@ -287,10 +320,8 @@ class VariableManager:
|
||||||
all_vars['play_hosts'] = host_list
|
all_vars['play_hosts'] = host_list
|
||||||
all_vars['ansible_play_hosts'] = host_list
|
all_vars['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
|
all_vars['omit'] = self._omit_token
|
||||||
|
|
||||||
all_vars['ansible_version'] = CLI.version_info(gitinfo=False)
|
all_vars['ansible_version'] = CLI.version_info(gitinfo=False)
|
||||||
|
|
||||||
if 'hostvars' in all_vars and host:
|
if 'hostvars' in all_vars and host:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue