Fix role completion detection problem

When the same role is listed consecutively in a play, the previous role
completion detection failed to mark it as complete as it only checked to
see if the role changed.

This patch addresses that by also keeping track of which task in the role
we are on, so that even if the same role is encountered during later passes
the task number will be less than or equal to the last noted task position.

Related to #15409
This commit is contained in:
James Cammarata 2016-12-28 08:16:02 -06:00
parent 9f2d22425b
commit fed079e4cb
3 changed files with 52 additions and 3 deletions

View file

@ -43,6 +43,7 @@ if PY3:
class_types = type,
text_type = str
binary_type = bytes
cmp = lambda a, b: (a > b) - (a < b)
MAXSIZE = sys.maxsize
else:
@ -51,6 +52,7 @@ else:
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
cmp = cmp
if sys.platform.startswith("java"):
# Jython always uses 32 bits.

View file

@ -24,6 +24,7 @@ import fnmatch
from ansible.compat.six import iteritems
from ansible import constants as C
from ansible.errors import AnsibleError
from ansible.module_utils.six import cmp
from ansible.playbook.block import Block
from ansible.playbook.task import Task
from ansible.playbook.role_include import IncludeRole
@ -48,6 +49,7 @@ class HostState:
self.cur_rescue_task = 0
self.cur_always_task = 0
self.cur_role = None
self.cur_role_task = None
self.cur_dep_chain = None
self.run_state = PlayIterator.ITERATING_SETUP
self.fail_state = PlayIterator.FAILED_NONE
@ -120,6 +122,8 @@ class HostState:
new_state.cur_rescue_task = self.cur_rescue_task
new_state.cur_always_task = self.cur_always_task
new_state.cur_role = self.cur_role
if self.cur_role_task:
new_state.cur_role_task = self.cur_role_task[:]
new_state.run_state = self.run_state
new_state.fail_state = self.fail_state
new_state.pending_setup = self.pending_setup
@ -276,12 +280,53 @@ class PlayIterator:
parent = parent._parent
return False
def _get_cur_task(s, depth=0):
res = [s.run_state, depth, s.cur_block, -1]
if s.run_state == self.ITERATING_TASKS:
if s.tasks_child_state:
res = _get_cur_task(s.tasks_child_state, depth=depth+1)
else:
res[-1] = s.cur_regular_task
elif s.run_state == self.ITERATING_RESCUE:
if s.rescue_child_state:
res = _get_cur_task(s.rescue_child_state, depth=depth+1)
else:
res[-1] = s.cur_rescue_task
elif s.run_state == self.ITERATING_ALWAYS:
if s.always_child_state:
res = _get_cur_task(s.always_child_state, depth=depth+1)
else:
res[-1] = s.cur_always_task
return res
def _role_task_cmp(s):
'''
tt is a tuple made of the regular/rescue/always task number
from the current state of the host.
'''
if not s.cur_role_task:
return 1
cur_task = _get_cur_task(s)
res = cmp(cur_task[0], s.cur_role_task[0])
if res == 0:
res = cmp(cur_task[1], s.cur_role_task[1])
if res == 0:
res = cmp(cur_task[2], s.cur_role_task[2])
if res == 0:
res = cmp(cur_task[3], s.cur_role_task[3])
return res
if task and task._role:
# if we had a current role, mark that role as completed
if s.cur_role and _roles_are_different(task._role, s.cur_role) and host.name in s.cur_role._had_task_run and \
not _role_is_child(s.cur_role) and not peek:
s.cur_role._completed[host.name] = True
if s.cur_role:
role_diff = _roles_are_different(task._role, s.cur_role)
role_child = _role_is_child(s.cur_role)
tasks_cmp = _role_task_cmp(s)
host_done = host.name in s.cur_role._had_task_run
if (role_diff or (not role_diff and tasks_cmp <= 0)) and host_done and not role_child and not peek:
s.cur_role._completed[host.name] = True
s.cur_role = task._role
s.cur_role_task = _get_cur_task(s)
s.cur_dep_chain = task.get_dep_chain()
if not peek:

View file

@ -38,6 +38,7 @@ if PY3:
class_types = type,
text_type = str
binary_type = bytes
cmp = lambda a, b: (a > b) - (a < b)
MAXSIZE = sys.maxsize
else:
@ -46,6 +47,7 @@ else:
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
cmp = cmp
if sys.platform.startswith("java"):
# Jython always uses 32 bits.