diff options
Diffstat (limited to 'lib/ansible/executor/play_iterator.py')
-rw-r--r-- | lib/ansible/executor/play_iterator.py | 125 |
1 files changed, 95 insertions, 30 deletions
diff --git a/lib/ansible/executor/play_iterator.py b/lib/ansible/executor/play_iterator.py index fd59478ead..09caeec2d9 100644 --- a/lib/ansible/executor/play_iterator.py +++ b/lib/ansible/executor/play_iterator.py @@ -49,6 +49,7 @@ class HostState: self.cur_rescue_task = 0 self.cur_always_task = 0 self.cur_role = None + self.cur_dep_chain = None self.run_state = PlayIterator.ITERATING_SETUP self.fail_state = PlayIterator.FAILED_NONE self.pending_setup = False @@ -57,14 +58,32 @@ class HostState: self.always_child_state = None def __repr__(self): - return "HOST STATE: block=%d, task=%d, rescue=%d, always=%d, role=%s, run_state=%d, fail_state=%d, pending_setup=%s, tasks child state? %s, rescue child state? %s, always child state? %s" % ( + def _run_state_to_string(n): + states = ["ITERATING_SETUP", "ITERATING_TASKS", "ITERATING_RESCUE", "ITERATING_ALWAYS", "ITERATING_COMPLETE"] + try: + return states[n] + except IndexError: + return "UNKNOWN STATE" + + def _failed_state_to_string(n): + states = {1:"FAILED_SETUP", 2:"FAILED_TASKS", 4:"FAILED_RESCUE", 8:"FAILED_ALWAYS"} + if n == 0: + return "FAILED_NONE" + else: + ret = [] + for i in (1, 2, 4, 8): + if n & i: + ret.append(states[i]) + return "|".join(ret) + + return "HOST STATE: block=%d, task=%d, rescue=%d, always=%d, role=%s, run_state=%s, fail_state=%s, pending_setup=%s, tasks child state? %s, rescue child state? %s, always child state? %s" % ( self.cur_block, self.cur_regular_task, self.cur_rescue_task, self.cur_always_task, self.cur_role, - self.run_state, - self.fail_state, + _run_state_to_string(self.run_state), + _failed_state_to_string(self.fail_state), self.pending_setup, self.tasks_child_state, self.rescue_child_state, @@ -84,6 +103,8 @@ class HostState: new_state.run_state = self.run_state new_state.fail_state = self.fail_state new_state.pending_setup = self.pending_setup + if self.cur_dep_chain is not None: + new_state.cur_dep_chain = self.cur_dep_chain[:] if self.tasks_child_state is not None: new_state.tasks_child_state = self.tasks_child_state.copy() if self.rescue_child_state is not None: @@ -119,30 +140,35 @@ class PlayIterator: self._blocks.append(new_block) self._host_states = {} + start_at_matched = False for host in inventory.get_hosts(self._play.hosts): - self._host_states[host.name] = HostState(blocks=self._blocks) - # if the host's name is in the variable manager's fact cache, then set - # its _gathered_facts flag to true for smart gathering tests later - if host.name in variable_manager._fact_cache: - host._gathered_facts = True - # if we're looking to start at a specific task, iterate through - # the tasks for this host until we find the specified task - if play_context.start_at_task is not None and not start_at_done: - while True: - (s, task) = self.get_next_task_for_host(host, peek=True) - if s.run_state == self.ITERATING_COMPLETE: - break - if task.name == play_context.start_at_task or fnmatch.fnmatch(task.name, play_context.start_at_task) or \ - task.get_name() == play_context.start_at_task or fnmatch.fnmatch(task.get_name(), play_context.start_at_task): - # we have our match, so clear the start_at_task field on the - # play context to flag that we've started at a task (and future - # plays won't try to advance) - play_context.start_at_task = None - break - else: - self.get_next_task_for_host(host) - # finally, reset the host's state to ITERATING_SETUP - self._host_states[host.name].run_state = self.ITERATING_SETUP + self._host_states[host.name] = HostState(blocks=self._blocks) + # if the host's name is in the variable manager's fact cache, then set + # its _gathered_facts flag to true for smart gathering tests later + if host.name in variable_manager._fact_cache: + host._gathered_facts = True + # if we're looking to start at a specific task, iterate through + # the tasks for this host until we find the specified task + if play_context.start_at_task is not None and not start_at_done: + while True: + (s, task) = self.get_next_task_for_host(host, peek=True) + if s.run_state == self.ITERATING_COMPLETE: + break + if task.name == play_context.start_at_task or fnmatch.fnmatch(task.name, play_context.start_at_task) or \ + task.get_name() == play_context.start_at_task or fnmatch.fnmatch(task.get_name(), play_context.start_at_task): + start_at_matched = True + break + else: + self.get_next_task_for_host(host) + + # finally, reset the host's state to ITERATING_SETUP + self._host_states[host.name].run_state = self.ITERATING_SETUP + + if start_at_matched: + # we have our match, so clear the start_at_task field on the + # play context to flag that we've started at a task (and future + # plays won't try to advance) + play_context.start_at_task = None # Extend the play handlers list to include the handlers defined in roles self._play.handlers.extend(play.compile_roles_handlers()) @@ -189,13 +215,21 @@ class PlayIterator: s.pending_setup = False if not task: + old_s = s (s, task) = self._get_next_task_from_state(s, peek=peek) + def _roles_are_different(ra, rb): + if ra != rb: + return True + else: + return old_s.cur_dep_chain != task._block._dep_chain + if task and task._role: # if we had a current role, mark that role as completed - if s.cur_role and task._role != s.cur_role and host.name in s.cur_role._had_task_run and not peek: + 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 peek: s.cur_role._completed[host.name] = True s.cur_role = task._role + s.cur_dep_chain = task._block._dep_chain if not peek: self._host_states[host.name] = s @@ -324,13 +358,21 @@ class PlayIterator: state.tasks_child_state = self._set_failed_state(state.tasks_child_state) else: state.fail_state |= self.FAILED_TASKS - state.run_state = self.ITERATING_RESCUE + if state._blocks[state.cur_block].rescue: + state.run_state = self.ITERATING_RESCUE + elif state._blocks[state.cur_block].always: + state.run_state = self.ITERATING_ALWAYS + else: + state.run_state = self.ITERATING_COMPLETE elif state.run_state == self.ITERATING_RESCUE: if state.rescue_child_state is not None: state.rescue_child_state = self._set_failed_state(state.rescue_child_state) else: state.fail_state |= self.FAILED_RESCUE - state.run_state = self.ITERATING_ALWAYS + if state._blocks[state.cur_block].always: + state.run_state = self.ITERATING_ALWAYS + else: + state.run_state = self.ITERATING_COMPLETE elif state.run_state == self.ITERATING_ALWAYS: if state.always_child_state is not None: state.always_child_state = self._set_failed_state(state.always_child_state) @@ -347,6 +389,28 @@ class PlayIterator: def get_failed_hosts(self): return dict((host, True) for (host, state) in iteritems(self._host_states) if state.run_state == self.ITERATING_COMPLETE and state.fail_state != self.FAILED_NONE) + def _check_failed_state(self, state): + if state is None: + return False + elif state.run_state == self.ITERATING_TASKS and self._check_failed_state(state.tasks_child_state): + return True + elif state.run_state == self.ITERATING_RESCUE and self._check_failed_state(state.rescue_child_state): + return True + elif state.run_state == self.ITERATING_ALWAYS and self._check_failed_state(state.always_child_state): + return True + elif state.run_state == self.ITERATING_COMPLETE and state.fail_state != self.FAILED_NONE: + if state.run_state == self.ITERATING_RESCUE and state.fail_state&self.FAILED_RESCUE == 0: + return False + elif state.run_state == self.ITERATING_ALWAYS and state.fail_state&self.FAILED_ALWAYS == 0: + return False + else: + return True + return False + + def is_failed(self, host): + s = self.get_host_state(host) + return self._check_failed_state(s) + def get_original_task(self, host, task): ''' Finds the task in the task list which matches the UUID of the given task. @@ -396,7 +460,8 @@ class PlayIterator: return None def _insert_tasks_into_state(self, state, task_list): - if state.fail_state != self.FAILED_NONE: + # if we've failed at all, or if the task list is empty, just return the current state + if state.fail_state != self.FAILED_NONE and state.run_state not in (self.ITERATING_RESCUE, self.ITERATING_ALWAYS) or not task_list: return state if state.run_state == self.ITERATING_TASKS: |