/lib/ansible/playbook/task_include.py

https://github.com/debfx/ansible · Python · 143 lines · 83 code · 27 blank · 33 comment · 18 complexity · 827b82692ea2dd7ddf59c503505a7665 MD5 · raw file

  1. # (c) 2012-2014, Michael DeHaan <michael.dehaan@gmail.com>
  2. #
  3. # This file is part of Ansible
  4. #
  5. # Ansible is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # Ansible is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with Ansible. If not, see <http://www.gnu.org/licenses/>.
  17. # Make coding more python3-ish
  18. from __future__ import (absolute_import, division, print_function)
  19. __metaclass__ = type
  20. import ansible.constants as C
  21. from ansible.errors import AnsibleParserError
  22. from ansible.playbook.attribute import FieldAttribute
  23. from ansible.playbook.block import Block
  24. from ansible.playbook.task import Task
  25. from ansible.utils.display import Display
  26. from ansible.utils.sentinel import Sentinel
  27. __all__ = ['TaskInclude']
  28. display = Display()
  29. class TaskInclude(Task):
  30. """
  31. A task include is derived from a regular task to handle the special
  32. circumstances related to the `- include: ...` task.
  33. """
  34. BASE = frozenset(('file', '_raw_params')) # directly assigned
  35. OTHER_ARGS = frozenset(('apply',)) # assigned to matching property
  36. VALID_ARGS = BASE.union(OTHER_ARGS) # all valid args
  37. VALID_INCLUDE_KEYWORDS = frozenset(('action', 'args', 'debugger', 'ignore_errors', 'loop', 'loop_control',
  38. 'loop_with', 'name', 'no_log', 'register', 'run_once', 'tags', 'vars',
  39. 'when'))
  40. # =================================================================================
  41. # ATTRIBUTES
  42. _static = FieldAttribute(isa='bool', default=None)
  43. def __init__(self, block=None, role=None, task_include=None):
  44. super(TaskInclude, self).__init__(block=block, role=role, task_include=task_include)
  45. self.statically_loaded = False
  46. @staticmethod
  47. def load(data, block=None, role=None, task_include=None, variable_manager=None, loader=None):
  48. ti = TaskInclude(block=block, role=role, task_include=task_include)
  49. task = ti.load_data(data, variable_manager=variable_manager, loader=loader)
  50. # Validate options
  51. my_arg_names = frozenset(task.args.keys())
  52. # validate bad args, otherwise we silently ignore
  53. bad_opts = my_arg_names.difference(TaskInclude.VALID_ARGS)
  54. if bad_opts and task.action in ('include_tasks', 'import_tasks'):
  55. raise AnsibleParserError('Invalid options for %s: %s' % (task.action, ','.join(list(bad_opts))), obj=data)
  56. if not task.args.get('_raw_params'):
  57. task.args['_raw_params'] = task.args.pop('file')
  58. apply_attrs = task.args.get('apply', {})
  59. if apply_attrs and task.action != 'include_tasks':
  60. raise AnsibleParserError('Invalid options for %s: apply' % task.action, obj=data)
  61. elif not isinstance(apply_attrs, dict):
  62. raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data)
  63. return task
  64. def preprocess_data(self, ds):
  65. ds = super(TaskInclude, self).preprocess_data(ds)
  66. diff = set(ds.keys()).difference(TaskInclude.VALID_INCLUDE_KEYWORDS)
  67. for k in diff:
  68. # This check doesn't handle ``include`` as we have no idea at this point if it is static or not
  69. if ds[k] is not Sentinel and ds['action'] in ('include_tasks', 'include_role'):
  70. if C.INVALID_TASK_ATTRIBUTE_FAILED:
  71. raise AnsibleParserError("'%s' is not a valid attribute for a %s" % (k, self.__class__.__name__), obj=ds)
  72. else:
  73. display.warning("Ignoring invalid attribute: %s" % k)
  74. return ds
  75. def copy(self, exclude_parent=False, exclude_tasks=False):
  76. new_me = super(TaskInclude, self).copy(exclude_parent=exclude_parent, exclude_tasks=exclude_tasks)
  77. new_me.statically_loaded = self.statically_loaded
  78. return new_me
  79. def get_vars(self):
  80. '''
  81. We override the parent Task() classes get_vars here because
  82. we need to include the args of the include into the vars as
  83. they are params to the included tasks. But ONLY for 'include'
  84. '''
  85. if self.action != 'include':
  86. all_vars = super(TaskInclude, self).get_vars()
  87. else:
  88. all_vars = dict()
  89. if self._parent:
  90. all_vars.update(self._parent.get_vars())
  91. all_vars.update(self.vars)
  92. all_vars.update(self.args)
  93. if 'tags' in all_vars:
  94. del all_vars['tags']
  95. if 'when' in all_vars:
  96. del all_vars['when']
  97. return all_vars
  98. def build_parent_block(self):
  99. '''
  100. This method is used to create the parent block for the included tasks
  101. when ``apply`` is specified
  102. '''
  103. apply_attrs = self.args.pop('apply', {})
  104. if apply_attrs:
  105. apply_attrs['block'] = []
  106. p_block = Block.load(
  107. apply_attrs,
  108. play=self._parent._play,
  109. task_include=self,
  110. role=self._role,
  111. variable_manager=self._variable_manager,
  112. loader=self._loader,
  113. )
  114. else:
  115. p_block = self
  116. return p_block