summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Cammarata <jimi@sngx.net>2016-12-28 08:16:02 -0600
committerJames Cammarata <jimi@sngx.net>2017-01-03 13:45:10 -0600
commit99b223979d72527185702ed68f4ae7d582c6454b (patch)
tree2a19ae1fd8397b85438141f30a6360380683e634
parent61897fa4b762cf9e38fc7edb1f1448aacbe3f8e6 (diff)
downloadansible-issue_15409_role_execution_problems.tar.gz
Fix role completion detection problemissue_15409_role_execution_problems
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
-rw-r--r--lib/ansible/compat/six/_six.py2
-rw-r--r--lib/ansible/executor/play_iterator.py51
-rw-r--r--lib/ansible/module_utils/six.py2
3 files changed, 52 insertions, 3 deletions
diff --git a/lib/ansible/compat/six/_six.py b/lib/ansible/compat/six/_six.py
index 190c0239cd..8caf2ad85c 100644
--- a/lib/ansible/compat/six/_six.py
+++ b/lib/ansible/compat/six/_six.py
@@ -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.
diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py
index ad8facac65..2408226d6f 100644
--- a/lib/ansible/executor/play_iterator.py
+++ b/lib/ansible/executor/play_iterator.py
@@ -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:
diff --git a/lib/ansible/module_utils/six.py b/lib/ansible/module_utils/six.py
index 85898ec712..b93d4bf8bb 100644
--- a/lib/ansible/module_utils/six.py
+++ b/lib/ansible/module_utils/six.py
@@ -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.