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
(cherry picked from commit fed079e4cb
)
This commit is contained in:
parent
e05222a4cc
commit
47e16bef08
3 changed files with 52 additions and 3 deletions
|
@ -43,6 +43,7 @@ if PY3:
|
||||||
class_types = type,
|
class_types = type,
|
||||||
text_type = str
|
text_type = str
|
||||||
binary_type = bytes
|
binary_type = bytes
|
||||||
|
cmp = lambda a, b: (a > b) - (a < b)
|
||||||
|
|
||||||
MAXSIZE = sys.maxsize
|
MAXSIZE = sys.maxsize
|
||||||
else:
|
else:
|
||||||
|
@ -51,6 +52,7 @@ else:
|
||||||
class_types = (type, types.ClassType)
|
class_types = (type, types.ClassType)
|
||||||
text_type = unicode
|
text_type = unicode
|
||||||
binary_type = str
|
binary_type = str
|
||||||
|
cmp = cmp
|
||||||
|
|
||||||
if sys.platform.startswith("java"):
|
if sys.platform.startswith("java"):
|
||||||
# Jython always uses 32 bits.
|
# Jython always uses 32 bits.
|
||||||
|
|
|
@ -26,6 +26,7 @@ from ansible.compat.six import iteritems
|
||||||
from ansible import constants as C
|
from ansible import constants as C
|
||||||
|
|
||||||
from ansible.errors import AnsibleError
|
from ansible.errors import AnsibleError
|
||||||
|
from ansible.module_utils.six import cmp
|
||||||
from ansible.playbook.block import Block
|
from ansible.playbook.block import Block
|
||||||
from ansible.playbook.task import Task
|
from ansible.playbook.task import Task
|
||||||
from ansible.playbook.role_include import IncludeRole
|
from ansible.playbook.role_include import IncludeRole
|
||||||
|
@ -50,6 +51,7 @@ class HostState:
|
||||||
self.cur_rescue_task = 0
|
self.cur_rescue_task = 0
|
||||||
self.cur_always_task = 0
|
self.cur_always_task = 0
|
||||||
self.cur_role = None
|
self.cur_role = None
|
||||||
|
self.cur_role_task = None
|
||||||
self.cur_dep_chain = None
|
self.cur_dep_chain = None
|
||||||
self.run_state = PlayIterator.ITERATING_SETUP
|
self.run_state = PlayIterator.ITERATING_SETUP
|
||||||
self.fail_state = PlayIterator.FAILED_NONE
|
self.fail_state = PlayIterator.FAILED_NONE
|
||||||
|
@ -122,6 +124,8 @@ class HostState:
|
||||||
new_state.cur_rescue_task = self.cur_rescue_task
|
new_state.cur_rescue_task = self.cur_rescue_task
|
||||||
new_state.cur_always_task = self.cur_always_task
|
new_state.cur_always_task = self.cur_always_task
|
||||||
new_state.cur_role = self.cur_role
|
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.run_state = self.run_state
|
||||||
new_state.fail_state = self.fail_state
|
new_state.fail_state = self.fail_state
|
||||||
new_state.pending_setup = self.pending_setup
|
new_state.pending_setup = self.pending_setup
|
||||||
|
@ -277,12 +281,53 @@ class PlayIterator:
|
||||||
parent = parent._parent
|
parent = parent._parent
|
||||||
return False
|
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 task and task._role:
|
||||||
# if we had a current role, mark that role as completed
|
# 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 \
|
if s.cur_role:
|
||||||
not _role_is_child(s.cur_role) and not peek:
|
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._completed[host.name] = True
|
||||||
s.cur_role = task._role
|
s.cur_role = task._role
|
||||||
|
s.cur_role_task = _get_cur_task(s)
|
||||||
s.cur_dep_chain = task.get_dep_chain()
|
s.cur_dep_chain = task.get_dep_chain()
|
||||||
|
|
||||||
if not peek:
|
if not peek:
|
||||||
|
|
|
@ -38,6 +38,7 @@ if PY3:
|
||||||
class_types = type,
|
class_types = type,
|
||||||
text_type = str
|
text_type = str
|
||||||
binary_type = bytes
|
binary_type = bytes
|
||||||
|
cmp = lambda a, b: (a > b) - (a < b)
|
||||||
|
|
||||||
MAXSIZE = sys.maxsize
|
MAXSIZE = sys.maxsize
|
||||||
else:
|
else:
|
||||||
|
@ -46,6 +47,7 @@ else:
|
||||||
class_types = (type, types.ClassType)
|
class_types = (type, types.ClassType)
|
||||||
text_type = unicode
|
text_type = unicode
|
||||||
binary_type = str
|
binary_type = str
|
||||||
|
cmp = cmp
|
||||||
|
|
||||||
if sys.platform.startswith("java"):
|
if sys.platform.startswith("java"):
|
||||||
# Jython always uses 32 bits.
|
# Jython always uses 32 bits.
|
||||||
|
|
Loading…
Reference in a new issue