diff --git a/lib/ansible/executor/task_executor.py b/lib/ansible/executor/task_executor.py index 2efee392c54..36264c1185a 100644 --- a/lib/ansible/executor/task_executor.py +++ b/lib/ansible/executor/task_executor.py @@ -521,6 +521,18 @@ class TaskExecutor: # get handler self._handler = self._get_action_handler(connection=self._connection, templar=templar) + # Apply default params for action/module, if present + # These are collected as a list of dicts, so we need to merge them + module_defaults = {} + for default in self._task.module_defaults: + module_defaults.update(default) + if module_defaults: + module_defaults = templar.template(module_defaults) + if self._task.action in module_defaults: + tmp_args = module_defaults[self._task.action].copy() + tmp_args.update(self._task.args) + self._task.args = tmp_args + # And filter out any fields which were set to default(omit), and got the omit token value omit_token = variables.get('omit') if omit_token is not None: diff --git a/lib/ansible/playbook/base.py b/lib/ansible/playbook/base.py index e1fde75d26a..9662e913b50 100644 --- a/lib/ansible/playbook/base.py +++ b/lib/ansible/playbook/base.py @@ -152,6 +152,9 @@ class Base(with_metaclass(BaseMeta, object)): # variables _vars = FieldAttribute(isa='dict', priority=100, inherit=False) + # module default params + _module_defaults = FieldAttribute(isa='list', extend=True, prepend=True) + # flags and misc. settings _environment = FieldAttribute(isa='list', extend=True, prepend=True) _no_log = FieldAttribute(isa='bool') diff --git a/test/integration/targets/module_defaults/aliases b/test/integration/targets/module_defaults/aliases new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/integration/targets/module_defaults/tasks/main.yml b/test/integration/targets/module_defaults/tasks/main.yml new file mode 100644 index 00000000000..3ed960d3b50 --- /dev/null +++ b/test/integration/targets/module_defaults/tasks/main.yml @@ -0,0 +1,89 @@ +- name: main block + vars: + test_file: /tmp/ansible-test.module_defaults.foo + module_defaults: + debug: + msg: test default + file: + path: '{{ test_file }}' + block: + - debug: + register: foo + + - name: test that 'debug' task used default 'msg' param + assert: + that: foo.msg == "test default" + + - name: remove test file + file: + state: absent + + - name: touch test file + file: + state: touch + + - name: stat test file + stat: + path: '{{ test_file }}' + register: foo + + - name: check that test file exists + assert: + that: foo.stat.exists + + - name: remove test file + file: + state: absent + + - name: test that module defaults from parent are inherited and merged + module_defaults: + # Meaningless values to make sure that 'module_defaults' gets + # evaluated for this block + foo: + bar: baz + block: + - debug: + register: foo + + - assert: + that: foo.msg == "test default" + + - name: test that we can override module defaults inherited from parent + module_defaults: + debug: + msg: "different test message" + block: + - debug: + register: foo + + - assert: + that: foo.msg == "different test message" + + - name: test that module defaults inherited from parent can be removed + module_defaults: + debug: {} + block: + - debug: + register: foo + + - assert: + that: + foo.msg == "Hello world!" + + - name: test that module defaults can be overridden by module params + block: + - debug: + msg: another test message + register: foo + + - assert: + that: + foo.msg == "another test message" + + - debug: + msg: '{{ omit }}' + register: foo + + - assert: + that: + foo.msg == "Hello world!"