PlayIterator: skip tasks from already ran roles (#83793)

... so strategies do not have to filter them.
pull/84076/head
Martin Krizek 2 months ago committed by GitHub
parent d0df3a174a
commit f1f0d9bd53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,2 @@
minor_changes:
- PlayIterator - do not return tasks from already executed roles so specific strategy plugins do not have to do the filtering of such tasks themselves

@ -440,7 +440,7 @@ class PlayIterator:
else: else:
state.cur_handlers_task += 1 state.cur_handlers_task += 1
if task.is_host_notified(host): if task.is_host_notified(host):
break return state, task
elif state.run_state == IteratingStates.COMPLETE: elif state.run_state == IteratingStates.COMPLETE:
return (state, None) return (state, None)
@ -463,9 +463,15 @@ class PlayIterator:
and all(not h.notified_hosts for h in self.handlers) and all(not h.notified_hosts for h in self.handlers)
) )
): ):
continue display.debug("No handler notifications for %s, skipping." % host.name)
elif (
break (role := task._role)
and role._metadata.allow_duplicates is False
and host.name in self._play._get_cached_role(role)._completed
):
display.debug("'%s' skipped because role has already run" % task)
else:
break
return (state, task) return (state, task)

@ -19,7 +19,7 @@ from __future__ import annotations
from ansible import constants as C from ansible import constants as C
from ansible import context from ansible import context
from ansible.errors import AnsibleParserError, AnsibleAssertionError from ansible.errors import AnsibleParserError, AnsibleAssertionError, AnsibleError
from ansible.module_utils.common.text.converters import to_native from ansible.module_utils.common.text.converters import to_native
from ansible.module_utils.common.collections import is_sequence from ansible.module_utils.common.collections import is_sequence
from ansible.module_utils.six import binary_type, string_types, text_type from ansible.module_utils.six import binary_type, string_types, text_type
@ -100,6 +100,15 @@ class Play(Base, Taggable, CollectionSearch):
def __repr__(self): def __repr__(self):
return self.get_name() return self.get_name()
def _get_cached_role(self, role):
role_path = role.get_role_path()
role_cache = self.role_cache[role_path]
try:
idx = role_cache.index(role)
return role_cache[idx]
except ValueError:
raise AnsibleError(f'Cannot locate {role.get_name()} in role cache')
def _validate_hosts(self, attribute, name, value): def _validate_hosts(self, attribute, name, value):
# Only validate 'hosts' if a value was passed in to original data set. # Only validate 'hosts' if a value was passed in to original data set.
if 'hosts' in self._ds: if 'hosts' in self._ds:

@ -1102,13 +1102,7 @@ class StrategyBase:
return [res] return [res]
def _get_cached_role(self, task, play): def _get_cached_role(self, task, play):
role_path = task._role.get_role_path() return play._get_cached_role(task._role)
role_cache = play.role_cache[role_path]
try:
idx = role_cache.index(task._role)
return role_cache[idx]
except ValueError:
raise AnsibleError(f'Cannot locate {task._role.get_name()} in role cache')
def get_hosts_left(self, iterator): def get_hosts_left(self, iterator):
''' returns list of available hosts for this iterator by filtering out unreachables ''' ''' returns list of available hosts for this iterator by filtering out unreachables '''

@ -172,15 +172,6 @@ class StrategyModule(StrategyBase):
display.warning("Using run_once with the free strategy is not currently supported. This task will still be " display.warning("Using run_once with the free strategy is not currently supported. This task will still be "
"executed for every host in the inventory list.") "executed for every host in the inventory list.")
# check to see if this task should be skipped, due to it being a member of a
# role which has already run (and whether that role allows duplicate execution)
if not isinstance(task, Handler) and task._role:
role_obj = self._get_cached_role(task, iterator._play)
if role_obj.has_run(host) and task._role._metadata.allow_duplicates is False:
display.debug("'%s' skipped because role has already run" % task, host=host_name)
del self._blocked_hosts[host_name]
continue
if task.action in C._ACTION_META: if task.action in C._ACTION_META:
if self._host_pinned: if self._host_pinned:
meta_task_dummy_results_count += 1 meta_task_dummy_results_count += 1

@ -130,14 +130,6 @@ class StrategyModule(StrategyBase):
run_once = False run_once = False
work_to_do = True work_to_do = True
# check to see if this task should be skipped, due to it being a member of a
# role which has already run (and whether that role allows duplicate execution)
if not isinstance(task, Handler) and task._role:
role_obj = self._get_cached_role(task, iterator._play)
if role_obj.has_run(host) and task._role._metadata.allow_duplicates is False:
display.debug("'%s' skipped because role has already run" % task)
continue
display.debug("getting variables") display.debug("getting variables")
task_vars = self._variable_manager.get_vars(play=iterator._play, host=host, task=task, task_vars = self._variable_manager.get_vars(play=iterator._play, host=host, task=task,
_hosts=self._hosts_cache, _hosts_all=self._hosts_cache_all) _hosts=self._hosts_cache, _hosts_all=self._hosts_cache_all)

Loading…
Cancel
Save