Add toggle to fix module_defaults with module-as-redirected-action on a per-module basis (#77265)

* If there is a platform specific handler, prefer the resolved module over the resolved action when loading module_defaults

Add a toggle for action plugins to prefer the resolved module when loading module_defaults

Allow moving away from modules intercepted as actions pattern

Fixes #77059
This commit is contained in:
Sloane Hertel
2022-05-24 13:38:46 -04:00
committed by GitHub
parent eecbaee7f4
commit 621e782ed0
15 changed files with 499 additions and 16 deletions
@@ -1,3 +1,49 @@
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
@@ -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
@@ -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
@@ -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
@@ -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()
@@ -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()
@@ -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()
@@ -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()
@@ -2,8 +2,13 @@
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 "$@"
@@ -110,3 +110,140 @@
- "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'