def compile(self): ''' Compiles and returns the task list for this play, compiled from the roles (which are themselves compiled recursively) and/or the list of tasks specified in the play. ''' # create a block containing a single flush handlers meta # task, so we can be sure to run handlers at certain points # of the playbook execution flush_block = Block.load( data={'meta': 'flush_handlers'}, play=self, variable_manager=self._variable_manager, loader=self._loader ) for task in flush_block.block: task.implicit = True block_list = [] if self.force_handlers: noop_task = Task() noop_task.action = 'meta' noop_task.args['_raw_params'] = 'noop' noop_task.implicit = True noop_task.set_loader(self._loader) b = Block(play=self) b.block = self.pre_tasks or [noop_task] b.always = [flush_block] block_list.append(b) tasks = self._compile_roles() + self.tasks b = Block(play=self) b.block = tasks or [noop_task] b.always = [flush_block] block_list.append(b) b = Block(play=self) b.block = self.post_tasks or [noop_task] b.always = [flush_block] block_list.append(b) return block_list block_list.extend(self.pre_tasks) block_list.append(flush_block) block_list.extend(self._compile_roles()) block_list.extend(self.tasks) block_list.append(flush_block) block_list.extend(self.post_tasks) block_list.append(flush_block) return block_list
def compile(self, play, dep_chain=None): ''' Returns the task list for this role, which is created by first recursively compiling the tasks for all direct dependencies, and then adding on the tasks for this role. The role compile() also remembers and saves the dependency chain with each task, so tasks know by which route they were found, and can correctly take their parent's tags/conditionals into account. ''' from ansible.playbook.block import Block from ansible.playbook.task import Task block_list = [] # update the dependency chain here if dep_chain is None: dep_chain = [] new_dep_chain = dep_chain + [self] deps = self.get_direct_dependencies() for dep in deps: dep_blocks = dep.compile(play=play, dep_chain=new_dep_chain) block_list.extend(dep_blocks) for task_block in self._task_blocks: new_task_block = task_block.copy() new_task_block._dep_chain = new_dep_chain new_task_block._play = play block_list.append(new_task_block) eor_block = Block(play=play) eor_block._loader = self._loader eor_block._role = self eor_block._variable_manager = self._variable_manager eor_block.run_once = False eor_task = Task(block=eor_block) eor_task._role = self eor_task.action = 'meta' eor_task.args = {'_raw_params': 'role_complete'} eor_task.implicit = True eor_task.tags = ['always'] eor_task.when = True eor_block.block = [eor_task] block_list.append(eor_block) return block_list
def _get_next_task_lockstep(self, hosts, iterator): ''' Returns a list of (host, task) tuples, where the task may be a noop task to keep the iterator in lock step across all hosts. ''' noop_task = Task() noop_task.action = 'meta' noop_task.args['_raw_params'] = 'noop' noop_task.implicit = True noop_task.set_loader(iterator._play._loader) host_tasks = {} display.debug("building list of next tasks for hosts") for host in hosts: host_tasks[host.name] = iterator.get_next_task_for_host(host, peek=True) display.debug("done building task lists") num_setups = 0 num_tasks = 0 num_rescue = 0 num_always = 0 display.debug("counting tasks in each state of execution") host_tasks_to_run = [(host, state_task) for host, state_task in iteritems(host_tasks) if state_task and state_task[1]] if host_tasks_to_run: try: lowest_cur_block = min( (iterator.get_active_state(s).cur_block for h, (s, t) in host_tasks_to_run if s.run_state != PlayIterator.ITERATING_COMPLETE)) except ValueError: lowest_cur_block = None else: # empty host_tasks_to_run will just run till the end of the function # without ever touching lowest_cur_block lowest_cur_block = None for (k, v) in host_tasks_to_run: (s, t) = v s = iterator.get_active_state(s) if s.cur_block > lowest_cur_block: # Not the current block, ignore it continue if s.run_state == PlayIterator.ITERATING_SETUP: num_setups += 1 elif s.run_state == PlayIterator.ITERATING_TASKS: num_tasks += 1 elif s.run_state == PlayIterator.ITERATING_RESCUE: num_rescue += 1 elif s.run_state == PlayIterator.ITERATING_ALWAYS: num_always += 1 display.debug("done counting tasks in each state of execution:\n\tnum_setups: %s\n\tnum_tasks: %s\n\tnum_rescue: %s\n\tnum_always: %s" % (num_setups, num_tasks, num_rescue, num_always)) def _advance_selected_hosts(hosts, cur_block, cur_state): ''' This helper returns the task for all hosts in the requested state, otherwise they get a noop dummy task. This also advances the state of the host, since the given states are determined while using peek=True. ''' # we return the values in the order they were originally # specified in the given hosts array rvals = [] display.debug("starting to advance hosts") for host in hosts: host_state_task = host_tasks.get(host.name) if host_state_task is None: continue (s, t) = host_state_task s = iterator.get_active_state(s) if t is None: continue if s.run_state == cur_state and s.cur_block == cur_block: new_t = iterator.get_next_task_for_host(host) rvals.append((host, t)) else: rvals.append((host, noop_task)) display.debug("done advancing hosts to next task") return rvals # if any hosts are in ITERATING_SETUP, return the setup task # while all other hosts get a noop if num_setups: display.debug("advancing hosts in ITERATING_SETUP") return _advance_selected_hosts(hosts, lowest_cur_block, PlayIterator.ITERATING_SETUP) # if any hosts are in ITERATING_TASKS, return the next normal # task for these hosts, while all other hosts get a noop if num_tasks: display.debug("advancing hosts in ITERATING_TASKS") return _advance_selected_hosts(hosts, lowest_cur_block, PlayIterator.ITERATING_TASKS) # if any hosts are in ITERATING_RESCUE, return the next rescue # task for these hosts, while all other hosts get a noop if num_rescue: display.debug("advancing hosts in ITERATING_RESCUE") return _advance_selected_hosts(hosts, lowest_cur_block, PlayIterator.ITERATING_RESCUE) # if any hosts are in ITERATING_ALWAYS, return the next always # task for these hosts, while all other hosts get a noop if num_always: display.debug("advancing hosts in ITERATING_ALWAYS") return _advance_selected_hosts(hosts, lowest_cur_block, PlayIterator.ITERATING_ALWAYS) # at this point, everything must be ITERATING_COMPLETE, so we # return None for all hosts in the list display.debug("all hosts are done, so returning None's for all hosts") return [(host, None) for host in hosts]
def _get_next_task_lockstep(self, hosts, iterator): ''' Returns a list of (host, task) tuples, where the task may be a noop task to keep the iterator in lock step across all hosts. ''' noop_task = Task() noop_task.action = 'meta' noop_task.args['_raw_params'] = 'noop' noop_task.implicit = True noop_task.set_loader(iterator._play._loader) state_task_per_host = {} for host in hosts: state, task = iterator.get_next_task_for_host(host, peek=True) if task is not None: state_task_per_host[host] = state, task if not state_task_per_host: return [(h, None) for h in hosts] if self._in_handlers and not any( filter(lambda rs: rs == IteratingStates.HANDLERS, (s.run_state for s, _ in state_task_per_host.values()))): self._in_handlers = False if self._in_handlers: lowest_cur_handler = min( s.cur_handlers_task for s, t in state_task_per_host.values() if s.run_state == IteratingStates.HANDLERS) else: task_uuids = [t._uuid for s, t in state_task_per_host.values()] _loop_cnt = 0 while _loop_cnt <= 1: try: cur_task = iterator.all_tasks[iterator.cur_task] except IndexError: # pick up any tasks left after clear_host_errors iterator.cur_task = 0 _loop_cnt += 1 else: iterator.cur_task += 1 if cur_task._uuid in task_uuids: break else: # prevent infinite loop raise AnsibleAssertionError( 'BUG: There seems to be a mismatch between tasks in PlayIterator and HostStates.' ) host_tasks = [] for host, (state, task) in state_task_per_host.items(): if ((self._in_handlers and lowest_cur_handler == state.cur_handlers_task) or (not self._in_handlers and cur_task._uuid == task._uuid)): iterator.set_state_for_host(host.name, state) host_tasks.append((host, task)) else: host_tasks.append((host, noop_task)) # once hosts synchronize on 'flush_handlers' lockstep enters # '_in_handlers' phase where handlers are run instead of tasks # until at least one host is in IteratingStates.HANDLERS if (not self._in_handlers and cur_task.action == 'meta' and cur_task.args.get('_raw_params') == 'flush_handlers'): self._in_handlers = True return host_tasks