From 8a754e0858d922e955e71b253c139e071ecec432 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 28 Apr 2024 18:04:21 +0200 Subject: Adding upstream version 2.14.3. Signed-off-by: Daniel Baumann --- .../module_defaults/action_plugins/debug.py | 80 +++++++ test/integration/targets/module_defaults/aliases | 2 + .../othercoll/plugins/action/other_echoaction.py | 8 + .../othercoll/plugins/modules/other_echo1.py | 13 ++ .../testns/testcoll/meta/runtime.yml | 85 +++++++ .../testns/testcoll/plugins/action/echoaction.py | 19 ++ .../testns/testcoll/plugins/action/eos.py | 18 ++ .../testns/testcoll/plugins/action/ios.py | 18 ++ .../testns/testcoll/plugins/action/vyos.py | 18 ++ .../testcoll/plugins/module_utils/echo_impl.py | 15 ++ .../testns/testcoll/plugins/modules/echo1.py | 13 ++ .../testns/testcoll/plugins/modules/echo2.py | 13 ++ .../testns/testcoll/plugins/modules/eosfacts.py | 35 +++ .../testns/testcoll/plugins/modules/ios_facts.py | 35 +++ .../testns/testcoll/plugins/modules/metadata.py | 45 ++++ .../testns/testcoll/plugins/modules/module.py | 35 +++ .../testns/testcoll/plugins/modules/ping.py | 83 +++++++ .../testns/testcoll/plugins/modules/vyosfacts.py | 35 +++ .../targets/module_defaults/library/legacy_ping.py | 83 +++++++ .../library/test_module_defaults.py | 30 +++ test/integration/targets/module_defaults/runme.sh | 14 ++ .../targets/module_defaults/tasks/main.yml | 89 ++++++++ .../templates/test_metadata_warning.yml.j2 | 8 + .../module_defaults/test_action_group_metadata.yml | 123 ++++++++++ .../targets/module_defaults/test_action_groups.yml | 132 +++++++++++ .../targets/module_defaults/test_defaults.yml | 249 +++++++++++++++++++++ 26 files changed, 1298 insertions(+) create mode 100644 test/integration/targets/module_defaults/action_plugins/debug.py create mode 100644 test/integration/targets/module_defaults/aliases create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/eosfacts.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ios_facts.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/metadata.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/module.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ping.py create mode 100644 test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/vyosfacts.py create mode 100644 test/integration/targets/module_defaults/library/legacy_ping.py create mode 100644 test/integration/targets/module_defaults/library/test_module_defaults.py create mode 100755 test/integration/targets/module_defaults/runme.sh create mode 100644 test/integration/targets/module_defaults/tasks/main.yml create mode 100644 test/integration/targets/module_defaults/templates/test_metadata_warning.yml.j2 create mode 100644 test/integration/targets/module_defaults/test_action_group_metadata.yml create mode 100644 test/integration/targets/module_defaults/test_action_groups.yml create mode 100644 test/integration/targets/module_defaults/test_defaults.yml (limited to 'test/integration/targets/module_defaults') diff --git a/test/integration/targets/module_defaults/action_plugins/debug.py b/test/integration/targets/module_defaults/action_plugins/debug.py new file mode 100644 index 0000000..2584fd3 --- /dev/null +++ b/test/integration/targets/module_defaults/action_plugins/debug.py @@ -0,0 +1,80 @@ +# Copyright 2012, Dag Wieers +# Copyright 2016, Toshio Kuratomi +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.errors import AnsibleUndefinedVariable +from ansible.module_utils.six import string_types +from ansible.module_utils._text import to_text +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + ''' Print statements during execution ''' + + TRANSFERS_FILES = False + _VALID_ARGS = frozenset(('msg', 'var', 'verbosity')) + + def run(self, tmp=None, task_vars=None): + if task_vars is None: + task_vars = dict() + + if 'msg' in self._task.args and 'var' in self._task.args: + return {"failed": True, "msg": "'msg' and 'var' are incompatible options"} + + result = super(ActionModule, self).run(tmp, task_vars) + del tmp # tmp no longer has any effect + + # get task verbosity + verbosity = int(self._task.args.get('verbosity', 0)) + + if verbosity <= self._display.verbosity: + if 'msg' in self._task.args: + result['msg'] = self._task.args['msg'] + + elif 'var' in self._task.args: + try: + results = self._templar.template(self._task.args['var'], convert_bare=True, fail_on_undefined=True) + if results == self._task.args['var']: + # if results is not str/unicode type, raise an exception + if not isinstance(results, string_types): + raise AnsibleUndefinedVariable + # If var name is same as result, try to template it + results = self._templar.template("{{" + results + "}}", convert_bare=True, fail_on_undefined=True) + except AnsibleUndefinedVariable as e: + results = u"VARIABLE IS NOT DEFINED!" + if self._display.verbosity > 0: + results += u": %s" % to_text(e) + + if isinstance(self._task.args['var'], (list, dict)): + # If var is a list or dict, use the type as key to display + result[to_text(type(self._task.args['var']))] = results + else: + result[self._task.args['var']] = results + else: + result['msg'] = 'Hello world!' + + # force flag to make debug output module always verbose + result['_ansible_verbose_always'] = True + else: + result['skipped_reason'] = "Verbosity threshold not met." + result['skipped'] = True + + result['failed'] = False + + return result diff --git a/test/integration/targets/module_defaults/aliases b/test/integration/targets/module_defaults/aliases new file mode 100644 index 0000000..8278ec8 --- /dev/null +++ b/test/integration/targets/module_defaults/aliases @@ -0,0 +1,2 @@ +shippable/posix/group3 +context/controller diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py new file mode 100644 index 0000000..f7777b8 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/action/other_echoaction.py @@ -0,0 +1,8 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.action.echoaction import ActionModule as BaseAM + + +class ActionModule(BaseAM): + pass diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py new file mode 100644 index 0000000..771395f --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/othercoll/plugins/modules/other_echo1.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml new file mode 100644 index 0000000..a8c2c8c --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/meta/runtime.yml @@ -0,0 +1,85 @@ +plugin_routing: + action: + # Backwards compat for modules-redirected-as-actions: + # By default, each module_defaults entry is resolved as an action plugin, + # and if it does not exist, it is resolved a a module. + # All modules that redirect to the same action will resolve to the same action. + module_uses_action_defaults: + redirect: testns.testcoll.eos + + # module-redirected-as-action overridden by action_plugin + iosfacts: + redirect: testns.testcoll.nope + ios_facts: + redirect: testns.testcoll.nope + + redirected_action: + redirect: testns.testcoll.ios + modules: + # Any module_defaults for testns.testcoll.module will not apply to a module_uses_action_defaults task: + # + # module_defaults: + # testns.testcoll.module: + # option: value + # + # But defaults for testns.testcoll.module_uses_action_defaults or testns.testcoll.eos will: + # + # module_defaults: + # testns.testcoll.module_uses_action_defaults: + # option: value + # testns.testcoll.eos: + # option: defined_last_i_win + module_uses_action_defaults: + redirect: testns.testcoll.module + + # Not "eos_facts" to ensure TE is not finding handler via prefix + # eosfacts tasks should not get eos module_defaults (or defaults for other modules that use eos action plugin) + eosfacts: + action_plugin: testns.testcoll.eos + + # Test that `action_plugin` has higher precedence than module-redirected-as-action - reverse this? + # Current behavior is iosfacts/ios_facts do not get ios defaults. + iosfacts: + redirect: testns.testcoll.ios_facts + ios_facts: + action_plugin: testns.testcoll.redirected_action + +action_groups: + testgroup: + # Test metadata 'extend_group' feature does not get stuck in a recursive loop + - metadata: + extend_group: othergroup + - metadata + - ping + - testns.testcoll.echo1 + - testns.testcoll.echo2 +# note we can define defaults for an action + - testns.testcoll.echoaction +# note we can define defaults in this group for actions/modules in another collection + - testns.othercoll.other_echoaction + - testns.othercoll.other_echo1 + othergroup: + - metadata: + extend_group: + - testgroup + empty_metadata: + - metadata: {} + bad_metadata_format: + - unexpected_key: + key: value + metadata: + extend_group: testgroup + multiple_metadata: + - metadata: + extend_group: testgroup + - metadata: + extend_group: othergroup + bad_metadata_options: + - metadata: + unexpected_key: testgroup + bad_metadata_type: + - metadata: [testgroup] + bad_metadata_option_type: + - metadata: + extend_group: + name: testgroup diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py new file mode 100644 index 0000000..2fa097b --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/echoaction.py @@ -0,0 +1,19 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action import ActionBase + + +class ActionModule(ActionBase): + TRANSFERS_FILES = False + _VALID_ARGS = frozenset() + + def run(self, tmp=None, task_vars=None): + if task_vars is None: + task_vars = dict() + + result = super(ActionModule, self).run(None, task_vars) + + result = dict(changed=False, args_in=self._task.args) + + return result diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py new file mode 100644 index 0000000..0d39f26 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/eos.py @@ -0,0 +1,18 @@ +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action.normal import ActionModule as ActionBase +from ansible.utils.vars import merge_hash + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + + result = super(ActionModule, self).run(tmp, task_vars) + result['action_plugin'] = 'eos' + + return result diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py new file mode 100644 index 0000000..20284fd --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/ios.py @@ -0,0 +1,18 @@ +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action.normal import ActionModule as ActionBase +from ansible.utils.vars import merge_hash + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + + result = super(ActionModule, self).run(tmp, task_vars) + result['action_plugin'] = 'ios' + + return result diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py new file mode 100644 index 0000000..b0e1904 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py @@ -0,0 +1,18 @@ +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action.normal import ActionModule as ActionBase +from ansible.utils.vars import merge_hash + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + + result = super(ActionModule, self).run(tmp, task_vars) + result['action_plugin'] = 'vyos' + + return result diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py new file mode 100644 index 0000000..f5c5d73 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/module_utils/echo_impl.py @@ -0,0 +1,15 @@ +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json +from ansible.module_utils import basic +from ansible.module_utils.basic import _load_params, AnsibleModule + + +def do_echo(): + p = _load_params() + d = json.loads(basic._ANSIBLE_ARGS) + d['ANSIBLE_MODULE_ARGS'] = {} + basic._ANSIBLE_ARGS = json.dumps(d).encode('utf-8') + module = AnsibleModule(argument_spec={}) + module.exit_json(args_in=p) diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py new file mode 100644 index 0000000..771395f --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo1.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py new file mode 100644 index 0000000..771395f --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/echo2.py @@ -0,0 +1,13 @@ +#!/usr/bin/python +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible_collections.testns.testcoll.plugins.module_utils.echo_impl import do_echo + + +def main(): + do_echo() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/eosfacts.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/eosfacts.py new file mode 100644 index 0000000..8c73fe1 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/eosfacts.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: eosfacts +short_description: module to test module_defaults +description: module to test module_defaults +version_added: '2.13' +''' + +EXAMPLES = r''' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + eosfacts=dict(type=bool), + ), + supports_check_mode=True + ) + module.exit_json(eosfacts=module.params['eosfacts']) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ios_facts.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ios_facts.py new file mode 100644 index 0000000..e2ed598 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ios_facts.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: ios_facts +short_description: module to test module_defaults +description: module to test module_defaults +version_added: '2.13' +''' + +EXAMPLES = r''' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + ios_facts=dict(type=bool), + ), + supports_check_mode=True + ) + module.exit_json(ios_facts=module.params['ios_facts']) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/metadata.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/metadata.py new file mode 100644 index 0000000..6a818fd --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/metadata.py @@ -0,0 +1,45 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: metadata +version_added: 2.12 +short_description: Test module with a specific name +description: Test module with a specific name +options: + data: + description: Required option to test module_defaults work + required: True + type: str +author: + - Ansible Core Team +''' + +EXAMPLES = ''' +''' + +RETURN = ''' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + data=dict(type='str', required=True), + ), + ) + + module.exit_json() + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/module.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/module.py new file mode 100644 index 0000000..b98a5f9 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/module.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: module +short_description: module to test module_defaults +description: module to test module_defaults +version_added: '2.13' +''' + +EXAMPLES = r''' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + action_option=dict(type=bool), + ), + supports_check_mode=True + ) + module.exit_json(action_option=module.params['action_option']) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ping.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ping.py new file mode 100644 index 0000000..2cb1fb2 --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/ping.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2012, Michael DeHaan +# (c) 2016, Toshio Kuratomi +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ping +version_added: historical +short_description: Try to connect to host, verify a usable python and return C(pong) on success +description: + - A trivial test module, this module always returns C(pong) on successful + contact. It does not make sense in playbooks, but it is useful from + C(/usr/bin/ansible) to verify the ability to login and that a usable Python is configured. + - This is NOT ICMP ping, this is just a trivial test module that requires Python on the remote-node. + - For Windows targets, use the M(ansible.windows.win_ping) module instead. + - For Network targets, use the M(ansible.netcommon.net_ping) module instead. +options: + data: + description: + - Data to return for the C(ping) return value. + - If this parameter is set to C(crash), the module will cause an exception. + type: str + default: pong +seealso: + - module: ansible.netcommon.net_ping + - module: ansible.windows.win_ping +author: + - Ansible Core Team + - Michael DeHaan +notes: + - Supports C(check_mode). +''' + +EXAMPLES = ''' +# Test we can logon to 'webservers' and execute python with json lib. +# ansible webservers -m ping + +- name: Example from an Ansible Playbook + ansible.builtin.ping: + +- name: Induce an exception to see what happens + ansible.builtin.ping: + data: crash +''' + +RETURN = ''' +ping: + description: Value provided with the data parameter. + returned: success + type: str + sample: pong +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + data=dict(type='str', default='pong'), + ), + supports_check_mode=True + ) + + if module.params['data'] == 'crash': + raise Exception("boom") + + result = dict( + ping=module.params['data'], + ) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/vyosfacts.py b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/vyosfacts.py new file mode 100644 index 0000000..3a9abbc --- /dev/null +++ b/test/integration/targets/module_defaults/collections/ansible_collections/testns/testcoll/plugins/modules/vyosfacts.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +# Copyright: (c) 2022, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: vyosfacts +short_description: module to test module_defaults +description: module to test module_defaults +version_added: '2.13' +''' + +EXAMPLES = r''' +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + vyosfacts=dict(type=bool), + ), + supports_check_mode=True + ) + module.exit_json(vyosfacts=module.params['vyosfacts']) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/library/legacy_ping.py b/test/integration/targets/module_defaults/library/legacy_ping.py new file mode 100644 index 0000000..2cb1fb2 --- /dev/null +++ b/test/integration/targets/module_defaults/library/legacy_ping.py @@ -0,0 +1,83 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2012, Michael DeHaan +# (c) 2016, Toshio Kuratomi +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: ping +version_added: historical +short_description: Try to connect to host, verify a usable python and return C(pong) on success +description: + - A trivial test module, this module always returns C(pong) on successful + contact. It does not make sense in playbooks, but it is useful from + C(/usr/bin/ansible) to verify the ability to login and that a usable Python is configured. + - This is NOT ICMP ping, this is just a trivial test module that requires Python on the remote-node. + - For Windows targets, use the M(ansible.windows.win_ping) module instead. + - For Network targets, use the M(ansible.netcommon.net_ping) module instead. +options: + data: + description: + - Data to return for the C(ping) return value. + - If this parameter is set to C(crash), the module will cause an exception. + type: str + default: pong +seealso: + - module: ansible.netcommon.net_ping + - module: ansible.windows.win_ping +author: + - Ansible Core Team + - Michael DeHaan +notes: + - Supports C(check_mode). +''' + +EXAMPLES = ''' +# Test we can logon to 'webservers' and execute python with json lib. +# ansible webservers -m ping + +- name: Example from an Ansible Playbook + ansible.builtin.ping: + +- name: Induce an exception to see what happens + ansible.builtin.ping: + data: crash +''' + +RETURN = ''' +ping: + description: Value provided with the data parameter. + returned: success + type: str + sample: pong +''' + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + data=dict(type='str', default='pong'), + ), + supports_check_mode=True + ) + + if module.params['data'] == 'crash': + raise Exception("boom") + + result = dict( + ping=module.params['data'], + ) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/library/test_module_defaults.py b/test/integration/targets/module_defaults/library/test_module_defaults.py new file mode 100644 index 0000000..ede8c99 --- /dev/null +++ b/test/integration/targets/module_defaults/library/test_module_defaults.py @@ -0,0 +1,30 @@ +#!/usr/bin/python +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible.module_utils.basic import AnsibleModule + + +def main(): + module = AnsibleModule( + argument_spec=dict( + arg1=dict(type='str', default='default1'), + arg2=dict(type='str', default='default2'), + arg3=dict(type='str', default='default3'), + ), + supports_check_mode=True + ) + + result = dict( + test_module_defaults=dict( + arg1=module.params['arg1'], + arg2=module.params['arg2'], + arg3=module.params['arg3'], + ), + ) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/module_defaults/runme.sh b/test/integration/targets/module_defaults/runme.sh new file mode 100755 index 0000000..fe9c40c --- /dev/null +++ b/test/integration/targets/module_defaults/runme.sh @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +set -eux + +# Symlink is test for backwards-compat (only workaround for https://github.com/ansible/ansible/issues/77059) +sudo ln -s "${PWD}/collections/ansible_collections/testns/testcoll/plugins/action/vyos.py" ./collections/ansible_collections/testns/testcoll/plugins/action/vyosfacts.py + +ansible-playbook test_defaults.yml "$@" + +sudo rm ./collections/ansible_collections/testns/testcoll/plugins/action/vyosfacts.py + +ansible-playbook test_action_groups.yml "$@" + +ansible-playbook test_action_group_metadata.yml "$@" 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 0000000..747c2f9 --- /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 + ping: + 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!" diff --git a/test/integration/targets/module_defaults/templates/test_metadata_warning.yml.j2 b/test/integration/targets/module_defaults/templates/test_metadata_warning.yml.j2 new file mode 100644 index 0000000..b45aaba --- /dev/null +++ b/test/integration/targets/module_defaults/templates/test_metadata_warning.yml.j2 @@ -0,0 +1,8 @@ +--- +- hosts: localhost + gather_facts: no + module_defaults: + group/{{ group_name }}: + data: value + tasks: + - ping: diff --git a/test/integration/targets/module_defaults/test_action_group_metadata.yml b/test/integration/targets/module_defaults/test_action_group_metadata.yml new file mode 100644 index 0000000..e2555b1 --- /dev/null +++ b/test/integration/targets/module_defaults/test_action_group_metadata.yml @@ -0,0 +1,123 @@ +--- +- hosts: localhost + gather_facts: no + environment: + ANSIBLE_NOCOLOR: True + ANSIBLE_FORCE_COLOR: False + tasks: + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.empty_metadata + + - command: ansible-playbook test_metadata_warning.yml + register: result + + - assert: + that: metadata_warning not in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: "Invalid metadata was found" + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.bad_metadata_format + + - command: ansible-playbook test_metadata_warning.yml + register: result + + - assert: + that: metadata_warning in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: >- + Invalid metadata was found for action_group testns.testcoll.bad_metadata_format while loading module_defaults. + The only expected key is metadata, but got keys: metadata, unexpected_key + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.multiple_metadata + + - command: ansible-playbook test_metadata_warning.yml + register: result + + - assert: + that: metadata_warning in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: >- + Invalid metadata was found for action_group testns.testcoll.multiple_metadata while loading module_defaults. + The group contains multiple metadata entries. + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.bad_metadata_options + + - command: 'ansible-playbook test_metadata_warning.yml' + register: result + + - assert: + that: metadata_warning in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: >- + Invalid metadata was found for action_group testns.testcoll.bad_metadata_options while loading module_defaults. + The metadata contains unexpected keys: unexpected_key + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.bad_metadata_type + + - command: ansible-playbook test_metadata_warning.yml + register: result + + - assert: + that: metadata_warning in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: >- + Invalid metadata was found for action_group testns.testcoll.bad_metadata_type while loading module_defaults. + The metadata is not a dictionary. Got ['testgroup'] + + - template: + src: test_metadata_warning.yml.j2 + dest: test_metadata_warning.yml + vars: + group_name: testns.testcoll.bad_metadata_option_type + + - command: ansible-playbook test_metadata_warning.yml + register: result + + - assert: + that: metadata_warning in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: >- + Invalid metadata was found for action_group testns.testcoll.bad_metadata_option_type while loading module_defaults. + The metadata contains unexpected key types: extend_group is {'name': 'testgroup'} (expected type list) + + - name: test disabling action_group metadata validation + command: ansible-playbook test_metadata_warning.yml + environment: + ANSIBLE_VALIDATE_ACTION_GROUP_METADATA: False + register: result + + - assert: + that: metadata_warning not in warnings + vars: + warnings: "{{ result.stderr | regex_replace('\\n', ' ') }}" + metadata_warning: "Invalid metadata was found for action_group" + + - file: + path: test_metadata_warning.yml + state: absent diff --git a/test/integration/targets/module_defaults/test_action_groups.yml b/test/integration/targets/module_defaults/test_action_groups.yml new file mode 100644 index 0000000..33a3c9c --- /dev/null +++ b/test/integration/targets/module_defaults/test_action_groups.yml @@ -0,0 +1,132 @@ +--- +- hosts: localhost + gather_facts: no + tasks: + - name: test ansible.legacy short group name + module_defaults: + group/testgroup: + data: test + block: + - legacy_ping: + register: result + - assert: + that: "result.ping == 'pong'" + + - ansible.legacy.legacy_ping: + register: result + - assert: + that: "result.ping == 'pong'" + + - ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.legacy.ping: # resolves to ansible.builtin.ping + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.builtin.ping: + register: result + - assert: + that: "result.ping == 'test'" + + - formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.builtin.formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - name: test group that includes a legacy action + module_defaults: + # As of 2.12, legacy actions must be included in the action group definition + group/testlegacy: + data: test + block: + - legacy_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.legacy.legacy_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - name: test ansible.builtin fully qualified group name + module_defaults: + group/ansible.builtin.testgroup: + data: test + block: + # ansible.builtin does not contain ansible.legacy + - legacy_ping: + register: result + - assert: + that: "result.ping != 'test'" + + # ansible.builtin does not contain ansible.legacy + - ansible.legacy.legacy_ping: + register: result + - assert: + that: "result.ping != 'test'" + + - ping: + register: result + - assert: + that: "result.ping == 'test'" + + # Resolves to ansible.builtin.ping + - ansible.legacy.ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.builtin.ping: + register: result + - assert: + that: "result.ping == 'test'" + + - formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.builtin.formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - name: test collection group name + module_defaults: + group/testns.testcoll.testgroup: + data: test + block: + # Plugin resolving to a different collection does not get the default + - ping: + register: result + - assert: + that: "result.ping != 'test'" + + - formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - ansible.builtin.formerly_core_ping: + register: result + - assert: + that: "result.ping == 'test'" + + - testns.testcoll.ping: + register: result + - assert: + that: "result.ping == 'test'" + + - metadata: + collections: + - testns.testcoll diff --git a/test/integration/targets/module_defaults/test_defaults.yml b/test/integration/targets/module_defaults/test_defaults.yml new file mode 100644 index 0000000..6206d3a --- /dev/null +++ b/test/integration/targets/module_defaults/test_defaults.yml @@ -0,0 +1,249 @@ +- hosts: localhost + gather_facts: no + collections: + - testns.testcoll + - testns.othercoll + module_defaults: + testns.testcoll.echoaction: + explicit_module_default: from playbook + testns.testcoll.echo1: + explicit_module_default: from playbook + group/testgroup: + group_module_default: from playbook + tasks: + - testns.testcoll.echoaction: + task_arg: from task + register: echoaction_fq + - echoaction: + task_arg: from task + register: echoaction_unq + - testns.testcoll.echo1: + task_arg: from task + register: echo1_fq + - echo1: + task_arg: from task + register: echo1_unq + - testns.testcoll.echo2: + task_arg: from task + register: echo2_fq + - echo2: + task_arg: from task + register: echo2_unq + - testns.othercoll.other_echoaction: + task_arg: from task + register: other_echoaction_fq + - other_echoaction: + task_arg: from task + register: other_echoaction_unq + - testns.othercoll.other_echo1: + task_arg: from task + register: other_echo1_fq + - other_echo1: + task_arg: from task + register: other_echo1_unq + + - debug: var=echo1_fq + + - legacy_ping: + register: legacy_ping_1 + module_defaults: + legacy_ping: + data: from task + + - legacy_ping: + register: legacy_ping_2 + module_defaults: + ansible.legacy.legacy_ping: + data: from task + + - ansible.legacy.legacy_ping: + register: legacy_ping_3 + module_defaults: + legacy_ping: + data: from task + + - ansible.legacy.legacy_ping: + register: legacy_ping_4 + module_defaults: + ansible.legacy.legacy_ping: + data: from task + + - name: builtin uses legacy defaults + ansible.builtin.debug: + module_defaults: + debug: + msg: legacy default + register: builtin_legacy_defaults_1 + + - name: builtin uses legacy defaults + ansible.builtin.debug: + module_defaults: + ansible.legacy.debug: + msg: legacy default + register: builtin_legacy_defaults_2 + + - name: legacy does not use builtin defaults + ansible.legacy.debug: + register: legacy_builtin_defaults + module_defaults: + ansible.builtin.debug: + msg: legacy default + + - assert: + that: + - "echoaction_fq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echoaction_unq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo1_fq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo1_unq.args_in == {'task_arg': 'from task', 'explicit_module_default': 'from playbook', 'group_module_default': 'from playbook' }" + - "echo2_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "echo2_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echoaction_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echoaction_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echo1_fq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "other_echo1_unq.args_in == {'task_arg': 'from task', 'group_module_default': 'from playbook' }" + - "legacy_ping_1.ping == 'from task'" + - "legacy_ping_2.ping == 'from task'" + - "legacy_ping_3.ping == 'from task'" + - "legacy_ping_4.ping == 'from task'" + - "legacy_builtin_defaults.msg == 'Hello world!'" + - "builtin_legacy_defaults_1.msg == 'legacy default'" + - "builtin_legacy_defaults_2.msg == 'legacy default'" + + - include_tasks: tasks/main.yml + +- name: test preferring module name defaults for platform-specific actions + hosts: localhost + gather_facts: no + tasks: + - name: ensure eosfacts does not use action plugin default + testns.testcoll.eosfacts: + module_defaults: + testns.testcoll.eos: + fail: true + + - name: eosfacts does use module name defaults + testns.testcoll.eosfacts: + module_defaults: + testns.testcoll.eosfacts: + eosfacts: true + register: result + + - assert: + that: + - result.eosfacts + - result.action_plugin == 'eos' + + - name: ensure vyosfacts does not use action plugin default + testns.testcoll.vyosfacts: + module_defaults: + testns.testcoll.vyos: + fail: true + + - name: vyosfacts does use vyosfacts defaults + testns.testcoll.vyosfacts: + module_defaults: + testns.testcoll.vyosfacts: + vyosfacts: true + register: result + + - assert: + that: + - result.vyosfacts + - result.action_plugin == 'vyos' + + - name: iosfacts/ios_facts does not use action plugin default (module action_plugin field has precedence over module-as-action-redirect) + collections: + - testns.testcoll + module_defaults: + testns.testcoll.ios: + fail: true + block: + - ios_facts: + register: result + - assert: + that: + - result.action_plugin == 'ios' + + - iosfacts: + register: result + - assert: + that: + - result.action_plugin == 'ios' + + - name: ensure iosfacts/ios_facts uses ios_facts defaults + collections: + - testns.testcoll + module_defaults: + testns.testcoll.ios_facts: + ios_facts: true + block: + - ios_facts: + register: result + - assert: + that: + - result.ios_facts + - result.action_plugin == 'ios' + + - iosfacts: + register: result + - assert: + that: + - result.ios_facts + - result.action_plugin == 'ios' + + - name: ensure iosfacts/ios_facts uses iosfacts defaults + collections: + - testns.testcoll + module_defaults: + testns.testcoll.iosfacts: + ios_facts: true + block: + - ios_facts: + register: result + - assert: + that: + - result.ios_facts + - result.action_plugin == 'ios' + + - iosfacts: + register: result + - assert: + that: + - result.ios_facts + - result.action_plugin == 'ios' + + - name: ensure redirected action gets redirected action defaults + testns.testcoll.module_uses_action_defaults: + module_defaults: + testns.testcoll.module_uses_action_defaults: + action_option: true + register: result + + - assert: + that: + - result.action_option + - result.action_plugin == 'eos' + + - name: ensure redirected action gets resolved action defaults + testns.testcoll.module_uses_action_defaults: + module_defaults: + testns.testcoll.eos: + action_option: true + register: result + + - assert: + that: + - result.action_option + - result.action_plugin == 'eos' + + - name: ensure redirected action does not use module-specific defaults + testns.testcoll.module_uses_action_defaults: + module_defaults: + testns.testcoll.module: + fail: true + register: result + + - assert: + that: + - not result.action_option + - result.action_plugin == 'eos' -- cgit v1.2.3