PageRenderTime 24ms CodeModel.GetById 14ms app.highlight 7ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ansible/playbook/task_include.py

https://github.com/debfx/ansible
Python | 143 lines | 92 code | 22 blank | 29 comment | 15 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
 18# Make coding more python3-ish
 19from __future__ import (absolute_import, division, print_function)
 20__metaclass__ = type
 21
 22import ansible.constants as C
 23from ansible.errors import AnsibleParserError
 24from ansible.playbook.attribute import FieldAttribute
 25from ansible.playbook.block import Block
 26from ansible.playbook.task import Task
 27from ansible.utils.display import Display
 28from ansible.utils.sentinel import Sentinel
 29
 30__all__ = ['TaskInclude']
 31
 32display = Display()
 33
 34
 35class TaskInclude(Task):
 36
 37    """
 38    A task include is derived from a regular task to handle the special
 39    circumstances related to the `- include: ...` task.
 40    """
 41
 42    BASE = frozenset(('file', '_raw_params'))  # directly assigned
 43    OTHER_ARGS = frozenset(('apply',))  # assigned to matching property
 44    VALID_ARGS = BASE.union(OTHER_ARGS)  # all valid args
 45    VALID_INCLUDE_KEYWORDS = frozenset(('action', 'args', 'debugger', 'ignore_errors', 'loop', 'loop_control',
 46                                        'loop_with', 'name', 'no_log', 'register', 'run_once', 'tags', 'vars',
 47                                        'when'))
 48
 49    # =================================================================================
 50    # ATTRIBUTES
 51
 52    _static = FieldAttribute(isa='bool', default=None)
 53
 54    def __init__(self, block=None, role=None, task_include=None):
 55        super(TaskInclude, self).__init__(block=block, role=role, task_include=task_include)
 56        self.statically_loaded = False
 57
 58    @staticmethod
 59    def load(data, block=None, role=None, task_include=None, variable_manager=None, loader=None):
 60        ti = TaskInclude(block=block, role=role, task_include=task_include)
 61        task = ti.load_data(data, variable_manager=variable_manager, loader=loader)
 62
 63        # Validate options
 64        my_arg_names = frozenset(task.args.keys())
 65
 66        # validate bad args, otherwise we silently ignore
 67        bad_opts = my_arg_names.difference(TaskInclude.VALID_ARGS)
 68        if bad_opts and task.action in ('include_tasks', 'import_tasks'):
 69            raise AnsibleParserError('Invalid options for %s: %s' % (task.action, ','.join(list(bad_opts))), obj=data)
 70
 71        if not task.args.get('_raw_params'):
 72            task.args['_raw_params'] = task.args.pop('file')
 73
 74        apply_attrs = task.args.get('apply', {})
 75        if apply_attrs and task.action != 'include_tasks':
 76            raise AnsibleParserError('Invalid options for %s: apply' % task.action, obj=data)
 77        elif not isinstance(apply_attrs, dict):
 78            raise AnsibleParserError('Expected a dict for apply but got %s instead' % type(apply_attrs), obj=data)
 79
 80        return task
 81
 82    def preprocess_data(self, ds):
 83        ds = super(TaskInclude, self).preprocess_data(ds)
 84
 85        diff = set(ds.keys()).difference(TaskInclude.VALID_INCLUDE_KEYWORDS)
 86        for k in diff:
 87            # This check doesn't handle ``include`` as we have no idea at this point if it is static or not
 88            if ds[k] is not Sentinel and ds['action'] in ('include_tasks', 'include_role'):
 89                if C.INVALID_TASK_ATTRIBUTE_FAILED:
 90                    raise AnsibleParserError("'%s' is not a valid attribute for a %s" % (k, self.__class__.__name__), obj=ds)
 91                else:
 92                    display.warning("Ignoring invalid attribute: %s" % k)
 93
 94        return ds
 95
 96    def copy(self, exclude_parent=False, exclude_tasks=False):
 97        new_me = super(TaskInclude, self).copy(exclude_parent=exclude_parent, exclude_tasks=exclude_tasks)
 98        new_me.statically_loaded = self.statically_loaded
 99        return new_me
100
101    def get_vars(self):
102        '''
103        We override the parent Task() classes get_vars here because
104        we need to include the args of the include into the vars as
105        they are params to the included tasks. But ONLY for 'include'
106        '''
107        if self.action != 'include':
108            all_vars = super(TaskInclude, self).get_vars()
109        else:
110            all_vars = dict()
111            if self._parent:
112                all_vars.update(self._parent.get_vars())
113
114            all_vars.update(self.vars)
115            all_vars.update(self.args)
116
117            if 'tags' in all_vars:
118                del all_vars['tags']
119            if 'when' in all_vars:
120                del all_vars['when']
121
122        return all_vars
123
124    def build_parent_block(self):
125        '''
126        This method is used to create the parent block for the included tasks
127        when ``apply`` is specified
128        '''
129        apply_attrs = self.args.pop('apply', {})
130        if apply_attrs:
131            apply_attrs['block'] = []
132            p_block = Block.load(
133                apply_attrs,
134                play=self._parent._play,
135                task_include=self,
136                role=self._role,
137                variable_manager=self._variable_manager,
138                loader=self._loader,
139            )
140        else:
141            p_block = self
142
143        return p_block