From 24781c01bebdc18185fe9428c85acca405bb0485 Mon Sep 17 00:00:00 2001 From: Sara-Touqan <55439434+Sara-Touqan@users.noreply.github.com> Date: Wed, 8 Jan 2020 18:51:37 +0200 Subject: [PATCH] Supporting AAA Configs in Ansible. (#64434) --- lib/ansible/modules/network/onyx/onyx_aaa.py | 161 ++++++++++++++++++ .../network/onyx/fixtures/onyx_show_aaa.cfg | 16 ++ .../modules/network/onyx/test_onyx_aaa.py | 74 ++++++++ 3 files changed, 251 insertions(+) create mode 100644 lib/ansible/modules/network/onyx/onyx_aaa.py create mode 100644 test/units/modules/network/onyx/fixtures/onyx_show_aaa.cfg create mode 100644 test/units/modules/network/onyx/test_onyx_aaa.py diff --git a/lib/ansible/modules/network/onyx/onyx_aaa.py b/lib/ansible/modules/network/onyx/onyx_aaa.py new file mode 100644 index 00000000000..8060e2aaa2d --- /dev/null +++ b/lib/ansible/modules/network/onyx/onyx_aaa.py @@ -0,0 +1,161 @@ +#!/usr/bin/python +# +# Copyright: 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 + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +--- +module: onyx_aaa +version_added: "2.10" +author: "Sara Touqan (@sarato)" +short_description: Configures AAA parameters +description: + - This module provides declarative management of AAA protocol params + on Mellanox ONYX network devices. +options: + tacacs_accounting_enabled: + description: + - Configures accounting settings. + type: bool + auth_default_user: + description: + - Sets local user default mapping. + type: str + choices: ['admin', 'monitor'] + auth_order: + description: + - Sets the order on how to handle remote to local user mappings. + type: str + choices: ['local-only', 'remote-first', 'remote-only'] + auth_fallback_enabled: + description: + - Enables/Disables fallback server-err option. + type: bool +""" + +EXAMPLES = """ +- name: configures aaa + onyx_aaa: + tacacs_accounting_enabled: yes + auth_default_user: monitor + auth_order: local-only + auth_fallback_enabled: false +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device. + returned: always + type: list + sample: + - aaa accounting changes default stop-only tacacs+ + - no aaa accounting changes default stop-only tacacs+ + - aaa authorization map default-user + - aaa authorization map order + - aaa authorization map fallback server-err + - no aaa authorization map fallback server-err +""" + +import re + +from ansible.module_utils.basic import AnsibleModule + +from ansible.module_utils.network.onyx.onyx import show_cmd +from ansible.module_utils.network.onyx.onyx import BaseOnyxModule + + +class OnyxAAAModule(BaseOnyxModule): + + def init_module(self): + """ initialize module + """ + element_spec = dict( + tacacs_accounting_enabled=dict(type='bool'), + auth_default_user=dict(type='str', choices=['admin', 'monitor']), + auth_order=dict(type='str', choices=['local-only', 'remote-first', 'remote-only']), + auth_fallback_enabled=dict(type='bool') + ) + argument_spec = dict() + argument_spec.update(element_spec) + self._module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True) + + def get_required_config(self): + module_params = self._module.params + self._required_config = dict(module_params) + self.validate_param_values(self._required_config) + + def _set_aaa_config(self, all_aaa_config): + aaa_config = all_aaa_config[0] + self._current_config['auth_default_user'] = aaa_config.get("Default User") + self._current_config['auth_order'] = aaa_config.get("Map Order") + auth_fallback_enabled = aaa_config.get("Fallback on server-err") + if auth_fallback_enabled == "yes": + self._current_config['auth_fallback_enabled'] = True + else: + self._current_config['auth_fallback_enabled'] = False + aaa_config_2 = all_aaa_config[2] + accounting_message = aaa_config_2.get("message") + if accounting_message == "No accounting methods configured.": + self._current_config['tacacs_accounting_enabled'] = False + else: + self._current_config['tacacs_accounting_enabled'] = True + + def _show_aaa_config(self): + cmd = "show aaa" + return show_cmd(self._module, cmd, json_fmt=True, fail_on_error=False) + + def load_current_config(self): + self._current_config = dict() + aaa_config = self._show_aaa_config() + if aaa_config: + self._set_aaa_config(aaa_config) + + def generate_commands(self): + tacacs_accounting_enabled = self._required_config.get("tacacs_accounting_enabled") + if tacacs_accounting_enabled is not None: + current_accounting_enabled = self._current_config.get("tacacs_accounting_enabled") + if current_accounting_enabled != tacacs_accounting_enabled: + if tacacs_accounting_enabled is True: + self._commands.append('aaa accounting changes default stop-only tacacs+') + else: + self._commands.append('no aaa accounting changes default stop-only tacacs+') + + auth_default_user = self._required_config.get("auth_default_user") + if auth_default_user is not None: + current_user = self._current_config.get("auth_default_user") + if current_user != auth_default_user: + self._commands.append('aaa authorization map default-user {0}' .format(auth_default_user)) + + auth_order = self._required_config.get("auth_order") + if auth_order is not None: + current_order = self._current_config.get("auth_order") + if current_order != auth_order: + self._commands.append('aaa authorization map order {0}' .format(auth_order)) + + auth_fallback_enabled = self._required_config.get("auth_fallback_enabled") + if auth_fallback_enabled is not None: + current_fallback = self._current_config.get("auth_fallback_enabled") + if current_fallback != auth_fallback_enabled: + if auth_fallback_enabled is True: + self._commands.append('aaa authorization map fallback server-err') + else: + self._commands.append('no aaa authorization map fallback server-err') + + +def main(): + """ main entry point for module execution + """ + OnyxAAAModule.main() + + +if __name__ == '__main__': + main() diff --git a/test/units/modules/network/onyx/fixtures/onyx_show_aaa.cfg b/test/units/modules/network/onyx/fixtures/onyx_show_aaa.cfg new file mode 100644 index 00000000000..40abbf21838 --- /dev/null +++ b/test/units/modules/network/onyx/fixtures/onyx_show_aaa.cfg @@ -0,0 +1,16 @@ +[ + { + "Default User": "admin", + "header": "AAA authorization", + "Fallback on server-err": "yes", + "Map Order": "remote-first" + }, + { + "header": "Authentication method(s)", + "message": "local" + }, + { + "header": "Accounting method(s)", + "message": "No accounting methods configured." + } +] diff --git a/test/units/modules/network/onyx/test_onyx_aaa.py b/test/units/modules/network/onyx/test_onyx_aaa.py new file mode 100644 index 00000000000..fa276620830 --- /dev/null +++ b/test/units/modules/network/onyx/test_onyx_aaa.py @@ -0,0 +1,74 @@ +# +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from units.compat.mock import patch +from ansible.modules.network.onyx import onyx_aaa +from units.modules.utils import set_module_args +from .onyx_module import TestOnyxModule, load_fixture + + +class TestOnyxAAAModule(TestOnyxModule): + + module = onyx_aaa + + def setUp(self): + self.enabled = False + super(TestOnyxAAAModule, self).setUp() + self.mock_get_config = patch.object( + onyx_aaa.OnyxAAAModule, "_show_aaa_config") + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch( + 'ansible.module_utils.network.onyx.onyx.load_config') + self.load_config = self.mock_load_config.start() + + def tearDown(self): + super(TestOnyxAAAModule, self).tearDown() + self.mock_get_config.stop() + self.mock_load_config.stop() + + def load_fixtures(self, commands=None, transport='cli'): + config_file = 'onyx_show_aaa.cfg' + self.get_config.return_value = load_fixture(config_file) + self.load_config.return_value = None + + def test_aaa_accounting_no_change(self): + set_module_args(dict(tacacs_accounting_enabled=False)) + self.execute_module(changed=False) + + def test_aaa_accounting_with_change(self): + set_module_args(dict(tacacs_accounting_enabled=True)) + commands = ['aaa accounting changes default stop-only tacacs+'] + self.execute_module(changed=True, commands=commands) + + def test_aaa_auth_default_user_no_change(self): + set_module_args(dict(auth_default_user='admin')) + self.execute_module(changed=False) + + def test_aaa_auth_default_user_with_change(self): + set_module_args(dict(auth_default_user='monitor')) + commands = ['aaa authorization map default-user monitor'] + self.execute_module(changed=True, commands=commands) + + def test_aaa_auth_order_no_change(self): + set_module_args(dict(auth_order='remote-first')) + self.execute_module(changed=False) + + def test_aaa_auth_order_with_change(self): + set_module_args(dict(auth_order='local-only')) + commands = ['aaa authorization map order local-only'] + self.execute_module(changed=True, commands=commands) + + def test_aaa_fallback_no_change(self): + set_module_args(dict(auth_fallback_enabled=True)) + self.execute_module(changed=False) + + def test_aaa_fallback_with_change(self): + set_module_args(dict(auth_fallback_enabled=False)) + commands = ['no aaa authorization map fallback server-err'] + self.execute_module(changed=True, commands=commands)