diff options
author | James Cammarata <jimi@sngx.net> | 2017-06-06 16:39:48 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-06-06 16:39:48 -0500 |
commit | 483df9c5f83befbef85796090c0f61de6436b130 (patch) | |
tree | fa4ca2141a84adda4b402725d78c9a8f9a1b9be2 /lib/ansible | |
parent | 354939167360b7259386aad8e461de8f35c87236 (diff) | |
download | ansible-483df9c5f83befbef85796090c0f61de6436b130.tar.gz |
Imports and includes (#25399)
Initial commit to split includes into static imports/dynamic includes
This implements the new include/import syntax for Ansible 2.4:
* include_{tasks,role,variables} = dynamic
* import_{playbook,tasks,role} = static
The old bare `include` will be considered deprecated, as will any use of the `static: {yes|no}` option.
This also adds docs for import/include and reorganizing the "Playbook Reuse" section of the documentation.
Diffstat (limited to 'lib/ansible')
-rw-r--r-- | lib/ansible/executor/task_executor.py | 4 | ||||
-rw-r--r-- | lib/ansible/executor/task_queue_manager.py | 10 | ||||
-rw-r--r-- | lib/ansible/parsing/mod_args.py | 6 | ||||
-rw-r--r-- | lib/ansible/playbook/__init__.py | 4 | ||||
-rw-r--r-- | lib/ansible/playbook/helpers.py | 28 | ||||
-rw-r--r-- | lib/ansible/playbook/included_file.py | 2 | ||||
-rw-r--r-- | lib/ansible/playbook/playbook_include.py | 28 | ||||
-rw-r--r-- | lib/ansible/playbook/role_include.py | 5 | ||||
-rw-r--r-- | lib/ansible/playbook/task.py | 10 | ||||
-rw-r--r-- | lib/ansible/plugins/callback/default.py | 5 | ||||
-rw-r--r-- | lib/ansible/plugins/strategy/__init__.py | 2 |
11 files changed, 63 insertions, 41 deletions
diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index cc8276ec0d..7df69f48aa 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -432,7 +432,7 @@ class TaskExecutor: if self._loop_eval_error is not None: raise self._loop_eval_error # skip conditional exception in the case of includes as the vars needed might not be available except in the included tasks or due to tags - if self._task.action not in ['include', 'include_role']: + if self._task.action not in ['include', 'include_tasks', 'include_role']: raise # Not skipping, if we had loop error raised earlier we need to raise it now to halt the execution of this task @@ -445,7 +445,7 @@ class TaskExecutor: # if this task is a TaskInclude, we just return now with a success code so the # main thread can expand the task list for the given host - if self._task.action == 'include': + if self._task.action in ('include', 'include_tasks'): include_variables = self._task.args.copy() include_file = include_variables.pop('_raw_params', None) if not include_file: diff --git a/lib/ansible/executor/task_queue_manager.py b/lib/ansible/executor/task_queue_manager.py index 3e24853b19..f5a6886e1c 100644 --- a/lib/ansible/executor/task_queue_manager.py +++ b/lib/ansible/executor/task_queue_manager.py @@ -83,11 +83,11 @@ class TaskQueueManager: self._callback_plugins = [] self._start_at_done = False - # make sure the module path (if specified) is parsed and - # added to the module_loader object - if options.module_path is not None: - for path in options.module_path.split(os.pathsep): - module_loader.add_directory(path) + # make sure any module paths (if specified) are added to the module_loader + if isinstance(options.module_path, list): + for path in options.module_path: + if path is not None: + module_loader.add_directory(path) # a special flag to help us exit cleanly self._terminated = False diff --git a/lib/ansible/parsing/mod_args.py b/lib/ansible/parsing/mod_args.py index 2ed4b9202c..d48a3d2468 100644 --- a/lib/ansible/parsing/mod_args.py +++ b/lib/ansible/parsing/mod_args.py @@ -36,6 +36,10 @@ RAW_PARAM_MODULES = ([ 'script', 'include', 'include_vars', + 'include_tasks', + 'include_role', + 'import_tasks', + 'import_role', 'add_host', 'group_by', 'set_fact', @@ -281,7 +285,7 @@ class ModuleArgsParser: # walk the input dictionary to see we recognize a module name for (item, value) in iteritems(self._task_ds): - if item in module_loader or item in ['meta', 'include', 'include_role']: + if item in module_loader or item in ['meta', 'include', 'include_tasks', 'include_role', 'import_tasks', 'import_role']: # finding more than one module name is a problem if action is not None: raise AnsibleParserError("conflicting action statements: %s, %s" % (action, item), obj=self._task_ds) diff --git a/lib/ansible/playbook/__init__.py b/lib/ansible/playbook/__init__.py index c47025d5eb..cc0451a2f8 100644 --- a/lib/ansible/playbook/__init__.py +++ b/lib/ansible/playbook/__init__.py @@ -89,7 +89,9 @@ class Playbook: self._loader.set_basedir(cur_basedir) raise AnsibleParserError("playbook entries must be either a valid play or an include statement", obj=entry) - if 'include' in entry: + if 'include' in entry or 'import_playbook' in entry: + if 'include' in entry: + display.deprecated("You should use 'import_playbook' instead of 'include' for playbook includes") pb = PlaybookInclude.load(entry, basedir=self._basedir, variable_manager=variable_manager, loader=self._loader) if pb is not None: self._entries.extend(pb._entries) diff --git a/lib/ansible/playbook/helpers.py b/lib/ansible/playbook/helpers.py index f2a5b6fb33..7b500de44a 100644 --- a/lib/ansible/playbook/helpers.py +++ b/lib/ansible/playbook/helpers.py @@ -108,7 +108,11 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h ) task_list.append(t) else: - if 'include' in task_ds: + if 'include' in task_ds or 'import_tasks' in task_ds or 'include_tasks' in task_ds: + if 'include' in task_ds: + display.deprecated("The use of 'include' for tasks has been deprecated. " + "Use 'import_tasks' for static inclusions or 'include_tasks' for dynamic inclusions") + if use_handlers: include_class = HandlerTaskInclude else: @@ -129,7 +133,13 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # 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: + if 'include_tasks' in task_ds: + is_static = False + elif 'import_tasks' in task_ds: + is_static = True + elif t.static is not None: + display.deprecated("The use of 'static' has been deprecated. " + "Use 'import_role' for static inclusion, or 'include_role' for dynamic inclusion") is_static = t.static else: is_static = C.DEFAULT_TASK_INCLUDES_STATIC or \ @@ -138,7 +148,10 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h if is_static: if t.loop is not None: - raise AnsibleParserError("You cannot use 'static' on an include with a loop", obj=task_ds) + if 'import_tasks' in task_ds: + 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 @@ -202,7 +215,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # 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) + display.vv("statically imported: %s" % include_file) except AnsibleFileNotFound: if t.static or \ C.DEFAULT_TASK_INCLUDES_STATIC or \ @@ -267,8 +280,7 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h else: task_list.append(t) - elif 'include_role' in task_ds: - + elif 'include_role' in task_ds or 'import_role' in task_ds: ir = IncludeRole.load( task_ds, block=block, @@ -280,7 +292,11 @@ def load_list_of_tasks(ds, play, block=None, role=None, task_include=None, use_h # 1. the user has set the 'static' option to false or true # 2. one of the appropriate config options was set + if 'import_role' in task_ds: + is_static = True if 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") is_static = ir.static else: display.debug('Determine if include_role is static') diff --git a/lib/ansible/playbook/included_file.py b/lib/ansible/playbook/included_file.py index 5a7902db74..5784ea0650 100644 --- a/lib/ansible/playbook/included_file.py +++ b/lib/ansible/playbook/included_file.py @@ -64,7 +64,7 @@ class IncludedFile: original_host = res._host original_task = res._task - if original_task.action == 'include': + if original_task.action in ('include', 'include_tasks'): if original_task.loop: if 'results' not in res._result: continue diff --git a/lib/ansible/playbook/playbook_include.py b/lib/ansible/playbook/playbook_include.py index 3cf6aca188..04423ff6ec 100644 --- a/lib/ansible/playbook/playbook_include.py +++ b/lib/ansible/playbook/playbook_include.py @@ -35,7 +35,7 @@ from ansible.template import Templar class PlaybookInclude(Base, Conditional, Taggable): _name = FieldAttribute(isa='string') - _include = FieldAttribute(isa='string') + _import_playbook = FieldAttribute(isa='string') _vars = FieldAttribute(isa='dict', default=dict()) @staticmethod @@ -66,7 +66,7 @@ class PlaybookInclude(Base, Conditional, Taggable): # then we use the object to load a Playbook pb = Playbook(loader=loader) - file_name = templar.template(new_obj.include) + file_name = templar.template(new_obj.import_playbook) if not os.path.isabs(file_name): file_name = os.path.join(basedir, file_name) @@ -114,35 +114,35 @@ class PlaybookInclude(Base, Conditional, Taggable): new_ds.ansible_pos = ds.ansible_pos for (k, v) in iteritems(ds): - if k == 'include': - self._preprocess_include(ds, new_ds, k, v) + if k in ('include', 'import_playbook'): + self._preprocess_import(ds, new_ds, k, v) else: # some basic error checking, to make sure vars are properly # formatted and do not conflict with k=v parameters if k == 'vars': if 'vars' in new_ds: - raise AnsibleParserError("include parameters cannot be mixed with 'vars' entries for include statements", obj=ds) + raise AnsibleParserError("import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) elif not isinstance(v, dict): - raise AnsibleParserError("vars for include statements must be specified as a dictionary", obj=ds) + raise AnsibleParserError("vars for import_playbook statements must be specified as a dictionary", obj=ds) new_ds[k] = v return super(PlaybookInclude, self).preprocess_data(new_ds) - def _preprocess_include(self, ds, new_ds, k, v): + def _preprocess_import(self, ds, new_ds, k, v): ''' - Splits the include line up into filename and parameters + Splits the playbook import line up into filename and parameters ''' if v is None: - raise AnsibleParserError("include parameter is missing", obj=ds) + raise AnsibleParserError("playbook import parameter is missing", obj=ds) - # The include line must include at least one item, which is the filename - # to include. Anything after that should be regarded as a parameter to the include + # The import_playbook line must include at least one item, which is the filename + # to import. Anything after that should be regarded as a parameter to the import items = split_args(v) if len(items) == 0: - raise AnsibleParserError("include statements must specify the file name to include", obj=ds) + raise AnsibleParserError("import_playbook statements must specify the file name to import", obj=ds) else: - new_ds['include'] = items[0] + new_ds['import_playbook'] = items[0] if len(items) > 1: # rejoin the parameter portion of the arguments and # then use parse_kv() to get a dict of params back @@ -150,5 +150,5 @@ class PlaybookInclude(Base, Conditional, Taggable): if 'tags' in params: new_ds['tags'] = params.pop('tags') if 'vars' in new_ds: - raise AnsibleParserError("include parameters cannot be mixed with 'vars' entries for include statements", obj=ds) + raise AnsibleParserError("import_playbook parameters cannot be mixed with 'vars' entries for import statements", obj=ds) new_ds['vars'] = params diff --git a/lib/ansible/playbook/role_include.py b/lib/ansible/playbook/role_include.py index 44d57fab57..ccb2b1cf9f 100644 --- a/lib/ansible/playbook/role_include.py +++ b/lib/ansible/playbook/role_include.py @@ -23,7 +23,7 @@ from os.path import basename from ansible.errors import AnsibleParserError from ansible.playbook.attribute import FieldAttribute -from ansible.playbook.task import Task +from ansible.playbook.task_include import TaskInclude from ansible.playbook.role import Role from ansible.playbook.role.include import RoleInclude @@ -36,7 +36,7 @@ except ImportError: __all__ = ['IncludeRole'] -class IncludeRole(Task): +class IncludeRole(TaskInclude): """ A Role include is derived from a regular role to handle the special @@ -55,7 +55,6 @@ class IncludeRole(Task): super(IncludeRole, self).__init__(block=block, role=role, task_include=task_include) - self.statically_loaded = False self._from_files = {} self._parent_role = role self._role_name = None diff --git a/lib/ansible/playbook/task.py b/lib/ansible/playbook/task.py index 979f0ce8a5..a40cd79b28 100644 --- a/lib/ansible/playbook/task.py +++ b/lib/ansible/playbook/task.py @@ -215,10 +215,10 @@ class Task(Base, Conditional, Taggable, Become): # top level of the task, so we move those into the 'vars' dictionary # here, and show a deprecation message as we will remove this at # some point in the future. - if action == 'include' and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES: - display.deprecated("Specifying include variables at the top-level of the task is deprecated. " - "Please see:\nhttp://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n " - "for currently supported syntax regarding included files and variables", version="2.7") + if action in ('include', 'include_tasks') and k not in self._valid_attrs and k not in self.DEPRECATED_ATTRIBUTES: + display.deprecated("Specifying include variables at the top-level of the task is deprecated." + " Please see:\nhttp://docs.ansible.com/ansible/playbooks_roles.html#task-include-files-and-encouraging-reuse\n\n" + " for currently supported syntax regarding included files and variables", version="2.7") new_ds['vars'][k] = v else: new_ds[k] = v @@ -331,7 +331,7 @@ class Task(Base, Conditional, Taggable, Become): all_vars = dict() if self._parent: all_vars.update(self._parent.get_include_params()) - if self.action in ('include', 'include_role'): + if self.action in ('include', 'include_tasks', 'include_role'): all_vars.update(self.vars) return all_vars diff --git a/lib/ansible/plugins/callback/default.py b/lib/ansible/plugins/callback/default.py index 8313c39513..87d7e33642 100644 --- a/lib/ansible/plugins/callback/default.py +++ b/lib/ansible/plugins/callback/default.py @@ -27,6 +27,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type from ansible import constants as C +from ansible.playbook.task_include import TaskInclude from ansible.plugins.callback import CallbackBase from ansible.utils.color import colorize, hostcolor @@ -80,7 +81,7 @@ class CallbackModule(CallbackBase): delegated_vars = result._result.get('_ansible_delegated_vars', None) self._clean_results(result._result, result._task.action) - if result._task.action in ('include', 'include_role'): + if isinstance(result._task, TaskInclude): return elif result._result.get('changed', False): if delegated_vars: @@ -194,7 +195,7 @@ class CallbackModule(CallbackBase): def v2_runner_item_on_ok(self, result): delegated_vars = result._result.get('_ansible_delegated_vars', None) - if result._task.action in ('include', 'include_role'): + if isinstance(result._task, TaskInclude): return elif result._result.get('changed', False): msg = 'changed' diff --git a/lib/ansible/plugins/strategy/__init__.py b/lib/ansible/plugins/strategy/__init__.py index a26074b5b9..2b10f87b7a 100644 --- a/lib/ansible/plugins/strategy/__init__.py +++ b/lib/ansible/plugins/strategy/__init__.py @@ -535,7 +535,7 @@ class StrategyBase: if self._diff: self._tqm.send_callback('v2_on_file_diff', task_result) - if original_task.action not in ['include', 'include_role']: + if not isinstance(original_task, TaskInclude): self._tqm._stats.increment('ok', original_host.name) if 'changed' in task_result._result and task_result._result['changed']: self._tqm._stats.increment('changed', original_host.name) |