diff --git a/changelogs/fragments/remove-deprecated-get_delegated_vars.yml b/changelogs/fragments/remove-deprecated-get_delegated_vars.yml new file mode 100644 index 00000000000..8cc0659ee57 --- /dev/null +++ b/changelogs/fragments/remove-deprecated-get_delegated_vars.yml @@ -0,0 +1,2 @@ +removed_features: + - Remove deprecated `VariableManager._get_delegated_vars` method (https://github.com/ansible/ansible/issues/82950) diff --git a/lib/ansible/vars/manager.py b/lib/ansible/vars/manager.py index 96559a67350..708cda7fd6c 100644 --- a/lib/ansible/vars/manager.py +++ b/lib/ansible/vars/manager.py @@ -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 diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 9f6b7ed438d..107249def94 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -177,4 +177,3 @@ test/integration/targets/find/files/hello_world.gbk no-unwanted-characters lib/ansible/galaxy/collection/__init__.py pylint:ansible-deprecated-version-comment # 2.18 deprecation lib/ansible/plugins/action/__init__.py pylint:ansible-deprecated-version # 2.18 deprecation lib/ansible/template/__init__.py pylint:ansible-deprecated-version # 2.18 deprecation -lib/ansible/vars/manager.py pylint:ansible-deprecated-version # 2.18 deprecation