def test_strategy_base_run_handlers(self, mock_worker): def fake_run(*args): return mock_worker.side_effect = fake_run mock_play_context = MagicMock() mock_handler_task = MagicMock(Handler) mock_handler_task.action = 'foo' mock_handler_task.get_name.return_value = "test handler" mock_handler_task.has_triggered.return_value = False mock_handler = MagicMock() mock_handler.block = [mock_handler_task] mock_handler.flag_for_host.return_value = False mock_play = MagicMock() mock_play.handlers = [mock_handler] mock_host = MagicMock(Host) mock_host.name = "test01" mock_host.has_hostkey = True mock_inventory = MagicMock() mock_inventory.get_hosts.return_value = [mock_host] mock_var_mgr = MagicMock() mock_var_mgr.get_vars.return_value = dict() mock_iterator = MagicMock mock_iterator._play = mock_play fake_loader = DictDataLoader() mock_options = MagicMock() mock_options.module_path = None tqm = TaskQueueManager( inventory=mock_inventory, variable_manager=mock_var_mgr, loader=fake_loader, options=mock_options, passwords=None, ) tqm._initialize_processes(3) tqm.hostvars = dict() try: strategy_base = StrategyBase(tqm=tqm) strategy_base._inventory = mock_inventory strategy_base._notified_handlers = {"test handler": [mock_host]} task_result = TaskResult(Host('host01'), Handler(), dict(changed=False)) tqm._final_q.put(('host_task_ok', task_result)) result = strategy_base.run_handlers(iterator=mock_iterator, play_context=mock_play_context) finally: tqm.cleanup()
def load_list_of_tasks(ds, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task assert type(ds) == list task_list = [] for task in ds: if not isinstance(task, dict): raise AnsibleParserError("task/handler entries must be dictionaries (got a %s)" % type(task), obj=ds) if 'block' in task: t = Block.load( task, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) else: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader)
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task assert isinstance(ds, list) task_list = [] for task in ds: assert isinstance(task, dict) if 'block' in task: t = Block.load( task, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) else: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.handler import Handler from ansible.playbook.task import Task #from ansible.playbook.task_include import TaskInclude assert type(ds) == list task_list = [] for task in ds: if not isinstance(task, dict): raise AnsibleParserError("task/handler entries must be dictionaries (got a %s)" % type(task), obj=ds) #if 'include' in task: # cur_basedir = None # if isinstance(task, AnsibleBaseYAMLObject) and loader: # pos_info = task.get_position_info() # new_basedir = os.path.dirname(pos_info[0]) # cur_basedir = loader.get_basedir() # loader.set_basedir(new_basedir) # t = TaskInclude.load( # task, # block=block, # role=role, # task_include=task_include, # use_handlers=use_handlers, # loader=loader # ) # if cur_basedir and loader: # loader.set_basedir(cur_basedir) #else: if True: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task assert isinstance(ds, list) task_list = [] for task in ds: assert isinstance(task, dict) if 'block' in task: t = Block.load( task, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) else: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader, play=play) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) if isinstance(t, list): task_list.extend(t) else: task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task if not isinstance(ds, list): raise AnsibleParserError('task has bad type: "%s". Expected "list"' % type(ds).__name__, obj=ds) task_list = [] for task in ds: if not isinstance(task, dict): raise AnsibleParserError('task/handler has bad type: "%s". Expected "dict"' % type(task).__name__, obj=task) if 'block' in task: t = Block.load( task, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) else: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.handler import Handler from ansible.playbook.task import Task #from ansible.playbook.task_include import TaskInclude assert type(ds) == list task_list = [] for task in ds: if not isinstance(task, dict): raise AnsibleParserError( "task/handler entries must be dictionaries (got a %s)" % type(task), obj=ds) #if 'include' in task: # cur_basedir = None # if isinstance(task, AnsibleBaseYAMLObject) and loader: # pos_info = task.ansible_pos # new_basedir = os.path.dirname(pos_info[0]) # cur_basedir = loader.get_basedir() # loader.set_basedir(new_basedir) # t = TaskInclude.load( # task, # block=block, # role=role, # task_include=task_include, # use_handlers=use_handlers, # loader=loader # ) # if cur_basedir and loader: # loader.set_basedir(cur_basedir) #else: if True: if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def test_strategy_base_run_handlers(self, mock_worker): def fake_run(*args): return mock_worker.side_effect = fake_run mock_play_context = MagicMock() mock_handler_task = Handler() mock_handler_task.action = 'foo' mock_handler_task.cached_name = False mock_handler_task.name = "test handler" mock_handler_task.listen = [] mock_handler_task._role = None mock_handler_task._parent = None mock_handler_task._uuid = 'xxxxxxxxxxxxxxxx' mock_handler = MagicMock() mock_handler.block = [mock_handler_task] mock_handler.flag_for_host.return_value = False mock_play = MagicMock() mock_play.handlers = [mock_handler] mock_host = MagicMock(Host) mock_host.name = "test01" mock_host.has_hostkey = True mock_inventory = MagicMock() mock_inventory.get_hosts.return_value = [mock_host] mock_inventory.get.return_value = mock_host mock_inventory.get_host.return_value = mock_host mock_var_mgr = MagicMock() mock_var_mgr.get_vars.return_value = dict() mock_iterator = MagicMock() mock_iterator._play = mock_play fake_loader = DictDataLoader() tqm = TaskQueueManager( inventory=mock_inventory, variable_manager=mock_var_mgr, loader=fake_loader, passwords=None, forks=5, ) tqm._initialize_processes(3) tqm.hostvars = dict() try: strategy_base = StrategyBase(tqm=tqm) strategy_base._inventory = mock_inventory task_result = TaskResult(mock_host.name, mock_handler_task._uuid, dict(changed=False)) strategy_base._queued_task_cache = dict() strategy_base._queued_task_cache[(mock_host.name, mock_handler_task._uuid)] = { 'task': mock_handler_task, 'host': mock_host, 'task_vars': {}, 'play_context': mock_play_context } tqm._final_q.put(task_result) result = strategy_base.run_handlers(iterator=mock_iterator, play_context=mock_play_context) finally: strategy_base.cleanup() tqm.cleanup()
def test_strategy_base_process_pending_results(self): mock_tqm = MagicMock() mock_tqm._terminated = False mock_tqm._failed_hosts = dict() mock_tqm._unreachable_hosts = dict() mock_tqm.send_callback.return_value = None queue_items = [] def _queue_empty(*args, **kwargs): return len(queue_items) == 0 def _queue_get(*args, **kwargs): if len(queue_items) == 0: raise Queue.Empty else: return queue_items.pop() def _queue_put(item, *args, **kwargs): queue_items.append(item) mock_queue = MagicMock() mock_queue.empty.side_effect = _queue_empty mock_queue.get.side_effect = _queue_get mock_queue.put.side_effect = _queue_put mock_tqm._final_q = mock_queue mock_tqm._stats = MagicMock() mock_tqm._stats.increment.return_value = None mock_play = MagicMock() mock_host = MagicMock() mock_host.name = 'test01' mock_host.vars = dict() mock_host.get_vars.return_value = dict() mock_host.has_hostkey = True mock_task = MagicMock() mock_task._role = None mock_task._parent = None mock_task.ignore_errors = False mock_task.ignore_unreachable = False mock_task._uuid = str(uuid.uuid4()) mock_task.loop = None mock_task.copy.return_value = mock_task mock_handler_task = Handler() mock_handler_task.name = 'test handler' mock_handler_task.action = 'foo' mock_handler_task._parent = None mock_handler_task._uuid = 'xxxxxxxxxxxxx' mock_iterator = MagicMock() mock_iterator._play = mock_play mock_iterator.mark_host_failed.return_value = None mock_iterator.get_next_task_for_host.return_value = (None, None) mock_handler_block = MagicMock() mock_handler_block.block = [mock_handler_task] mock_handler_block.rescue = [] mock_handler_block.always = [] mock_play.handlers = [mock_handler_block] mock_group = MagicMock() mock_group.add_host.return_value = None def _get_host(host_name): if host_name == 'test01': return mock_host return None def _get_group(group_name): if group_name in ('all', 'foo'): return mock_group return None mock_inventory = MagicMock() mock_inventory._hosts_cache = dict() mock_inventory.hosts.return_value = mock_host mock_inventory.get_host.side_effect = _get_host mock_inventory.get_group.side_effect = _get_group mock_inventory.clear_pattern_cache.return_value = None mock_inventory.get_host_vars.return_value = {} mock_inventory.hosts.get.return_value = mock_host mock_var_mgr = MagicMock() mock_var_mgr.set_host_variable.return_value = None mock_var_mgr.set_host_facts.return_value = None mock_var_mgr.get_vars.return_value = dict() strategy_base = StrategyBase(tqm=mock_tqm) strategy_base._inventory = mock_inventory strategy_base._variable_manager = mock_var_mgr strategy_base._blocked_hosts = dict() def _has_dead_workers(): return False strategy_base._tqm.has_dead_workers.side_effect = _has_dead_workers results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 0) task_result = TaskResult(host=mock_host.name, task=mock_task._uuid, return_data=dict(changed=True)) queue_items.append(task_result) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 def mock_queued_task_cache(): return { (mock_host.name, mock_task._uuid): { 'task': mock_task, 'host': mock_host, 'task_vars': {}, 'play_context': {}, } } strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(results[0], task_result) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) task_result = TaskResult(host=mock_host.name, task=mock_task._uuid, return_data='{"failed":true}') queue_items.append(task_result) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 mock_iterator.is_failed.return_value = True strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(results[0], task_result) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) # self.assertIn('test01', mock_tqm._failed_hosts) # del mock_tqm._failed_hosts['test01'] mock_iterator.is_failed.return_value = False task_result = TaskResult(host=mock_host.name, task=mock_task._uuid, return_data='{"unreachable": true}') queue_items.append(task_result) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(results[0], task_result) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) self.assertIn('test01', mock_tqm._unreachable_hosts) del mock_tqm._unreachable_hosts['test01'] task_result = TaskResult(host=mock_host.name, task=mock_task._uuid, return_data='{"skipped": true}') queue_items.append(task_result) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(results[0], task_result) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) queue_items.append( TaskResult( host=mock_host.name, task=mock_task._uuid, return_data=dict( add_host=dict(host_name='newhost01', new_groups=['foo'])))) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) queue_items.append( TaskResult(host=mock_host.name, task=mock_task._uuid, return_data=dict(add_group=dict(group_name='foo')))) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) queue_items.append( TaskResult(host=mock_host.name, task=mock_task._uuid, return_data=dict(changed=True, _ansible_notify=['test handler']))) strategy_base._blocked_hosts['test01'] = True strategy_base._pending_results = 1 strategy_base._queued_task_cache = mock_queued_task_cache() results = strategy_base._wait_on_pending_results( iterator=mock_iterator) self.assertEqual(len(results), 1) self.assertEqual(strategy_base._pending_results, 0) self.assertNotIn('test01', strategy_base._blocked_hosts) self.assertTrue(mock_handler_task.is_host_notified(mock_host)) # queue_items.append(('set_host_var', mock_host, mock_task, None, 'foo', 'bar')) # results = strategy_base._process_pending_results(iterator=mock_iterator) # self.assertEqual(len(results), 0) # self.assertEqual(strategy_base._pending_results, 1) # queue_items.append(('set_host_facts', mock_host, mock_task, None, 'foo', dict())) # results = strategy_base._process_pending_results(iterator=mock_iterator) # self.assertEqual(len(results), 0) # self.assertEqual(strategy_base._pending_results, 1) # queue_items.append(('bad')) # self.assertRaises(AnsibleError, strategy_base._process_pending_results, iterator=mock_iterator) strategy_base.cleanup()
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.playbook.role_include import IncludeRole from ansible.playbook.handler_task_include import HandlerTaskInclude from ansible.template import Templar if not isinstance(ds, list): raise AnsibleAssertionError( 'The ds (%s) should be a list but was a %s' % (ds, type(ds))) task_list = [] for task_ds in ds: if not isinstance(task_ds, dict): raise AnsibleAssertionError( 'The ds (%s) should be a dict but was a %s' % (ds, type(ds))) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: args_parser = ModuleArgsParser(task_ds) try: (action, args, delegate_to) = args_parser.parse(skip_action_validation=True) except AnsibleParserError as e: # if the raises exception was created with obj=ds args, then it includes the detail # so we dont need to add it so we can just re raise. if e.obj: raise # But if it wasn't, we can add the yaml object now to get more detail raise AnsibleParserError(to_native(e), obj=task_ds, orig_exc=e) if action in C._ACTION_ALL_INCLUDE_IMPORT_TASKS: if use_handlers: include_class = HandlerTaskInclude else: include_class = TaskInclude t = include_class.load(task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader) all_vars = variable_manager.get_vars(play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is dynamic or static: # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if action in C._ACTION_INCLUDE_TASKS: is_static = False elif action in C._ACTION_IMPORT_TASKS: is_static = True else: display.deprecated( '"include" is deprecated, use include_tasks/import_tasks instead', "2.16") is_static = not templar.is_template( t.args['_raw_params']) and t.all_parents_static( ) and not t.loop if is_static: if t.loop is not None: if action in C._ACTION_IMPORT_TASKS: raise AnsibleParserError( "You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.", obj=task_ds) else: raise AnsibleParserError( "You cannot use 'static' on an include with a loop", obj=task_ds) # we set a flag to indicate this include was static t.statically_loaded = True # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = block cumulative_path = None found = False subdir = 'tasks' if use_handlers: subdir = 'handlers' while parent_include is not None: if not isinstance(parent_include, TaskInclude): parent_include = parent_include._parent continue try: parent_include_dir = os.path.dirname( templar.template( parent_include.args.get('_raw_params'))) except AnsibleUndefinedVariable as e: if not parent_include.statically_loaded: raise AnsibleParserError( "Error when evaluating variable in dynamic parent include path: %s. " "When using static imports, the parent dynamic include cannot utilize host facts " "or variables from inventory" % parent_include.args.get('_raw_params'), obj=task_ds, suppress_extended_error=True, orig_exc=e) raise if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join( parent_include_dir, cumulative_path) include_target = templar.template( t.args['_raw_params']) if t._role: new_basedir = os.path.join(t._role._role_path, subdir, cumulative_path) include_file = loader.path_dwim_relative( new_basedir, subdir, include_target) else: include_file = loader.path_dwim_relative( loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): found = True break else: parent_include = parent_include._parent if not found: try: include_target = templar.template( t.args['_raw_params']) except AnsibleUndefinedVariable as e: raise AnsibleParserError( "Error when evaluating variable in import path: %s.\n\n" "When using static imports, ensure that any variables used in their names are defined in vars/vars_files\n" "or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory\n" "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, orig_exc=e) if t._role: include_file = loader.path_dwim_relative( t._role._role_path, subdir, include_target) else: include_file = loader.path_dwim(include_target) data = loader.load_from_file(include_file) if not data: display.warning( 'file %s is empty and had no tasks to include' % include_file) continue elif not isinstance(data, list): raise AnsibleParserError( "included task files must contain a list of tasks", obj=data) # since we can't send callbacks here, we display a message directly in # the same fashion used by the on_include callback. We also do it here, # because the recursive nature of helper methods means we may be loading # nested includes, and we want the include order printed correctly display.vv("statically imported: %s" % include_file) ti_copy = t.copy(exclude_parent=True) ti_copy._parent = block included_blocks = load_list_of_blocks( data, play=play, parent_block=None, task_include=ti_copy, role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) tags = ti_copy.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: t.is_static = False task_list.append(t) elif action in C._ACTION_ALL_PROPER_INCLUDE_IMPORT_ROLES: ir = IncludeRole.load( task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader, ) # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set is_static = False if action in C._ACTION_IMPORT_ROLE: is_static = True if is_static: if ir.loop is not None: if action in C._ACTION_IMPORT_ROLE: raise AnsibleParserError( "You cannot use loops on 'import_role' statements. You should use 'include_role' instead.", obj=task_ds) else: raise AnsibleParserError( "You cannot use 'static' on an include_role with a loop", obj=task_ds) # we set a flag to indicate this include was static ir.statically_loaded = True # template the role name now, if needed all_vars = variable_manager.get_vars(play=play, task=ir) templar = Templar(loader=loader, variables=all_vars) ir._role_name = templar.template(ir._role_name) # uses compiled list from object blocks, _ = ir.get_block_list( variable_manager=variable_manager, loader=loader) task_list.extend(blocks) else: # passes task object itself for latter generation of list task_list.append(ir) else: if use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.playbook.role_include import IncludeRole from ansible.playbook.handler_task_include import HandlerTaskInclude from ansible.template import Templar assert isinstance(ds, list) task_list = [] for task_ds in ds: assert isinstance(task_ds, dict) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: if 'include' in task_ds: if use_handlers: include_class = HandlerTaskInclude else: include_class = TaskInclude t = include_class.load( task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader ) all_vars = variable_manager.get_vars(loader=loader, play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is dynamic or static: # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if t.static is not None: is_static = t.static else: is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ (not templar._contains_vars(t.args['_raw_params']) and t.all_parents_static() and not t.loop) if is_static: if t.loop is not None: raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds) # we set a flag to indicate this include was static t.statically_loaded = True # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = block cumulative_path = None found = False subdir = 'tasks' if use_handlers: subdir = 'handlers' while parent_include is not None: if not isinstance(parent_include, TaskInclude): parent_include = parent_include._parent continue parent_include_dir = templar.template(os.path.dirname(parent_include.args.get('_raw_params'))) if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join(parent_include_dir, cumulative_path) include_target = templar.template(t.args['_raw_params']) if t._role: new_basedir = os.path.join(t._role._role_path, subdir, cumulative_path) include_file = loader.path_dwim_relative(new_basedir, subdir, include_target) else: include_file = loader.path_dwim_relative(loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): found = True break else: parent_include = parent_include._parent if not found: try: include_target = templar.template(t.args['_raw_params']) except AnsibleUndefinedVariable: raise AnsibleParserError( "Error when evaluating variable in include name: %s.\n\n" \ "When using static includes, ensure that any variables used in their names are defined in vars/vars_files\n" \ "or extra-vars passed in from the command line. Static includes cannot use variables from inventory\n" \ "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, ) if t._role: include_file = loader.path_dwim_relative(t._role._role_path, subdir, include_target) else: include_file = loader.path_dwim(include_target) try: data = loader.load_from_file(include_file) if data is None: return [] elif not isinstance(data, list): raise AnsibleParserError("included task files must contain a list of tasks", obj=data) # since we can't send callbacks here, we display a message directly in # the same fashion used by the on_include callback. We also do it here, # because the recursive nature of helper methods means we may be loading # nested includes, and we want the include order printed correctly display.vv("statically included: %s" % include_file) except AnsibleFileNotFound: if t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ C.DEFAULT_HANDLER_INCLUDES_STATIC and use_handlers: raise display.deprecated( "Included file '%s' not found, however since this include is not " \ "explicitly marked as 'static: yes', we will try and include it dynamically " \ "later. In the future, this will be an error unless 'static: no' is used " \ "on the include task. If you do not want missing includes to be considered " \ "dynamic, use 'static: yes' on the include or set the global ansible.cfg " \ "options to make all inclues static for tasks and/or handlers" % include_file, ) task_list.append(t) continue included_blocks = load_list_of_blocks( data, play=play, parent_block=None, task_include=t.copy(), role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) # pop tags out of the include args, if they were specified there, and assign # them to the include. If the include already had tags specified, we raise an # error so that users know not to specify them both ways tags = t.vars.pop('tags', []) if isinstance(tags, string_types): tags = tags.split(',') if len(tags) > 0: if len(t.tags) > 0: raise AnsibleParserError( "Include tasks should not specify tags in more than one way (both via args and directly on the task). " \ "Mixing styles in which tags are specified is prohibited for whole import hierarchy, not only for single import statement", obj=task_ds, suppress_extended_error=True, ) display.deprecated("You should not specify tags in the include parameters. All tags should be specified using the task-level option") else: tags = t.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: task_list.append(t) elif 'include_role' in task_ds: ir = IncludeRole.load( task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader ) # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if ir.static is not None: is_static = ir.static else: display.debug('Determine if include_role is static') # Check to see if this include is dynamic or static: all_vars = variable_manager.get_vars(loader=loader, play=play, task=ir) templar = Templar(loader=loader, variables=all_vars) needs_templating = False for param in ir.args: if templar._contains_vars(ir.args[param]): if not templar.templatable(ir.args[param]): needs_templating = True break is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ (not needs_templating and ir.all_parents_static() and not ir.loop) display.debug('Determined that if include_role static is %s' % str(is_static)) if is_static: # uses compiled list from object t = task_list.extend(ir.get_block_list(variable_manager=variable_manager, loader=loader)) else: # passes task object itself for latter generation of list t = task_list.append(ir) else: if use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
if True: ======= if 'block' in task: t = Block.load( task, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) else: >>>>>>> remote if use_handlers: t = Handler.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list def load_list_of_roles(ds, current_role_path=None, variable_manager=None, loader=None): ''' Loads and returns a list of RoleInclude objects from the datastructure list of role definitions ''' # we import here to prevent a circular dependency with imports
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task if not isinstance(ds, list): raise AnsibleAssertionError( 'The ds (%s) should be a list but was a %s' % (ds, type(ds))) task_list = [] for task_ds in ds: if not isinstance(task_ds, dict): raise AnsibleAssertionError( 'The ds (%s) should be a dict but was a %s' % (ds, type(ds))) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: args_parser = ModuleArgsParser(task_ds) try: (action, args, delegate_to) = args_parser.parse(skip_action_validation=True) except AnsibleParserError as e: # if the raises exception was created with obj=ds args, then it includes the detail # so we dont need to add it so we can just re raise. if e._obj: raise # But if it wasn't, we can add the yaml object now to get more detail raise AnsibleParserError(to_native(e), obj=task_ds, orig_exc=e) if action in ('include', 'import_tasks', 'include_tasks'): task_list = _list_action_in_task( task_ds, play, action, task_list, block=block, role=role, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader) elif action in ('include_role', 'import_role'): task_list = _list_action_in_role( task_ds, play, action, task_list, block=block, role=role, variable_manager=variable_manager, loader=loader) else: if use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.template import Templar assert isinstance(ds, list) task_list = [] for task_ds in ds: assert isinstance(task_ds, dict) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: if 'include' in task_ds: t = TaskInclude.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) all_vars = variable_manager.get_vars(loader=loader, play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is static, which can be true if: # 1. the user set the 'static' option to true # 2. one of the appropriate config options was set # 3. the included file name contains no variables, and has no loop is_static = t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ not templar._contains_vars(t.args.get('_raw_params')) and t.loop is None if is_static: if t.loop is not None: raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds) # FIXME: all of this code is very similar (if not identical) to that in # plugins/strategy/__init__.py, and should be unified to avoid # patches only being applied to one or the other location if task_include: # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = task_include cumulative_path = None while parent_include is not None: parent_include_dir = templar.template(os.path.dirname(parent_include.args.get('_raw_params'))) if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join(parent_include_dir, cumulative_path) include_target = templar.template(t.args['_raw_params']) if t._role: new_basedir = os.path.join(t._role._role_path, 'tasks', cumulative_path) include_file = loader.path_dwim_relative(new_basedir, 'tasks', include_target) else: include_file = loader.path_dwim_relative(loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): break else: parent_include = parent_include._task_include else: try: include_target = templar.template(t.args['_raw_params']) except AnsibleUndefinedVariable as e: raise AnsibleParserError( "Error when evaluating variable in include name: %s.\n\n" \ "When using static includes, ensure that any variables used in their names are defined in vars/vars_files\n" \ "or extra-vars passed in from the command line. Static includes cannot use variables from inventory\n" \ "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, ) if t._role: if use_handlers: include_file = loader.path_dwim_relative(t._role._role_path, 'handlers', include_target) else: include_file = loader.path_dwim_relative(t._role._role_path, 'tasks', include_target) else: include_file = loader.path_dwim(include_target) try: data = loader.load_from_file(include_file) if data is None: return [] elif not isinstance(data, list): raise AnsibleError("included task files must contain a list of tasks", obj=data) except AnsibleFileNotFound as e: if t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ C.DEFAULT_HANDLER_INCLUDES_STATIC and use_handlers: raise display.deprecated( "Included file '%s' not found, however since this include is not " \ "explicitly marked as 'static: yes', we will try and include it dynamically " \ "later. In the future, this will be an error unless 'static: no' is used " \ "on the include task. If you do not want missing includes to be considered " \ "dynamic, use 'static: yes' on the include or set the global ansible.cfg " \ "options to make all inclues static for tasks and/or handlers" % include_file, ) task_list.append(t) continue included_blocks = load_list_of_blocks( data, play=play, parent_block=block, task_include=t, role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) # pop tags out of the include args, if they were specified there, and assign # them to the include. If the include already had tags specified, we raise an # error so that users know not to specify them both ways tags = t.vars.pop('tags', []) if isinstance(tags, string_types): tags = tags.split(',') if len(tags) > 0: if len(t.tags) > 0: raise AnsibleParserError( "Include tasks should not specify tags in more than one way (both via args and directly on the task)." \ " Mixing tag specify styles is prohibited for whole import hierarchy, not only for single import statement", obj=task_ds, suppress_extended_error=True, ) display.deprecated("You should not specify tags in the include parameters. All tags should be specified using the task-level option") else: tags = t.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: send callback here somehow... # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: task_list.append(t) elif use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.playbook.role_include import IncludeRole from ansible.playbook.handler_task_include import HandlerTaskInclude from ansible.template import Templar assert isinstance(ds, list) task_list = [] for task_ds in ds: assert isinstance(task_ds, dict) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: if 'include' in task_ds: if use_handlers: include_class = HandlerTaskInclude else: include_class = TaskInclude t = include_class.load(task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader) all_vars = variable_manager.get_vars(loader=loader, play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is dynamic or static: # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if t.static is not None: is_static = t.static else: is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ (not templar._contains_vars(t.args['_raw_params']) and t.all_parents_static() and not t.loop) if is_static: if t.loop is not None: raise AnsibleParserError( "You cannot use 'static' on an include with a loop", obj=task_ds) # we set a flag to indicate this include was static t.statically_loaded = True # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = block cumulative_path = None found = False subdir = 'tasks' if use_handlers: subdir = 'handlers' while parent_include is not None: if not isinstance(parent_include, TaskInclude): parent_include = parent_include._parent continue parent_include_dir = templar.template( os.path.dirname( parent_include.args.get('_raw_params'))) if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join( parent_include_dir, cumulative_path) include_target = templar.template( t.args['_raw_params']) if t._role: new_basedir = os.path.join(t._role._role_path, subdir, cumulative_path) include_file = loader.path_dwim_relative( new_basedir, subdir, include_target) else: include_file = loader.path_dwim_relative( loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): found = True break else: parent_include = parent_include._parent if not found: try: include_target = templar.template( t.args['_raw_params']) except AnsibleUndefinedVariable: raise AnsibleParserError( "Error when evaluating variable in include name: %s.\n\n" \ "When using static includes, ensure that any variables used in their names are defined in vars/vars_files\n" \ "or extra-vars passed in from the command line. Static includes cannot use variables from inventory\n" \ "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, ) if t._role: include_file = loader.path_dwim_relative( t._role._role_path, subdir, include_target) else: include_file = loader.path_dwim(include_target) try: data = loader.load_from_file(include_file) if data is None: return [] elif not isinstance(data, list): raise AnsibleParserError( "included task files must contain a list of tasks", obj=data) # since we can't send callbacks here, we display a message directly in # the same fashion used by the on_include callback. We also do it here, # because the recursive nature of helper methods means we may be loading # nested includes, and we want the include order printed correctly display.vv("statically included: %s" % include_file) except AnsibleFileNotFound: if t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ C.DEFAULT_HANDLER_INCLUDES_STATIC and use_handlers: raise display.deprecated( "Included file '%s' not found, however since this include is not " \ "explicitly marked as 'static: yes', we will try and include it dynamically " \ "later. In the future, this will be an error unless 'static: no' is used " \ "on the include task. If you do not want missing includes to be considered " \ "dynamic, use 'static: yes' on the include or set the global ansible.cfg " \ "options to make all inclues static for tasks and/or handlers" % include_file, ) task_list.append(t) continue included_blocks = load_list_of_blocks( data, play=play, parent_block=None, task_include=t.copy(), role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) # pop tags out of the include args, if they were specified there, and assign # them to the include. If the include already had tags specified, we raise an # error so that users know not to specify them both ways tags = t.vars.pop('tags', []) if isinstance(tags, string_types): tags = tags.split(',') if len(tags) > 0: if len(t.tags) > 0: raise AnsibleParserError( "Include tasks should not specify tags in more than one way (both via args and directly on the task). " \ "Mixing styles in which tags are specified is prohibited for whole import hierarchy, not only for single import statement", obj=task_ds, suppress_extended_error=True, ) display.deprecated( "You should not specify tags in the include parameters. All tags should be specified using the task-level option" ) else: tags = t.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: task_list.append(t) elif 'include_role' in task_ds: ir = IncludeRole.load(task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader) # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if ir.static is not None: is_static = ir.static else: display.debug('Determine if include_role is static') # Check to see if this include is dynamic or static: all_vars = variable_manager.get_vars(loader=loader, play=play, task=ir) templar = Templar(loader=loader, variables=all_vars) needs_templating = False for param in ir.args: if templar._contains_vars(ir.args[param]): if not templar.templatable(ir.args[param]): needs_templating = True break is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ (not needs_templating and ir.all_parents_static() and not ir.loop) display.debug( 'Determined that if include_role static is %s' % str(is_static)) if is_static: # uses compiled list from object t = task_list.extend( ir.get_block_list(variable_manager=variable_manager, loader=loader)) else: # passes task object itself for latter generation of list t = task_list.append(ir) else: if use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.playbook.role_include import IncludeRole from ansible.playbook.handler_task_include import HandlerTaskInclude from ansible.template import Templar if not isinstance(ds, list): raise AnsibleAssertionError( 'The ds (%s) should be a list but was a %s' % (ds, type(ds))) task_list = [] for task_ds in ds: if not isinstance(task_ds, dict): raise AnsibleAssertionError( 'The ds (%s) should be a dict but was a %s' % (ds, type(ds))) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: args_parser = ModuleArgsParser(task_ds) try: (action, args, delegate_to) = args_parser.parse(skip_action_validation=True) except AnsibleParserError as e: # if the raises exception was created with obj=ds args, then it includes the detail # so we dont need to add it so we can just re raise. if e._obj: raise # But if it wasn't, we can add the yaml object now to get more detail raise AnsibleParserError(to_native(e), obj=task_ds, orig_exc=e) if action in ('include', 'import_tasks', 'include_tasks'): if use_handlers: include_class = HandlerTaskInclude else: include_class = TaskInclude t = include_class.load(task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader) all_vars = variable_manager.get_vars(play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is dynamic or static: # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set if action == 'include_tasks': is_static = False elif action == 'import_tasks': is_static = True elif t.static is not None: display.deprecated( "The use of 'static' has been deprecated. " "Use 'import_tasks' for static inclusion, or 'include_tasks' for dynamic inclusion", version='ansible.builtin:2.12') is_static = t.static else: is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ (not templar.is_template(t.args['_raw_params']) and t.all_parents_static() and not t.loop) if is_static: if t.loop is not None: if action == 'import_tasks': raise AnsibleParserError( "You cannot use loops on 'import_tasks' statements. You should use 'include_tasks' instead.", obj=task_ds) else: raise AnsibleParserError( "You cannot use 'static' on an include with a loop", obj=task_ds) # we set a flag to indicate this include was static t.statically_loaded = True # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = block cumulative_path = None found = False subdir = 'tasks' if use_handlers: subdir = 'handlers' while parent_include is not None: if not isinstance(parent_include, TaskInclude): parent_include = parent_include._parent continue try: parent_include_dir = os.path.dirname( templar.template( parent_include.args.get('_raw_params'))) except AnsibleUndefinedVariable as e: if not parent_include.statically_loaded: raise AnsibleParserError( "Error when evaluating variable in dynamic parent include path: %s. " "When using static imports, the parent dynamic include cannot utilize host facts " "or variables from inventory" % parent_include.args.get('_raw_params'), obj=task_ds, suppress_extended_error=True, orig_exc=e) raise if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join( parent_include_dir, cumulative_path) include_target = templar.template( t.args['_raw_params']) if t._role: new_basedir = os.path.join(t._role._role_path, subdir, cumulative_path) include_file = loader.path_dwim_relative( new_basedir, subdir, include_target) else: include_file = loader.path_dwim_relative( loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): found = True break else: parent_include = parent_include._parent if not found: try: include_target = templar.template( t.args['_raw_params']) except AnsibleUndefinedVariable as e: raise AnsibleParserError( "Error when evaluating variable in import path: %s.\n\n" "When using static imports, ensure that any variables used in their names are defined in vars/vars_files\n" "or extra-vars passed in from the command line. Static imports cannot use variables from facts or inventory\n" "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, orig_exc=e) if t._role: include_file = loader.path_dwim_relative( t._role._role_path, subdir, include_target) else: include_file = loader.path_dwim(include_target) try: data = loader.load_from_file(include_file) if data is None: display.warning( 'file %s is empty and had no tasks to include' % include_file) continue elif not isinstance(data, list): raise AnsibleParserError( "included task files must contain a list of tasks", obj=data) # since we can't send callbacks here, we display a message directly in # the same fashion used by the on_include callback. We also do it here, # because the recursive nature of helper methods means we may be loading # nested includes, and we want the include order printed correctly display.vv("statically imported: %s" % include_file) except AnsibleFileNotFound: if action != 'include' or t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ C.DEFAULT_HANDLER_INCLUDES_STATIC and use_handlers: raise display.deprecated( "Included file '%s' not found, however since this include is not " "explicitly marked as 'static: yes', we will try and include it dynamically " "later. In the future, this will be an error unless 'static: no' is used " "on the include task. If you do not want missing includes to be considered " "dynamic, use 'static: yes' on the include or set the global ansible.cfg " "options to make all includes static for tasks and/or handlers" % include_file, version="ansible.builtin:2.12") task_list.append(t) continue ti_copy = t.copy(exclude_parent=True) ti_copy._parent = block included_blocks = load_list_of_blocks( data, play=play, parent_block=None, task_include=ti_copy, role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) # FIXME: remove once 'include' is removed # pop tags out of the include args, if they were specified there, and assign # them to the include. If the include already had tags specified, we raise an # error so that users know not to specify them both ways tags = ti_copy.vars.pop('tags', []) if isinstance(tags, string_types): tags = tags.split(',') if len(tags) > 0: if action in ('include_tasks', 'import_tasks'): raise AnsibleParserError( 'You cannot specify "tags" inline to the task, it is a task keyword' ) if len(ti_copy.tags) > 0: raise AnsibleParserError( "Include tasks should not specify tags in more than one way (both via args and directly on the task). " "Mixing styles in which tags are specified is prohibited for whole import hierarchy, not only for single import statement", obj=task_ds, suppress_extended_error=True, ) display.deprecated( "You should not specify tags in the include parameters. All tags should be specified using the task-level option", version="ansible.builtin:2.12") else: tags = ti_copy.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: t.is_static = False task_list.append(t) elif action in ('include_role', 'import_role'): ir = IncludeRole.load( task_ds, block=block, role=role, task_include=None, variable_manager=variable_manager, loader=loader, ) # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set is_static = False if action == 'import_role': is_static = True elif ir.static is not None: display.deprecated( "The use of 'static' for 'include_role' has been deprecated. " "Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion", version='ansible.builtin:2.12') is_static = ir.static if is_static: if ir.loop is not None: if action == 'import_role': raise AnsibleParserError( "You cannot use loops on 'import_role' statements. You should use 'include_role' instead.", obj=task_ds) else: raise AnsibleParserError( "You cannot use 'static' on an include_role with a loop", obj=task_ds) # we set a flag to indicate this include was static ir.statically_loaded = True # template the role name now, if needed all_vars = variable_manager.get_vars(play=play, task=ir) templar = Templar(loader=loader, variables=all_vars) ir._role_name = templar.template(ir._role_name) # uses compiled list from object blocks, _ = ir.get_block_list( variable_manager=variable_manager, loader=loader) task_list.extend(blocks) else: # passes task object itself for latter generation of list task_list.append(ir) else: if use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list
def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_handlers=False, variable_manager=None, loader=None): ''' Given a list of task datastructures (parsed from YAML), return a list of Task() or TaskInclude() objects. ''' # we import here to prevent a circular dependency with imports from ansible.playbook.block import Block from ansible.playbook.handler import Handler from ansible.playbook.task import Task from ansible.playbook.task_include import TaskInclude from ansible.template import Templar assert isinstance(ds, list) task_list = [] for task_ds in ds: assert isinstance(task_ds, dict) if 'block' in task_ds: t = Block.load( task_ds, play=play, parent_block=block, role=role, task_include=task_include, use_handlers=use_handlers, variable_manager=variable_manager, loader=loader, ) task_list.append(t) else: if 'include' in task_ds: t = TaskInclude.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) all_vars = variable_manager.get_vars(loader=loader, play=play, task=t) templar = Templar(loader=loader, variables=all_vars) # check to see if this include is static, which can be true if: # 1. the user set the 'static' option to true # 2. one of the appropriate config options was set # 3. the included file name contains no variables, and has no loop is_static = t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ (use_handlers and C.DEFAULT_HANDLER_INCLUDES_STATIC) or \ not templar._contains_vars(t.args.get('_raw_params')) and t.loop is None if is_static: if t.loop is not None: raise AnsibleParserError( "You cannot use 'static' on an include with a loop", obj=task_ds) # FIXME: all of this code is very similar (if not identical) to that in # plugins/strategy/__init__.py, and should be unified to avoid # patches only being applied to one or the other location if task_include: # handle relative includes by walking up the list of parent include # tasks and checking the relative result to see if it exists parent_include = task_include cumulative_path = None while parent_include is not None: parent_include_dir = templar.template( os.path.dirname( parent_include.args.get('_raw_params'))) if cumulative_path is None: cumulative_path = parent_include_dir elif not os.path.isabs(cumulative_path): cumulative_path = os.path.join( parent_include_dir, cumulative_path) include_target = templar.template( t.args['_raw_params']) if t._role: new_basedir = os.path.join( t._role._role_path, 'tasks', cumulative_path) include_file = loader.path_dwim_relative( new_basedir, 'tasks', include_target) else: include_file = loader.path_dwim_relative( loader.get_basedir(), cumulative_path, include_target) if os.path.exists(include_file): break else: parent_include = parent_include._task_include else: try: include_target = templar.template( t.args['_raw_params']) except AnsibleUndefinedVariable as e: raise AnsibleParserError( "Error when evaluating variable in include name: %s.\n\n" \ "When using static includes, ensure that any variables used in their names are defined in vars/vars_files\n" \ "or extra-vars passed in from the command line. Static includes cannot use variables from inventory\n" \ "sources like group or host vars." % t.args['_raw_params'], obj=task_ds, suppress_extended_error=True, ) if t._role: if use_handlers: include_file = loader.path_dwim_relative( t._role._role_path, 'handlers', include_target) else: include_file = loader.path_dwim_relative( t._role._role_path, 'tasks', include_target) else: include_file = loader.path_dwim(include_target) data = loader.load_from_file(include_file) if data is None: return [] elif not isinstance(data, list): raise AnsibleError( "included task files must contain a list of tasks", obj=data) included_blocks = load_list_of_blocks( data, play=play, parent_block=block, task_include=t, role=role, use_handlers=use_handlers, loader=loader, variable_manager=variable_manager, ) # Remove the raw params field from the module args, so it won't show up # later when getting the vars for this task/childen t.args.pop('_raw_params', None) # pop tags out of the include args, if they were specified there, and assign # them to the include. If the include already had tags specified, we raise an # error so that users know not to specify them both ways tags = t.vars.pop('tags', []) if isinstance(tags, string_types): tags = tags.split(',') if len(tags) > 0: if len(t.tags) > 0: raise AnsibleParserError( "Include tasks should not specify tags in more than one way (both via args and directly on the task)." \ " Mixing tag specify styles is prohibited for whole import hierarchy, not only for single import statement", obj=task_ds, suppress_extended_error=True, ) display.deprecated( "You should not specify tags in the include parameters. All tags should be specified using the task-level option" ) else: tags = t.tags[:] # now we extend the tags on each of the included blocks for b in included_blocks: b.tags = list(set(b.tags).union(tags)) # END FIXME # FIXME: send callback here somehow... # FIXME: handlers shouldn't need this special handling, but do # right now because they don't iterate blocks correctly if use_handlers: for b in included_blocks: task_list.extend(b.block) else: task_list.extend(included_blocks) else: task_list.append(t) elif use_handlers: t = Handler.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) else: t = Task.load(task_ds, block=block, role=role, task_include=task_include, variable_manager=variable_manager, loader=loader) task_list.append(t) return task_list