From e58c912a04fe7bf10c77db054c05e3f3f5c00b40 Mon Sep 17 00:00:00 2001 From: shade Date: Thu, 16 May 2024 14:12:47 -0700 Subject: [PATCH 01/12] Update AAA module authentication implementation --- playbooks/common_examples/sonic_aaa.yaml | 5 +- .../network/sonic/argspec/aaa/aaa.py | 10 +-- .../network/sonic/config/aaa/aaa.py | 59 +++++----------- .../network/sonic/facts/aaa/aaa.py | 9 +-- plugins/modules/sonic_aaa.py | 69 ++++++++++--------- .../roles/sonic_aaa/defaults/main.yml | 50 ++++++++------ .../regression/roles/sonic_aaa/tasks/main.yml | 7 +- .../network/sonic/fixtures/sonic_aaa.yaml | 11 +-- 8 files changed, 98 insertions(+), 122 deletions(-) diff --git a/playbooks/common_examples/sonic_aaa.yaml b/playbooks/common_examples/sonic_aaa.yaml index 7d968b83c..cb147a353 100644 --- a/playbooks/common_examples/sonic_aaa.yaml +++ b/playbooks/common_examples/sonic_aaa.yaml @@ -40,8 +40,9 @@ authentication: data: fail_through: true - group: tacacs+ - local: true + default_auth: + - tacacs+ + - local state: merged - name: Merge tacacs configurations sonic_tacacs_server: diff --git a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py index a61b7307b..31cc88491 100644 --- a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -45,11 +45,11 @@ def __init__(self, **kwargs): 'data': { 'options': { 'fail_through': {'type': 'bool'}, - 'group': { - 'choices': ['ldap', 'radius', 'tacacs+'], - 'type': 'str' + 'default_auth': { + 'choices': ['local', 'ldap', 'radius', 'tacacs+'], + 'type': 'list', + 'elements': 'str' }, - 'local': {'type': 'bool'} }, 'type': 'dict' } diff --git a/plugins/module_utils/network/sonic/config/aaa/aaa.py b/plugins/module_utils/network/sonic/config/aaa/aaa.py index 036567f0a..98377b077 100644 --- a/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -247,16 +247,10 @@ def get_create_aaa_request(self, commands): def build_create_aaa_payload(self, commands): payload = {} - auth_method_list = [] if "authentication" in commands and commands["authentication"]: payload = {"openconfig-system:aaa": {"authentication": {"config": {}}}} - if "local" in commands["authentication"]["data"] and commands["authentication"]["data"]["local"]: - auth_method_list.append('local') - if "group" in commands["authentication"]["data"] and commands["authentication"]["data"]["group"]: - auth_method = commands["authentication"]["data"]["group"] - auth_method_list.append(auth_method) - if auth_method_list: - cfg = {'authentication-method': auth_method_list} + if "default_auth" in commands["authentication"]["data"] and commands["authentication"]["data"]["default_auth"]: + cfg = {'authentication-method': commands["authentication"]["data"]["default_auth"]} payload['openconfig-system:aaa']['authentication']['config'].update(cfg) if "fail_through" in commands["authentication"]["data"]: cfg = {'failthrough': str(commands["authentication"]["data"]["fail_through"])} @@ -269,12 +263,9 @@ def remove_default_entries(self, data): return new_data else: new_data = {'authentication': {'data': {}}} - local = data['authentication']['data'].get('local', None) - if local is not None: - new_data["authentication"]["data"]["local"] = local - group = data['authentication']['data'].get('group', None) - if group is not None: - new_data["authentication"]["data"]["group"] = group + default_auth = data['authentication']['data'].get('default_auth', None) + if default_auth is not None: + new_data["authentication"]["data"]["default_auth"] = default_auth fail_through = data['authentication']['data'].get('fail_through', None) if fail_through is not None: new_data["authentication"]["data"]["fail_through"] = fail_through @@ -283,7 +274,7 @@ def remove_default_entries(self, data): def get_delete_all_aaa_request(self, have): requests = [] if "authentication" in have and have["authentication"]: - if "local" in have["authentication"]["data"] or "group" in have["authentication"]["data"]: + if "default_auth" in have["authentication"]["data"]: request = self.get_authentication_method_delete_request() requests.append(request) if "fail_through" in have["authentication"]["data"]: @@ -303,9 +294,7 @@ def get_failthrough_delete_request(self): request = {'path': path, 'method': method} return request - # Current SONiC code behavior for patch overwrites the OC authentication-method leaf-list - # This function serves as a workaround for the issue, allowing the user to append to the - # OC authentication-method leaf-list. + # Diff of default_auth needs to be compared as a whole list def get_diff_aaa(self, want, have): diff_cfg = {} diff_authentication = {} @@ -316,39 +305,23 @@ def get_diff_aaa(self, want, have): data = authentication.get('data', None) if data: fail_through = data.get('fail_through', None) - local = data.get('local', None) - group = data.get('group', None) + default_auth = data.get('default_auth', None) cfg_authentication = have.get('authentication', None) if cfg_authentication: cfg_data = cfg_authentication.get('data', None) if cfg_data: cfg_fail_through = cfg_data.get('fail_through', None) - cfg_local = cfg_data.get('local', None) - cfg_group = cfg_data.get('group', None) + cfg_default_auth = cfg_data.get('default_auth', None) if fail_through is not None and fail_through != cfg_fail_through: diff_data['fail_through'] = fail_through - if local and local != cfg_local: - diff_data['local'] = local - if group and group != cfg_group: - diff_data['group'] = group - - diff_local = diff_data.get('local', None) - diff_group = diff_data.get('group', None) - if diff_local and not diff_group and cfg_group: - diff_data['group'] = cfg_group - if diff_group and not diff_local and cfg_local: - diff_data['local'] = cfg_local + if default_auth != cfg_default_auth: + diff_data['default_auth'] = default_auth + if diff_data: + diff_authentication['data'] = diff_data + diff_cfg['authentication'] = diff_authentication else: - if fail_through is not None: - diff_data['fail_through'] = fail_through - if local: - diff_data['local'] = local - if group: - diff_data['group'] = group - if diff_data: - diff_authentication['data'] = diff_data - diff_cfg['authentication'] = diff_authentication + diff_cfg = want return diff_cfg diff --git a/plugins/module_utils/network/sonic/facts/aaa/aaa.py b/plugins/module_utils/network/sonic/facts/aaa/aaa.py index 541a5805e..9c380a5ab 100644 --- a/plugins/module_utils/network/sonic/facts/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/facts/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -97,12 +97,7 @@ def parse_sonic_aaa(self, spec, conf): if conf: temp = {} if ('authentication-method' in conf) and (conf['authentication-method']): - if 'local' in conf['authentication-method']: - temp['local'] = True - choices = ['tacacs+', 'ldap', 'radius'] - for i, word in enumerate(conf['authentication-method']): - if word in choices: - temp['group'] = conf['authentication-method'][i] + temp['default_auth'] = conf['authentication-method'] if ('failthrough' in conf): temp['fail_through'] = conf['failthrough'] if temp: diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index c17c0f711..164250974 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# Copyright 2023 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -61,15 +61,13 @@ description: - Specifies the state of failthrough type: bool - local: + default_auth: description: - - Enable or Disable local authentication - type: bool - group: - description: - - Specifies the method of aaa authentication - type: str + - Specifies order to authenticate aaa login methods + type: list + elements: str choices: + - local - ldap - radius - tacacs+ @@ -102,7 +100,8 @@ config: authentication: data: - local: True + default_auth: + - local state: deleted # After state: @@ -157,7 +156,9 @@ config: authentication: data: - local: true + default_auth: + - local + - tacacs+ fail_through: true state: merged @@ -168,10 +169,10 @@ # AAA Authentication Information # --------------------------------------------------------- # failthrough : True -# login-method : local +# login-method : local, tacacs+ -# Using replaced +# Using overridden # # Before state: # ------------- @@ -179,17 +180,16 @@ # do show aaa # AAA Authentication Information # --------------------------------------------------------- -# failthrough : False -# login-method : local, radius +# failthrough : True +# login-method : local, ldap -- name: Replace aaa configurations +- name: Override aaa configurations dellemc.enterprise_sonic.sonic_aaa: config: authentication: data: - group: ldap - fail_through: true - state: replaced + fail_through: False + state: overridden # After state: # ------------ @@ -197,11 +197,11 @@ # do show aaa # AAA Authentication Information # --------------------------------------------------------- -# failthrough : True -# login-method : local, ldap +# failthrough : False +# login-method : -# Using overridden +# Using replaced # # Before state: # ------------- @@ -210,16 +210,16 @@ # AAA Authentication Information # --------------------------------------------------------- # failthrough : False -# login-method : local, radius +# login-method : ldap, radius -- name: Override aaa configurations +- name: Replace aaa configurations dellemc.enterprise_sonic.sonic_aaa: config: authentication: data: - group: tacacs+ - fail_through: true - state: overridden + default_auth: + - local + state: replaced # After state: # ------------ @@ -227,32 +227,33 @@ # do show aaa # AAA Authentication Information # --------------------------------------------------------- -# failthrough : True -# login-method : tacacs+ +# failthrough : False +# login-method : local + """ RETURN = """ before: - description: The configuration prior to the model invocation. + description: The configuration prior to the module invocation. returned: always type: list sample: > The configuration returned will always be in the same format - of the parameters above. + as the parameters above. after: - description: The resulting configuration model invocation. + description: The resulting configuration module invocation. returned: when changed type: list sample: > The configuration returned will always be in the same format - of the parameters above. + as the parameters above. after(generated): - description: The generated configuration model invocation. + description: The generated configuration module invocation. returned: when C(check_mode) type: list sample: > The configuration returned will always be in the same format - of the parameters above. + as the parameters above. commands: description: The set of commands pushed to the remote device. returned: always diff --git a/tests/regression/roles/sonic_aaa/defaults/main.yml b/tests/regression/roles/sonic_aaa/defaults/main.yml index e51a026cd..b3dbc612d 100644 --- a/tests/regression/roles/sonic_aaa/defaults/main.yml +++ b/tests/regression/roles/sonic_aaa/defaults/main.yml @@ -3,14 +3,15 @@ ansible_connection: httpapi module_name: aaa tests: - name: test_case_01 - description: aaa properties + description: Create aaa properties state: merged input: authentication: data: fail_through: true - group: tacacs+ - local: true + default_auth: + - tacacs+ + - local - name: test_case_02 description: Update created aaa properties @@ -19,52 +20,59 @@ tests: authentication: data: fail_through: false + default_auth: + - local + - radius - name: test_case_03 - description: Update aaa properties - change group - state: merged + description: Replace aaa properties + state: replaced input: authentication: data: fail_through: true - group: radius + default_auth: + - local + - ldap - name: test_case_04 - description: Replace aaa properties - state: replaced + description: Delete aaa properties + state: deleted input: authentication: data: - fail_through: false - group: ldap + default_auth: + - local + - ldap - name: test_case_05 - description: Override aaa properties - state: overridden + description: Update aaa properties + state: merged input: authentication: data: fail_through: true - group: radius - local: true + default_auth: + - local + - radius - name: test_case_06 - description: Delete aaa properties - state: deleted + description: Override aaa properties + state: overridden input: authentication: data: - group: radius + fail_through: False - name: test_case_07 - description: aaa properties - state: merged + description: Override aaa properties + state: overridden input: authentication: data: fail_through: true - group: radius - local: true + default_auth: + - local test_delete_all: - name: del_all_test_case_01 diff --git a/tests/regression/roles/sonic_aaa/tasks/main.yml b/tests/regression/roles/sonic_aaa/tasks/main.yml index fdcaa9a43..09325ea7d 100644 --- a/tests/regression/roles/sonic_aaa/tasks/main.yml +++ b/tests/regression/roles/sonic_aaa/tasks/main.yml @@ -1,6 +1,6 @@ - debug: msg="sonic_aaa Test started ..." -- name: Preparations test +- name: Preparation tests include_tasks: preparation_tests.yaml - name: "Test {{ module_name }} started ..." @@ -10,8 +10,3 @@ - name: "test_delete_all {{ module_name }} stated ..." include_tasks: tasks_template_del.yaml loop: "{{ test_delete_all }}" - when: test_delete_all is defined - -- name: Display all variables/facts known for a host - debug: - var: hostvars[inventory_hostname].ansible_facts.test_reports diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml index e7ca4429a..8ddf6fb6c 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml @@ -4,9 +4,10 @@ merged_01: config: authentication: data: - local: true fail_through: true - group: radius + default_auth: + - local + - radius existing_aaa_config: - path: "data/openconfig-system:system/aaa" response: @@ -48,14 +49,16 @@ deleted_01: - path: "data/openconfig-system:system/aaa/authentication/config/failthrough" method: "delete" data: + deleted_02: module_args: config: authentication: data: - local: true fail_through: true - group: radius + default_auth: + - local + - radius state: deleted existing_aaa_config: - path: "data/openconfig-system:system/aaa" From 3dcedf667eac0c441adeb00dbd107bd880bc9d89 Mon Sep 17 00:00:00 2001 From: shade Date: Thu, 16 May 2024 14:24:22 -0700 Subject: [PATCH 02/12] Add fragment --- changelogs/fragments/382-aaa-breaking-change.yaml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/382-aaa-breaking-change.yaml diff --git a/changelogs/fragments/382-aaa-breaking-change.yaml b/changelogs/fragments/382-aaa-breaking-change.yaml new file mode 100644 index 000000000..e8901be00 --- /dev/null +++ b/changelogs/fragments/382-aaa-breaking-change.yaml @@ -0,0 +1,2 @@ +breaking_changes: + - sonic_aaa - Updated the authentication implementation to support ordered authentication (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/382) From 801965e717d9abe478f41ee3f582331874310a90 Mon Sep 17 00:00:00 2001 From: shade Date: Tue, 11 Jun 2024 13:23:09 -0700 Subject: [PATCH 03/12] Update new attribute with version_added --- plugins/modules/sonic_aaa.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index 164250974..e414d5dae 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -64,6 +64,7 @@ default_auth: description: - Specifies order to authenticate aaa login methods + version_added: 2.5.0 type: list elements: str choices: From 6ca34148d5a874b42f3e7cc76a8748ba2d8c0bb3 Mon Sep 17 00:00:00 2001 From: stalabi1 <54641848+stalabi1@users.noreply.github.com> Date: Wed, 12 Jun 2024 15:33:57 -0700 Subject: [PATCH 04/12] Update sonic_aaa.py --- plugins/modules/sonic_aaa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index e414d5dae..ebd139bc6 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -64,7 +64,7 @@ default_auth: description: - Specifies order to authenticate aaa login methods - version_added: 2.5.0 + version_added: 3.0.0 type: list elements: str choices: From e07ad906b35d48782f80dc7a2185a5530116ccd2 Mon Sep 17 00:00:00 2001 From: shade Date: Wed, 19 Jun 2024 16:46:53 -0700 Subject: [PATCH 05/12] Update AAA module to align with SONiC functionality --- .../fragments/382-aaa-breaking-change.yaml | 2 +- .../network/sonic/argspec/aaa/aaa.py | 31 +- .../network/sonic/config/aaa/aaa.py | 445 ++++++++++++------ .../network/sonic/facts/aaa/aaa.py | 116 +++-- plugins/modules/sonic_aaa.py | 257 ++++++---- .../regression/roles/common/defaults/main.yml | 2 +- .../roles/sonic_aaa/defaults/main.yml | 133 ++++-- .../regression/roles/sonic_aaa/tasks/main.yml | 9 +- .../sonic_aaa/tasks/preparation_tests.yaml | 2 +- .../sonic_aaa/tasks/tasks_template_del.yaml | 21 - .../network/sonic/fixtures/sonic_aaa.yaml | 291 ++++++++++-- .../modules/network/sonic/test_sonic_aaa.py | 14 + 12 files changed, 902 insertions(+), 421 deletions(-) delete mode 100644 tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml diff --git a/changelogs/fragments/382-aaa-breaking-change.yaml b/changelogs/fragments/382-aaa-breaking-change.yaml index e8901be00..96b0c64b7 100644 --- a/changelogs/fragments/382-aaa-breaking-change.yaml +++ b/changelogs/fragments/382-aaa-breaking-change.yaml @@ -1,2 +1,2 @@ breaking_changes: - - sonic_aaa - Updated the authentication implementation to support ordered authentication (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/382) + - sonic_aaa - Update AAA module to align with SONiC functionality (https://github.com/ansible-collections/dellemc.enterprise_sonic/pull/382) diff --git a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py index 31cc88491..35ddcd260 100644 --- a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py @@ -42,16 +42,27 @@ def __init__(self, **kwargs): 'options': { 'authentication': { 'options': { - 'data': { - 'options': { - 'fail_through': {'type': 'bool'}, - 'default_auth': { - 'choices': ['local', 'ldap', 'radius', 'tacacs+'], - 'type': 'list', - 'elements': 'str' - }, - }, - 'type': 'dict' + 'auth_method': { + 'choices': ['ldap', 'local', 'radius', 'tacacs+'], + 'elements': 'str', + 'type': 'list' + }, + 'console_auth_local': {'default': False, 'type': 'bool'}, + 'failthrough': {'type': 'bool'}, + }, + 'type': 'dict' + }, + 'authorization': { + 'options': { + 'commands_auth_method': { + 'choices': ['local', 'tacacs+'], + 'elements': 'str', + 'type': 'list' + }, + 'login_auth_method': { + 'choices': ['ldap', 'local'], + 'elements': 'str', + 'type': 'list' } }, 'type': 'dict' diff --git a/plugins/module_utils/network/sonic/config/aaa/aaa.py b/plugins/module_utils/network/sonic/config/aaa/aaa.py index 98377b077..89e58b3b2 100644 --- a/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -13,6 +13,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type +from copy import deepcopy from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) @@ -21,25 +22,47 @@ ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.facts.facts import Facts from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( - update_states, - get_diff, -) -from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( - utils, + remove_empties, + update_states ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( - to_request, - edit_config + edit_config, + to_request ) from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.formatted_diff_utils import ( get_new_config, get_formatted_config_diff ) +AAA_AUTHENTICATION_PATH = '/data/openconfig-system:system/aaa/authentication/config' +AAA_AUTHORIZATION_PATH = '/data/openconfig-system:system/aaa/authorization' PATCH = 'patch' DELETE = 'delete' +def __derive_authentication_delete_op(key_set, command, exist_conf): + new_conf = exist_conf + auth_method = command.get('auth_method') + console_auth_local = command.get('console_auth_local') + failthrough = command.get('failthrough') + cfg_auth_method = new_conf.get('auth_method') + cfg_console_auth_local = new_conf.get('console_auth_local') + cfg_failthrough = new_conf.get('failthrough') + + if auth_method and auth_method == cfg_auth_method: + new_conf.pop('auth_method') + if console_auth_local and console_auth_local == cfg_console_auth_local: + new_conf['console_auth_local'] = False + if failthrough is not None and failthrough == cfg_failthrough: + new_conf.pop('failthrough') + return True, new_conf + + +TEST_KEYS_formatted_diff = [ + {'authentication': {'__delete_op': __derive_authentication_delete_op}} +] + + class Aaa(ConfigBase): """ The sonic_aaa class @@ -66,7 +89,7 @@ def get_aaa_facts(self): facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources) aaa_facts = facts['ansible_network_resources'].get('aaa') if not aaa_facts: - return [] + return {} return aaa_facts def execute_module(self): @@ -76,14 +99,17 @@ def execute_module(self): :returns: The result from module execution """ result = {'changed': False} - warnings = list() - commands = list() + warnings = [] + commands = [] existing_aaa_facts = self.get_aaa_facts() commands, requests = self.set_config(existing_aaa_facts) if commands and len(requests) > 0: if not self._module.check_mode: - self.edit_config(requests) + try: + edit_config(self._module, to_request(self._module, requests)) + except ConnectionError as exc: + self._module.fail_json(msg=str(exc), code=exc.code) result['changed'] = True result['commands'] = commands @@ -97,7 +123,8 @@ def execute_module(self): old_config = existing_aaa_facts if self._module.check_mode: result.pop('after', None) - new_config = get_new_config(commands, existing_aaa_facts) + new_config = get_new_config(commands, existing_aaa_facts, TEST_KEYS_formatted_diff) + self.post_process_generated_config(new_config) result['after(generated)'] = new_config if self._module._diff: result['diff'] = get_formatted_config_diff(old_config, @@ -106,12 +133,6 @@ def execute_module(self): result['warnings'] = warnings return result - def edit_config(self, requests): - try: - response = edit_config(self._module, to_request(self._module, requests)) - except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) - def set_config(self, existing_aaa_facts): """ Collect the configuration from the args passed to the module, collect the current configuration (as a dict from facts) @@ -120,7 +141,7 @@ def set_config(self, existing_aaa_facts): :returns: the commands necessary to migrate the current configuration to the desired configuration """ - want = self._module.params['config'] + want = remove_empties(self._module.params['config']) have = existing_aaa_facts resp = self.set_state(want, have) return to_list(resp) @@ -134,23 +155,20 @@ def set_state(self, want, have): :returns: the commands necessary to migrate the current configuration to the desired configuration """ + commands = [] + requests = [] state = self._module.params['state'] - if not want: - want = {} - if not have: - have = {} - diff = self.get_diff_aaa(want, have) - if state == 'deleted': - commands = self._state_deleted(want, have) - elif state == 'merged': - commands = self._state_merged(diff) + if state == 'merged': + commands, requests = self._state_merged(diff) elif state == 'replaced': - commands = self._state_replaced(diff) + commands, requests = self._state_replaced(want, have, diff) elif state == 'overridden': - commands = self._state_overridden(want, have) - return commands + commands, requests = self._state_overridden(want, have) + elif state == 'deleted': + commands, requests = self._state_deleted(want, have) + return commands, requests def _state_merged(self, diff): """ The command generator when state is merged @@ -159,40 +177,17 @@ def _state_merged(self, diff): :returns: the commands necessary to merge the provided into the current configuration """ - commands = [] - requests = [] - if diff: - requests = self.get_create_aaa_request(diff) - if len(requests) > 0: - commands = update_states(diff, "merged") - return commands, requests + commands = diff + requests = self.get_modify_aaa_requests(commands) - def _state_deleted(self, want, have): - """ The command generator when state is deleted - - :rtype: A list - :returns: the commands necessary to remove the current configuration - of the provided objects - """ - commands = [] - requests = [] - if not want: - if have: - requests = self.get_delete_all_aaa_request(have) - if len(requests) > 0: - commands = update_states(have, "deleted") + if commands and len(requests) > 0: + commands = update_states(commands, 'merged') else: - want = utils.remove_empties(want) - new_have = self.remove_default_entries(have) - d_diff = get_diff(want, new_have, is_skeleton=True) - diff_want = get_diff(want, d_diff, is_skeleton=True) - if diff_want: - requests = self.get_delete_all_aaa_request(diff_want) - if len(requests) > 0: - commands = update_states(diff_want, "deleted") + commands = [] + return commands, requests - def _state_replaced(self, diff): + def _state_replaced(self, want, have, diff): """ The command generator when state is merged :rtype: A list @@ -200,11 +195,25 @@ def _state_replaced(self, diff): the current configuration """ commands = [] + mod_commands = [] requests = [] - if diff: - requests = self.get_create_aaa_request(diff) - if len(requests) > 0: - commands = update_states(diff, "replaced") + replaced_config = self.get_replaced_config(want, have) + + if replaced_config: + is_delete_all = replaced_config == have + del_requests = self.get_delete_aaa_requests(replaced_config, have, is_delete_all) + requests.extend(del_requests) + commands.extend(update_states(replaced_config, 'deleted')) + mod_commands = want + else: + mod_commands = diff + + if mod_commands: + mod_requests = self.get_modify_aaa_requests(mod_commands) + if mod_requests: + requests.extend(mod_requests) + commands.extend(update_states(mod_commands, 'replaced')) + return commands, requests def _state_overridden(self, want, have): @@ -220,108 +229,250 @@ def _state_overridden(self, want, have): requests = [] if have and have != want: - del_requests = self.get_delete_all_aaa_request(have) + is_delete_all = True + del_requests = self.get_delete_aaa_requests(have, None, is_delete_all) requests.extend(del_requests) - commands.extend(update_states(have, "deleted")) + commands.extend(update_states(have, 'deleted')) have = [] if not have and want: mod_commands = want - mod_requests = self.get_create_aaa_request(mod_commands) + mod_requests = self.get_modify_aaa_requests(mod_commands) - if len(mod_requests) > 0: + if mod_requests: requests.extend(mod_requests) - commands.extend(update_states(mod_commands, "overridden")) + commands.extend(update_states(mod_commands, 'overridden')) return commands, requests - def get_create_aaa_request(self, commands): - requests = [] - aaa_path = 'data/openconfig-system:system/aaa' - method = PATCH - aaa_payload = self.build_create_aaa_payload(commands) - if aaa_payload: - request = {'path': aaa_path, 'method': method, 'data': aaa_payload} - requests.append(request) - return requests + def _state_deleted(self, want, have): + """ The command generator when state is deleted - def build_create_aaa_payload(self, commands): - payload = {} - if "authentication" in commands and commands["authentication"]: - payload = {"openconfig-system:aaa": {"authentication": {"config": {}}}} - if "default_auth" in commands["authentication"]["data"] and commands["authentication"]["data"]["default_auth"]: - cfg = {'authentication-method': commands["authentication"]["data"]["default_auth"]} - payload['openconfig-system:aaa']['authentication']['config'].update(cfg) - if "fail_through" in commands["authentication"]["data"]: - cfg = {'failthrough': str(commands["authentication"]["data"]["fail_through"])} - payload['openconfig-system:aaa']['authentication']['config'].update(cfg) - return payload + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + is_delete_all = False - def remove_default_entries(self, data): - new_data = {} - if not data: - return new_data + if not want: + commands = deepcopy(have) + is_delete_all = True + else: + commands = deepcopy(want) + + self.remove_default_entries(commands) + requests = self.get_delete_aaa_requests(commands, have, is_delete_all) + + if commands and len(requests) > 0: + commands = update_states(commands, 'deleted') else: - new_data = {'authentication': {'data': {}}} - default_auth = data['authentication']['data'].get('default_auth', None) - if default_auth is not None: - new_data["authentication"]["data"]["default_auth"] = default_auth - fail_through = data['authentication']['data'].get('fail_through', None) - if fail_through is not None: - new_data["authentication"]["data"]["fail_through"] = fail_through - return new_data - - def get_delete_all_aaa_request(self, have): + commands = [] + + return commands, requests + + def get_modify_aaa_requests(self, commands): requests = [] - if "authentication" in have and have["authentication"]: - if "default_auth" in have["authentication"]["data"]: - request = self.get_authentication_method_delete_request() - requests.append(request) - if "fail_through" in have["authentication"]["data"]: - request = self.get_failthrough_delete_request() - requests.append(request) + + if commands: + # Authentication modification handling + authentication = commands.get('authentication') + if authentication: + authentication_cfg_dict = {} + auth_method = authentication.get('auth_method') + console_auth_local = authentication.get('console_auth_local') + failthrough = authentication.get('failthrough') + + if auth_method: + authentication_cfg_dict['authentication-method'] = auth_method + if console_auth_local is not None: + authentication_cfg_dict['console-authentication-local'] = console_auth_local + if failthrough is not None: + authentication_cfg_dict['failthrough'] = str(failthrough) + if authentication_cfg_dict: + payload = {'openconfig-system:config': authentication_cfg_dict} + requests.append({'path': AAA_AUTHENTICATION_PATH, 'method': PATCH, 'data': payload}) + + # Authorization modification handling + authorization = commands.get('authorization') + if authorization: + authorization_dict = {} + commands_auth_method = authorization.get('commands_auth_method') + login_auth_method = authorization.get('login_auth_method') + + if commands_auth_method: + authorization_dict['openconfig-aaa-tacacsplus-ext:commands'] = {'config': {'authorization-method': commands_auth_method}} + if login_auth_method: + authorization_dict['openconfig-aaa-ext:login'] = {'config': {'authorization-method': login_auth_method}} + if authorization_dict: + payload = {'openconfig-system:authorization': authorization_dict} + requests.append({'path': AAA_AUTHORIZATION_PATH, 'method': PATCH, 'data': payload}) + return requests - def get_authentication_method_delete_request(self): - path = 'data/openconfig-system:system/aaa/authentication/config/authentication-method' - method = DELETE - request = {'path': path, 'method': method} + def get_delete_aaa_requests(self, commands, have, is_delete_all): + requests = [] + + if not commands: + return requests + + if is_delete_all: + requests.append(self.get_delete_authentication(None)) + requests.append(self.get_delete_authorization(None)) + return requests + + config_dict = {} + # Authentication deletion handling + authentication = commands.get('authentication') + if authentication: + auth_method = authentication.get('auth_method') + console_auth_local = authentication.get('console_auth_local') + failthrough = authentication.get('failthrough') + + cfg_authentication = have.get('authentication') + if cfg_authentication: + authentication_dict = {} + cfg_auth_method = cfg_authentication.get('auth_method') + cfg_console_auth_local = cfg_authentication.get('console_auth_local') + cfg_failthrough = cfg_authentication.get('failthrough') + + # Current SONiC behavior doesn't support single list item deletion + if auth_method and auth_method == cfg_auth_method: + requests.append(self.get_delete_authentication('authentication-method')) + authentication_dict['auth_method'] = auth_method + # Don't delete default console_auth_local False + if console_auth_local and console_auth_local == cfg_console_auth_local: + requests.append(self.get_delete_authentication('console-authentication-local')) + authentication_dict['console_auth_local'] = console_auth_local + if failthrough is not None and failthrough == cfg_failthrough: + requests.append(self.get_delete_authentication('failthrough')) + authentication_dict['failthrough'] = failthrough + if authentication_dict: + config_dict['authentication'] = authentication_dict + + # Authorization deletion handling + authorization = commands.get('authorization') + if authorization: + commands_auth_method = authorization.get('commands_auth_method') + login_auth_method = authorization.get('login_auth_method') + + cfg_authorization = have.get('authorization') + if cfg_authorization: + authorization_dict = {} + cfg_commands_auth_method = cfg_authorization.get('commands_auth_method') + cfg_login_auth_method = cfg_authorization.get('login_auth_method') + + # Current SONiC behavior doesn't support single list item deletion + if commands_auth_method and commands_auth_method == cfg_commands_auth_method: + requests.append(self.get_delete_authorization('openconfig-aaa-tacacsplus-ext:commands/config/authorization-method')) + authorization_dict['commands_auth_method'] = commands_auth_method + # Current SONiC behavior doesn't support single list item deletion + if login_auth_method and login_auth_method == cfg_login_auth_method: + requests.append(self.get_delete_authorization('openconfig-aaa-ext:login/config/authorization-method')) + authorization_dict['login_auth_method'] = login_auth_method + if authorization_dict: + config_dict['authorization'] = authorization_dict + + return requests + + def get_delete_authentication(self, attr): + url = AAA_AUTHENTICATION_PATH + + if attr: + url += '/%s' % (attr) + request = {'path': url, 'method': DELETE} return request - def get_failthrough_delete_request(self): - path = 'data/openconfig-system:system/aaa/authentication/config/failthrough' - method = DELETE - request = {'path': path, 'method': method} + def get_delete_authorization(self, attr): + url = AAA_AUTHORIZATION_PATH + + if attr: + url += '/%s' % (attr) + request = {'path': url, 'method': DELETE} return request - # Diff of default_auth needs to be compared as a whole list + def remove_default_entries(self, data): + if data: + authentication = data.get('authentication') + if authentication: + console_auth_local = authentication.get('console_auth_local') + if console_auth_local is False: + data['authentication'].pop('console_auth_local') + if not data['authentication']: + data.pop('authentication') + def get_diff_aaa(self, want, have): - diff_cfg = {} - diff_authentication = {} - diff_data = {} + """AAA module requires custom diff method due to overwritting of list in SONiC""" + if not have: + return want - authentication = want.get('authentication', None) + cfg_dict = {} + # Authentication diff handling + authentication = want.get('authentication') if authentication: - data = authentication.get('data', None) - if data: - fail_through = data.get('fail_through', None) - default_auth = data.get('default_auth', None) - - cfg_authentication = have.get('authentication', None) - if cfg_authentication: - cfg_data = cfg_authentication.get('data', None) - if cfg_data: - cfg_fail_through = cfg_data.get('fail_through', None) - cfg_default_auth = cfg_data.get('default_auth', None) - - if fail_through is not None and fail_through != cfg_fail_through: - diff_data['fail_through'] = fail_through - if default_auth != cfg_default_auth: - diff_data['default_auth'] = default_auth - if diff_data: - diff_authentication['data'] = diff_data - diff_cfg['authentication'] = diff_authentication - else: - diff_cfg = want - - return diff_cfg + auth_method = authentication.get('auth_method') + console_auth_local = authentication.get('console_auth_local') + failthrough = authentication.get('failthrough') + + cfg_authentication = have.get('authentication') + if cfg_authentication: + authentication_dict = {} + cfg_auth_method = cfg_authentication.get('auth_method') + cfg_console_auth_local = cfg_authentication.get('console_auth_local') + cfg_failthrough = cfg_authentication.get('failthrough') + + if auth_method and auth_method != cfg_auth_method: + authentication_dict['auth_method'] = auth_method + if console_auth_local is not None and console_auth_local != cfg_console_auth_local: + authentication_dict['console_auth_local'] = console_auth_local + if failthrough and failthrough != cfg_failthrough: + authentication_dict['failthrough'] = failthrough + if authentication_dict: + cfg_dict['authentication'] = authentication_dict + + # Authorization diff handling + authorization = want.get('authorization') + if authorization: + commands_auth_method = authorization.get('commands_auth_method') + login_auth_method = authorization.get('login_auth_method') + + cfg_authorization = have.get('authorization') + if cfg_authorization: + authorization_dict = {} + cfg_commands_auth_method = cfg_authorization.get('commands_auth_method') + cfg_login_auth_method = cfg_authorization.get('login_auth_method') + + if commands_auth_method and commands_auth_method != cfg_commands_auth_method: + authorization_dict['commands_auth_method'] = commands_auth_method + if login_auth_method and login_auth_method != cfg_login_auth_method: + authorization_dict['login_auth_method'] = login_auth_method + if authorization_dict: + cfg_dict['authorization'] = authorization_dict + + return cfg_dict + + def get_replaced_config(self, want, have): + config_dict = {} + + if want and have: + authentication = want.get('authentication') + authorization = want.get('authorization') + cfg_authentication = have.get('authentication') + cfg_authorization = have.get('authorization') + + if authentication != cfg_authentication: + config_dict['authentication'] = cfg_authentication + if authorization != cfg_authorization: + config_dict['authorization'] = cfg_authorization + + return config_dict + + def post_process_generated_config(self, data): + if data: + authorization = data.get('authorization') + if authorization: + if 'commands_auth_method' in authorization and not authorization['commands_auth_method']: + data['authorization'].pop('commands_auth_method') + if 'login_auth_method' in authorization and not authorization['login_auth_method']: + data['authorization'].pop('login_auth_method') + if not data['authorization']: + data.pop('authorization') diff --git a/plugins/module_utils/network/sonic/facts/aaa/aaa.py b/plugins/module_utils/network/sonic/facts/aaa/aaa.py index 9c380a5ab..6f92b0275 100644 --- a/plugins/module_utils/network/sonic/facts/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/facts/aaa/aaa.py @@ -1,6 +1,6 @@ # # -*- coding: utf-8 -*- -# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved +# Copyright 2024 Dell Inc. or its subsidiaries. All Rights Reserved. # GNU General Public License v3.0+ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) """ @@ -9,8 +9,7 @@ for a given resource, parsed, and the facts tree is populated based on the configuration. """ - -from __future__ import absolute_import, division, print_function +from __future__ import (absolute_import, division, print_function) __metaclass__ = type from copy import deepcopy @@ -18,13 +17,16 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import ( utils, ) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.utils.utils import ( + remove_empties +) +from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.aaa.aaa import AaaArgs from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.sonic import ( to_request, edit_config ) -from ansible_collections.dellemc.enterprise_sonic.plugins.module_utils.network.sonic.argspec.aaa.aaa import AaaArgs -GET = "get" +AAA_PATH = '/data/openconfig-system:system/aaa' class AaaFacts(object): @@ -45,61 +47,79 @@ def __init__(self, module, subspec='config', options='options'): self.generated_spec = utils.generate_dict(facts_argument_spec) - def get_aaa(self): - """Get aaa details available in chassis""" - request = [{"path": "data/openconfig-system:system/aaa", "method": GET}] - try: - response = edit_config(self._module, to_request(self._module, request)) - except ConnectionError as exc: - self._module.fail_json(msg=str(exc), code=exc.code) - data = {} - if ('openconfig-system:aaa' in response[0][1]): - if ('authentication' in response[0][1]['openconfig-system:aaa']): - if ('config' in response[0][1]['openconfig-system:aaa']['authentication']): - data = response[0][1]['openconfig-system:aaa']['authentication']['config'] - return data - def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for aaa + """ Populate the facts for qos_pfc :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf :rtype: dictionary :returns: facts """ - if not data: - data = self.get_aaa() objs = [] - objs = self.render_config(self.generated_spec, data) + + if not data: + data = self.update_aaa(self._module) + objs = data facts = {} if objs: params = utils.validate_config(self.argument_spec, {'config': objs}) - facts['aaa'] = params['config'] - + facts['aaa'] = remove_empties(params['config']) ansible_facts['ansible_network_resources'].update(facts) return ansible_facts - def render_config(self, spec, conf): - """ - Render config as dictionary structure and delete keys - from spec for null values + def get_config(self, module, path, key_name): + """Retrive OC configuration from device""" + cfg = None + get_path = '%s/%s' % (AAA_PATH, path) + request = {'path': get_path, 'method': 'get'} - :param spec: The facts tree, generated from the argspec - :param conf: The configuration - :rtype: dictionary - :returns: The generated config - """ - config = self.parse_sonic_aaa(spec, conf) - return config - - def parse_sonic_aaa(self, spec, conf): - config = deepcopy(spec) - if conf: - temp = {} - if ('authentication-method' in conf) and (conf['authentication-method']): - temp['default_auth'] = conf['authentication-method'] - if ('failthrough' in conf): - temp['fail_through'] = conf['failthrough'] - if temp: - config['authentication']['data'] = temp - return utils.remove_empties(config) + try: + response = edit_config(module, to_request(module, request)) + if key_name in response[0][1]: + cfg = response[0][1].get(key_name) + except Exception as exc: + # Avoid raising error when there is no configuration + if 'Resource not found' in str(exc): + pass + else: + module.fail_json(msg=str(exc), code=exc.code) + + return cfg + + def update_aaa(self, module): + """Transform OC configuration to Ansible argspec""" + config_dict = {} + bool_dict = {'True': True, 'False': False} + + # Authentication configuration handling + authentication_cfg = self.get_config(module, 'authentication/config', 'openconfig-system:config') + if authentication_cfg: + authentication_dict = {} + auth_method = authentication_cfg.get('authentication-method') + console_auth_local = authentication_cfg.get('console-authentication-local') + failthrough = authentication_cfg.get('failthrough') + + if auth_method: + authentication_dict['auth_method'] = auth_method + if console_auth_local: + authentication_dict['console_auth_local'] = console_auth_local + if failthrough: + authentication_dict['failthrough'] = bool_dict[failthrough] + if authentication_dict: + config_dict['authentication'] = authentication_dict + + # Authoriation configuration handling + authorization_dict = {} + commands_auth_method = self.get_config(module, 'authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method', + 'openconfig-aaa-tacacsplus-ext:authorization-method') + login_auth_method = self.get_config(module, 'authorization/openconfig-aaa-ext:login/config/authorization-method', + 'openconfig-aaa-ext:authorization-method') + + if commands_auth_method: + authorization_dict['commands_auth_method'] = commands_auth_method + if login_auth_method: + authorization_dict['login_auth_method'] = login_auth_method + if authorization_dict: + config_dict['authorization'] = authorization_dict + + return config_dict diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index ebd139bc6..e4d03ac00 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -35,203 +35,268 @@ module: sonic_aaa version_added: 1.1.0 notes: -- Tested against Enterprise SONiC Distribution by Dell Technologies. -- Supports C(check_mode). -author: Abirami N (@abirami-n) -short_description: Manage AAA and its parameters +- Tested against Enterprise SONiC Distribution by Dell Technologies +- Supports C(check_mode) +author: Shade Talabi (@stalabi1) +short_description: Manage AAA configuration on SONiC description: - - This module is used for configuration management of aaa parameters on devices running Enterprise SONiC. + - This module provides configuration management of AAA for devices running SONiC. options: config: description: - - Specifies the aaa related configurations + - AAA configuration type: dict suboptions: authentication: description: - - Specifies the configurations required for aaa authentication + - AAA authentication configuration type: dict + version_added: 3.0.0 suboptions: - data: + auth_method: description: - - Specifies the data required for aaa authentication - type: dict - suboptions: - fail_through: - description: - - Specifies the state of failthrough - type: bool - default_auth: - description: - - Specifies order to authenticate aaa login methods - version_added: 3.0.0 - type: list - elements: str - choices: - - local - - ldap - - radius - - tacacs+ - + - Specifies the order of the methods in which to authenticate login + type: list + elements: str + choices: ['ldap', 'local', 'radius', 'tacacs+'] + console_auth_local : + description: + Enable/disable local authentication on console + type: bool + default: False + failthrough: + description: + - Enable/disable failthrough + type: bool + authorization: + description: + - AAA authorization configuration + type: dict + version_added: 3.0.0 + suboptions: + commands_auth_method: + description: + - Specifies the order of the methods in which to authorize commands + type: list + elements: str + choices: ['local', 'tacacs+'] + login_auth_method: + description: + - Specifies the order of the methods in which to authorize login + type: list + elements: str + choices: ['ldap', 'local'] state: description: - - Specifies the operation to be performed on the aaa parameters configured on the device. - - In case of merged, the input configuration will be merged with the existing aaa configuration on the device. - - In case of deleted the existing aaa configuration will be removed from the device. - - In case of replaced, the existing aaa configuration will be replaced with provided configuration. - - In case of overridden, the existing aaa configuration will be overridden with the provided configuration. - default: merged + - The state of the configuration after module completion choices: ['merged', 'deleted', 'overridden', 'replaced'] + default: merged type: str """ EXAMPLES = """ -# Using deleted +# Using Merged # # Before state: # ------------- # -# do show aaa -# AAA Authentication Information -# --------------------------------------------------------- -# failthrough : True -# login-method : local +# sonic# show aaa +# (No AAA configuration present) -- name: Delete aaa configurations +- name: Merge AAA configuration dellemc.enterprise_sonic.sonic_aaa: config: authentication: - data: - default_auth: - - local - state: deleted + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap + state: merged # After state: # ------------ # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- # failthrough : True -# login-method : +# login-method : local, ldap, radius, tacacs+ +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local, ldap +# commands : local, tacacs+ -# Using deleted +# Using Replaced # # Before state: # ------------- # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- # failthrough : True -# login-method : local +# login-method : local, ldap, radius, tacacs+ +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local, ldap +# commands : local, tacacs+ -- name: Delete aaa configurations +- name: Replace AAA configuration dellemc.enterprise_sonic.sonic_aaa: config: - state: deleted + authentication: + console_auth_local: True + failthrough: False + authorization: + commands_auth_method: + - local + state: replaced # After state: # ------------ # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- -# failthrough : +# failthrough : False # login-method : +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local -# Using merged +# Using Overridden # # Before state: # ------------- # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- -# failthrough : False -# login-method : +# failthrough : True +# login-method : local, ldap, radius, tacacs+ +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local, ldap +# commands : local, tacacs+ -- name: Merge aaa configurations +- name: Override AAA configuration dellemc.enterprise_sonic.sonic_aaa: config: authentication: - data: - default_auth: - - local - - tacacs+ - fail_through: true - state: merged + auth_method: + - tacacs+ + console_auth_local: True + failthrough: True + state: overridden # After state: # ------------ # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- # failthrough : True -# login-method : local, tacacs+ +# login-method : tacacs+ +# console authentication : local -# Using overridden +# Using Deleted # # Before state: # ------------- # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- # failthrough : True -# login-method : local, ldap +# login-method : local, ldap, radius, tacacs+ +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local, ldap +# commands : local, tacacs+ -- name: Override aaa configurations +- name: Delete AAA individual attributes dellemc.enterprise_sonic.sonic_aaa: config: authentication: - data: - fail_through: False - state: overridden + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap + state: deleted # After state: # ------------ # -# do show aaa -# AAA Authentication Information -# --------------------------------------------------------- -# failthrough : False -# login-method : +# sonic# show aaa +# (No AAA configuration present) -# Using replaced +# Using Deleted # # Before state: # ------------- # -# do show aaa +# sonic# show aaa +# --------------------------------------------------------- # AAA Authentication Information # --------------------------------------------------------- -# failthrough : False -# login-method : ldap, radius +# failthrough : True +# login-method : local, ldap, radius, tacacs+ +# console authentication : local +# --------------------------------------------------------- +# AAA Authorization Information +# --------------------------------------------------------- +# login : local, ldap +# commands : local, tacacs+ -- name: Replace aaa configurations +- name: Delete all AAA configuration dellemc.enterprise_sonic.sonic_aaa: - config: - authentication: - data: - default_auth: - - local - state: replaced + config: {} + state: deleted # After state: # ------------ # -# do show aaa -# AAA Authentication Information -# --------------------------------------------------------- -# failthrough : False -# login-method : local - - +# sonic# show aaa +# (No AAA configuration present) """ RETURN = """ before: diff --git a/tests/regression/roles/common/defaults/main.yml b/tests/regression/roles/common/defaults/main.yml index be0c9eaa0..a21414b6d 100644 --- a/tests/regression/roles/common/defaults/main.yml +++ b/tests/regression/roles/common/defaults/main.yml @@ -28,7 +28,7 @@ single_run_idem_condition: "{{ 'Passed' if (single_run_task_output.failed == fal else 'Failed' }}" -REPORT_DIR: "/var/www/html/ansible/regression" +REPORT_DIR: "~" empty: [] module_name1: debug diff --git a/tests/regression/roles/sonic_aaa/defaults/main.yml b/tests/regression/roles/sonic_aaa/defaults/main.yml index b3dbc612d..d10dc4767 100644 --- a/tests/regression/roles/sonic_aaa/defaults/main.yml +++ b/tests/regression/roles/sonic_aaa/defaults/main.yml @@ -1,80 +1,119 @@ --- ansible_connection: httpapi module_name: aaa + tests: - name: test_case_01 - description: Create aaa properties + description: Initial AAA configuration state: merged input: authentication: - data: - fail_through: true - default_auth: - - tacacs+ - - local + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap - name: test_case_02 - description: Update created aaa properties + description: Modify AAA configuration state: merged input: authentication: - data: - fail_through: false - default_auth: - - local - - radius + auth_method: + - local + - tacacs+ + - ldap + - radius + console_auth_local: False + failthrough: False + authorization: + commands_auth_method: + - tacacs+ + - local + login_auth_method: + - ldap + - local - name: test_case_03 - description: Replace aaa properties + description: Replace AAA configuration state: replaced input: authentication: - data: - fail_through: true - default_auth: - - local - - ldap + console_auth_local: True + authorization: + login_auth_method: + - local - name: test_case_04 - description: Delete aaa properties - state: deleted + description: Override AAA configuration + state: overridden input: authentication: - data: - default_auth: - - local - - ldap + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap - name: test_case_05 - description: Update aaa properties - state: merged + description: Delete AAA individual attributes + state: deleted input: authentication: - data: - fail_through: true - default_auth: - - local - - radius + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap - name: test_case_06 - description: Override aaa properties - state: overridden + description: Add AAA configuration for delete all + state: merged input: authentication: - data: - fail_through: False + auth_method: + - local + - tacacs+ + - ldap + - radius + console_auth_local: False + failthrough: False + authorization: + commands_auth_method: + - tacacs+ + - local + login_auth_method: + - ldap + - local - name: test_case_07 - description: Override aaa properties - state: overridden - input: - authentication: - data: - fail_through: true - default_auth: - - local - -test_delete_all: - - name: del_all_test_case_01 - description: Delete aaa properties + description: Delete all AAA configuratiom state: deleted + input: {} diff --git a/tests/regression/roles/sonic_aaa/tasks/main.yml b/tests/regression/roles/sonic_aaa/tasks/main.yml index 09325ea7d..0678e3732 100644 --- a/tests/regression/roles/sonic_aaa/tasks/main.yml +++ b/tests/regression/roles/sonic_aaa/tasks/main.yml @@ -1,4 +1,7 @@ -- debug: msg="sonic_aaa Test started ..." +- debug: msg="sonic_aaa test started ..." + +- set_fact: + base_cfg_path: "{{ playbook_dir + '/roles/' + role_name + '/' + 'templates/' }}" - name: Preparation tests include_tasks: preparation_tests.yaml @@ -6,7 +9,3 @@ - name: "Test {{ module_name }} started ..." include_tasks: tasks_template.yaml loop: "{{ tests }}" - -- name: "test_delete_all {{ module_name }} stated ..." - include_tasks: tasks_template_del.yaml - loop: "{{ test_delete_all }}" diff --git a/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml b/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml index e8a964f3f..ee5f19794 100644 --- a/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml +++ b/tests/regression/roles/sonic_aaa/tasks/preparation_tests.yaml @@ -1,4 +1,4 @@ -- name: Deletes old radius server configurations +- name: Delete existing AAA configuration sonic_aaa: config: {} state: deleted diff --git a/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml b/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml deleted file mode 100644 index b50cc26cd..000000000 --- a/tests/regression/roles/sonic_aaa/tasks/tasks_template_del.yaml +++ /dev/null @@ -1,21 +0,0 @@ -- name: "{{ item.name}} , {{ item.description}}" - sonic_aaa: - state: "{{ item.state }}" - config: - register: action_task_output - ignore_errors: yes - -- import_role: - name: common - tasks_from: action.facts.report.yaml - -- name: "{{ item.name}} , {{ item.description}} Idempotent" - sonic_aaa: - state: "{{ item.state }}" - config: - register: idempotent_task_output - ignore_errors: yes - -- import_role: - name: common - tasks_from: idempotent.facts.report.yaml diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml index 8ddf6fb6c..b2af1cabf 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml @@ -2,80 +2,283 @@ merged_01: module_args: config: - authentication: - data: - fail_through: true - default_auth: + authentication: + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap + existing_aaa_config: + - path: '/data/openconfig-system:system/aaa/authentication/config' + response: + code: 200 + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' + response: + code: 200 + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' + response: + code: 200 + expected_config_requests: + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'patch' + data: + openconfig-system:config: + authentication-method: - local + - ldap - radius + - tacacs+ + console-authentication-local: True + failthrough: 'True' + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'patch' + data: + openconfig-system:authorization: + openconfig-aaa-tacacsplus-ext:commands: + config: + authorization-method: + - local + - tacacs+ + openconfig-aaa-ext:login: + config: + authorization-method: + - local + - ldap + +replaced_01: + module_args: + config: + authentication: + console_auth_local: True + authorization: + commands_auth_method: + - local + state: replaced existing_aaa_config: - - path: "data/openconfig-system:system/aaa" + - path: '/data/openconfig-system:system/aaa/authentication/config' + response: + code: 200 + value: + openconfig-system:config: + authentication-method: + - local + - ldap + - radius + - tacacs+ + console-authentication-local: True + failthrough: 'True' + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' response: code: 200 - - path: "data/sonic-system-aaa:sonic-system-aaa" + value: + openconfig-aaa-tacacsplus-ext:authorization-method: + - local + - tacacs+ + + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' response: code: 200 value: + openconfig-aaa-ext:authorization-method: + - local + - ldap expected_config_requests: - - path: "data/openconfig-system:system/aaa" - method: "patch" + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'delete' data: - openconfig-system:aaa: - authentication: + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'patch' + data: + openconfig-system:config: + console-authentication-local: True + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'patch' + data: + openconfig-system:authorization: + openconfig-aaa-tacacsplus-ext:commands: config: - authentication-method: + authorization-method: - local - - radius - failthrough: 'True' + +overridden_01: + module_args: + config: + authentication: + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap + state: overridden + existing_aaa_config: + - path: '/data/openconfig-system:system/aaa/authentication/config' + response: + code: 200 + value: + openconfig-system:config: + console-authentication-local: False + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' + response: + code: 200 + value: + openconfig-aaa-tacacsplus-ext:authorization-method: + - local + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' + response: + code: 200 + expected_config_requests: + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'patch' + data: + openconfig-system:config: + authentication-method: + - local + - ldap + - radius + - tacacs+ + console-authentication-local: True + failthrough: 'True' + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'patch' + data: + openconfig-system:authorization: + openconfig-aaa-tacacsplus-ext:commands: + config: + authorization-method: + - local + - tacacs+ + openconfig-aaa-ext:login: + config: + authorization-method: + - local + - ldap + deleted_01: module_args: + config: + authentication: + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap state: deleted existing_aaa_config: - - path: "data/openconfig-system:system/aaa" + - path: '/data/openconfig-system:system/aaa/authentication/config' response: code: 200 value: - openconfig-system:aaa: - authentication: - config: - authentication-method: - - radius - - local - failthrough: true + openconfig-system:config: + authentication-method: + - local + - ldap + - radius + - tacacs+ + console-authentication-local: True + failthrough: 'True' + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' + response: + code: 200 + value: + openconfig-aaa-tacacsplus-ext:authorization-method: + - local + - tacacs+ + + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' + response: + code: 200 + value: + openconfig-aaa-ext:authorization-method: + - local + - ldap expected_config_requests: - - path: "data/openconfig-system:system/aaa/authentication/config/authentication-method" - method: "delete" + - path: '/data/openconfig-system:system/aaa/authentication/config/authentication-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authentication/config/console-authentication-local' + method: 'delete' data: - - path: "data/openconfig-system:system/aaa/authentication/config/failthrough" - method: "delete" + - path: '/data/openconfig-system:system/aaa/authentication/config/failthrough' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' + method: 'delete' data: deleted_02: module_args: - config: - authentication: - data: - fail_through: true - default_auth: - - local - - radius + config: {} state: deleted existing_aaa_config: - - path: "data/openconfig-system:system/aaa" + - path: '/data/openconfig-system:system/aaa/authentication/config' response: code: 200 value: - openconfig-system:aaa: - authentication: - config: - authentication-method: - - radius - - local - failthrough: true + openconfig-system:config: + authentication-method: + - local + - ldap + - radius + - tacacs+ + console-authentication-local: True + failthrough: 'True' + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method' + response: + code: 200 + value: + openconfig-aaa-tacacsplus-ext:authorization-method: + - local + - tacacs+ + - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' + response: + code: 200 + value: + openconfig-aaa-ext:authorization-method: + - local + - ldap expected_config_requests: - - path: "data/openconfig-system:system/aaa/authentication/config/authentication-method" - method: "delete" + - path: '/data/openconfig-system:system/aaa/authentication/config' + method: 'delete' data: - - path: "data/openconfig-system:system/aaa/authentication/config/failthrough" - method: "delete" + - path: '/data/openconfig-system:system/aaa/authorization' + method: 'delete' data: diff --git a/tests/unit/modules/network/sonic/test_sonic_aaa.py b/tests/unit/modules/network/sonic/test_sonic_aaa.py index de747c2c8..fb1296059 100644 --- a/tests/unit/modules/network/sonic/test_sonic_aaa.py +++ b/tests/unit/modules/network/sonic/test_sonic_aaa.py @@ -54,6 +54,20 @@ def test_sonic_aaa_merged_01(self): result = self.execute_module(changed=True) self.validate_config_requests() + def test_sonic_aaa_replaced_01(self): + set_module_args(self.fixture_data['replaced_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['replaced_01']['existing_aaa_config']) + self.initialize_config_requests(self.fixture_data['replaced_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + + def test_sonic_aaa_overridden_01(self): + set_module_args(self.fixture_data['overridden_01']['module_args']) + self.initialize_facts_get_requests(self.fixture_data['overridden_01']['existing_aaa_config']) + self.initialize_config_requests(self.fixture_data['overridden_01']['expected_config_requests']) + result = self.execute_module(changed=True) + self.validate_config_requests() + def test_sonic_aaa_deleted_01(self): set_module_args(self.fixture_data['deleted_01']['module_args']) self.initialize_facts_get_requests(self.fixture_data['deleted_01']['existing_aaa_config']) From f956683223aeac2f1d6e1c38a17d883f7d56a5ef Mon Sep 17 00:00:00 2001 From: shade Date: Wed, 19 Jun 2024 16:55:23 -0700 Subject: [PATCH 06/12] Update playbook example --- playbooks/common_examples/sonic_aaa.yaml | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/playbooks/common_examples/sonic_aaa.yaml b/playbooks/common_examples/sonic_aaa.yaml index cb147a353..9019a809d 100644 --- a/playbooks/common_examples/sonic_aaa.yaml +++ b/playbooks/common_examples/sonic_aaa.yaml @@ -38,11 +38,20 @@ sonic_aaa: config: authentication: - data: - fail_through: true - default_auth: - - tacacs+ - - local + auth_method: + - local + - ldap + - radius + - tacacs+ + console_auth_local: True + failthrough: True + authorization: + commands_auth_method: + - local + - tacacs+ + login_auth_method: + - local + - ldap state: merged - name: Merge tacacs configurations sonic_tacacs_server: From c32de67b11f6dffea7ec1aaf38a19bd1823a259d Mon Sep 17 00:00:00 2001 From: stalabi1 <54641848+stalabi1@users.noreply.github.com> Date: Wed, 19 Jun 2024 16:58:23 -0700 Subject: [PATCH 07/12] Revert --- tests/regression/roles/common/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/regression/roles/common/defaults/main.yml b/tests/regression/roles/common/defaults/main.yml index a21414b6d..be0c9eaa0 100644 --- a/tests/regression/roles/common/defaults/main.yml +++ b/tests/regression/roles/common/defaults/main.yml @@ -28,7 +28,7 @@ single_run_idem_condition: "{{ 'Passed' if (single_run_task_output.failed == fal else 'Failed' }}" -REPORT_DIR: "~" +REPORT_DIR: "/var/www/html/ansible/regression" empty: [] module_name1: debug From 8fb0dc4edf98e222872d38fe00c4544751a0dfc1 Mon Sep 17 00:00:00 2001 From: shade Date: Tue, 25 Jun 2024 13:50:26 -0700 Subject: [PATCH 08/12] Address review comments and add name-service feature --- .../network/sonic/argspec/aaa/aaa.py | 32 ++- .../network/sonic/config/aaa/aaa.py | 186 ++++++++++----- .../network/sonic/facts/aaa/aaa.py | 33 ++- plugins/modules/sonic_aaa.py | 106 ++++++++- .../roles/sonic_aaa/defaults/main.yml | 94 ++++++++ .../network/sonic/fixtures/sonic_aaa.yaml | 213 ++++++++++++++++++ 6 files changed, 601 insertions(+), 63 deletions(-) diff --git a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py index 35ddcd260..97dbdf198 100644 --- a/plugins/module_utils/network/sonic/argspec/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/argspec/aaa/aaa.py @@ -47,7 +47,7 @@ def __init__(self, **kwargs): 'elements': 'str', 'type': 'list' }, - 'console_auth_local': {'default': False, 'type': 'bool'}, + 'console_auth_local': {'type': 'bool'}, 'failthrough': {'type': 'bool'}, }, 'type': 'dict' @@ -66,6 +66,36 @@ def __init__(self, **kwargs): } }, 'type': 'dict' + }, + 'name_service': { + 'options': { + 'group': { + 'choices': ['ldap', 'local', 'login'], + 'elements': 'str', + 'type': 'list' + }, + 'netgroup': { + 'choices': ['ldap', 'local'], + 'elements': 'str', + 'type': 'list' + }, + 'passwd': { + 'choices': ['ldap', 'local', 'login'], + 'elements': 'str', + 'type': 'list' + }, + 'shadow': { + 'choices': ['ldap', 'local', 'login'], + 'elements': 'str', + 'type': 'list' + }, + 'sudoers': { + 'choices': ['ldap', 'local'], + 'elements': 'str', + 'type': 'list' + } + }, + 'type': 'dict' } }, 'type': 'dict' diff --git a/plugins/module_utils/network/sonic/config/aaa/aaa.py b/plugins/module_utils/network/sonic/config/aaa/aaa.py index 89e58b3b2..761615c99 100644 --- a/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -36,33 +36,11 @@ AAA_AUTHENTICATION_PATH = '/data/openconfig-system:system/aaa/authentication/config' AAA_AUTHORIZATION_PATH = '/data/openconfig-system:system/aaa/authorization' +AAA_NAME_SERVICE_PATH = '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' PATCH = 'patch' DELETE = 'delete' -def __derive_authentication_delete_op(key_set, command, exist_conf): - new_conf = exist_conf - auth_method = command.get('auth_method') - console_auth_local = command.get('console_auth_local') - failthrough = command.get('failthrough') - cfg_auth_method = new_conf.get('auth_method') - cfg_console_auth_local = new_conf.get('console_auth_local') - cfg_failthrough = new_conf.get('failthrough') - - if auth_method and auth_method == cfg_auth_method: - new_conf.pop('auth_method') - if console_auth_local and console_auth_local == cfg_console_auth_local: - new_conf['console_auth_local'] = False - if failthrough is not None and failthrough == cfg_failthrough: - new_conf.pop('failthrough') - return True, new_conf - - -TEST_KEYS_formatted_diff = [ - {'authentication': {'__delete_op': __derive_authentication_delete_op}} -] - - class Aaa(ConfigBase): """ The sonic_aaa class @@ -123,7 +101,7 @@ def execute_module(self): old_config = existing_aaa_facts if self._module.check_mode: result.pop('after', None) - new_config = get_new_config(commands, existing_aaa_facts, TEST_KEYS_formatted_diff) + new_config = get_new_config(commands, existing_aaa_facts) self.post_process_generated_config(new_config) result['after(generated)'] = new_config if self._module._diff: @@ -260,7 +238,6 @@ def _state_deleted(self, want, have): else: commands = deepcopy(want) - self.remove_default_entries(commands) requests = self.get_delete_aaa_requests(commands, have, is_delete_all) if commands and len(requests) > 0: @@ -307,6 +284,30 @@ def get_modify_aaa_requests(self, commands): payload = {'openconfig-system:authorization': authorization_dict} requests.append({'path': AAA_AUTHORIZATION_PATH, 'method': PATCH, 'data': payload}) + # Name-service modification handling + name_service = commands.get('name_service') + if name_service: + name_service_cfg_dict = {} + group = name_service.get('group') + netgroup = name_service.get('netgroup') + passwd = name_service.get('passwd') + shadow = name_service.get('shadow') + sudoers = name_service.get('sudoers') + + if group: + name_service_cfg_dict['group-method'] = group + if netgroup: + name_service_cfg_dict['netgroup-method'] = netgroup + if passwd: + name_service_cfg_dict['passwd-method'] = passwd + if shadow: + name_service_cfg_dict['shadow-method'] = shadow + if sudoers: + name_service_cfg_dict['sudoers-method'] = sudoers + if name_service_cfg_dict: + payload = {'openconfig-aaa-ext:config': name_service_cfg_dict} + requests.append({'path': AAA_NAME_SERVICE_PATH, 'method': PATCH, 'data': payload}) + return requests def get_delete_aaa_requests(self, commands, have, is_delete_all): @@ -316,8 +317,9 @@ def get_delete_aaa_requests(self, commands, have, is_delete_all): return requests if is_delete_all: - requests.append(self.get_delete_authentication(None)) - requests.append(self.get_delete_authorization(None)) + requests.append(self.get_delete_request(AAA_AUTHENTICATION_PATH, None)) + requests.append(self.get_delete_request(AAA_AUTHORIZATION_PATH, None)) + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, None)) return requests config_dict = {} @@ -337,14 +339,13 @@ def get_delete_aaa_requests(self, commands, have, is_delete_all): # Current SONiC behavior doesn't support single list item deletion if auth_method and auth_method == cfg_auth_method: - requests.append(self.get_delete_authentication('authentication-method')) + requests.append(self.get_delete_request(AAA_AUTHENTICATION_PATH, 'authentication-method')) authentication_dict['auth_method'] = auth_method - # Don't delete default console_auth_local False - if console_auth_local and console_auth_local == cfg_console_auth_local: - requests.append(self.get_delete_authentication('console-authentication-local')) + if console_auth_local is not None and console_auth_local == cfg_console_auth_local: + requests.append(self.get_delete_request(AAA_AUTHENTICATION_PATH, 'console-authentication-local')) authentication_dict['console_auth_local'] = console_auth_local if failthrough is not None and failthrough == cfg_failthrough: - requests.append(self.get_delete_authentication('failthrough')) + requests.append(self.get_delete_request(AAA_AUTHENTICATION_PATH, 'failthrough')) authentication_dict['failthrough'] = failthrough if authentication_dict: config_dict['authentication'] = authentication_dict @@ -363,43 +364,61 @@ def get_delete_aaa_requests(self, commands, have, is_delete_all): # Current SONiC behavior doesn't support single list item deletion if commands_auth_method and commands_auth_method == cfg_commands_auth_method: - requests.append(self.get_delete_authorization('openconfig-aaa-tacacsplus-ext:commands/config/authorization-method')) + requests.append(self.get_delete_request(AAA_AUTHORIZATION_PATH, + 'openconfig-aaa-tacacsplus-ext:commands/config/authorization-method')) authorization_dict['commands_auth_method'] = commands_auth_method - # Current SONiC behavior doesn't support single list item deletion if login_auth_method and login_auth_method == cfg_login_auth_method: - requests.append(self.get_delete_authorization('openconfig-aaa-ext:login/config/authorization-method')) + requests.append(self.get_delete_request(AAA_AUTHORIZATION_PATH, 'openconfig-aaa-ext:login/config/authorization-method')) authorization_dict['login_auth_method'] = login_auth_method if authorization_dict: config_dict['authorization'] = authorization_dict - return requests - - def get_delete_authentication(self, attr): - url = AAA_AUTHENTICATION_PATH + # Name-service deletion handling + name_service = commands.get('name_service') + if name_service: + group = name_service.get('group') + netgroup = name_service.get('netgroup') + passwd = name_service.get('passwd') + shadow = name_service.get('shadow') + sudoers = name_service.get('sudoers') + + cfg_name_service = have.get('name_service') + if cfg_name_service: + name_service_dict = {} + cfg_group = cfg_name_service.get('group') + cfg_netgroup = cfg_name_service.get('netgroup') + cfg_passwd = cfg_name_service.get('passwd') + cfg_shadow = cfg_name_service.get('shadow') + cfg_sudoers = cfg_name_service.get('sudoers') - if attr: - url += '/%s' % (attr) - request = {'path': url, 'method': DELETE} - return request + # Current SONiC behavior doesn't support single list item deletion + if group and group == cfg_group: + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, 'group-method')) + name_service_dict['group'] = group + if netgroup and netgroup == cfg_netgroup: + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, 'netgroup-method')) + name_service_dict['netgroup'] = netgroup + if passwd and passwd == cfg_passwd: + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, 'passwd-method')) + name_service_dict['passwd'] = passwd + if shadow and shadow == cfg_shadow: + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, 'shadow-method')) + name_service_dict['shadow'] = shadow + if sudoers and sudoers == cfg_sudoers: + requests.append(self.get_delete_request(AAA_NAME_SERVICE_PATH, 'sudoers-method')) + name_service_dict['sudoers'] = sudoers + if name_service_dict: + config_dict['name_service'] = name_service_dict + commands = config_dict - def get_delete_authorization(self, attr): - url = AAA_AUTHORIZATION_PATH + return requests + def get_delete_request(self, url, attr): if attr: url += '/%s' % (attr) request = {'path': url, 'method': DELETE} return request - def remove_default_entries(self, data): - if data: - authentication = data.get('authentication') - if authentication: - console_auth_local = authentication.get('console_auth_local') - if console_auth_local is False: - data['authentication'].pop('console_auth_local') - if not data['authentication']: - data.pop('authentication') - def get_diff_aaa(self, want, have): """AAA module requires custom diff method due to overwritting of list in SONiC""" if not have: @@ -424,10 +443,12 @@ def get_diff_aaa(self, want, have): authentication_dict['auth_method'] = auth_method if console_auth_local is not None and console_auth_local != cfg_console_auth_local: authentication_dict['console_auth_local'] = console_auth_local - if failthrough and failthrough != cfg_failthrough: + if failthrough is not None and failthrough != cfg_failthrough: authentication_dict['failthrough'] = failthrough if authentication_dict: cfg_dict['authentication'] = authentication_dict + else: + cfg_dict['authentication'] = authentication # Authorization diff handling authorization = want.get('authorization') @@ -447,6 +468,41 @@ def get_diff_aaa(self, want, have): authorization_dict['login_auth_method'] = login_auth_method if authorization_dict: cfg_dict['authorization'] = authorization_dict + else: + cfg_dict['authorization'] = authorization + + # Name-service diff handling + name_service = want.get('name_service') + if name_service: + group = name_service.get('group') + netgroup = name_service.get('netgroup') + passwd = name_service.get('passwd') + shadow = name_service.get('shadow') + sudoers = name_service.get('sudoers') + + cfg_name_service = have.get('name_service') + if cfg_name_service: + name_service_dict = {} + cfg_group = cfg_name_service.get('group') + cfg_netgroup = cfg_name_service.get('netgroup') + cfg_passwd = cfg_name_service.get('passwd') + cfg_shadow = cfg_name_service.get('shadow') + cfg_sudoers = cfg_name_service.get('sudoers') + + if group and group != cfg_group: + name_service_dict['group'] = group + if netgroup and netgroup != cfg_netgroup: + name_service_dict['netgroup'] = netgroup + if passwd and passwd != cfg_passwd: + name_service_dict['passwd'] = passwd + if shadow and shadow != cfg_shadow: + name_service_dict['shadow'] = shadow + if sudoers and sudoers != cfg_sudoers: + name_service_dict['sudoers'] = sudoers + if name_service_dict: + cfg_dict['name_service'] = name_service_dict + else: + cfg_dict['name_service'] = name_service return cfg_dict @@ -456,19 +512,31 @@ def get_replaced_config(self, want, have): if want and have: authentication = want.get('authentication') authorization = want.get('authorization') + name_service = want.get('name_service') cfg_authentication = have.get('authentication') cfg_authorization = have.get('authorization') + cfg_name_service = have.get('name_service') if authentication != cfg_authentication: config_dict['authentication'] = cfg_authentication if authorization != cfg_authorization: config_dict['authorization'] = cfg_authorization + if name_service != cfg_name_service: + config_dict['name_service'] = cfg_name_service return config_dict def post_process_generated_config(self, data): if data: + authentication = data.get('authentication') authorization = data.get('authorization') + name_service = data.get('name_service') + + if authentication: + if 'auth_method' in authentication and not authentication['auth_method']: + data['authentication'].pop('auth_method') + if not data['authentication']: + data.pop('authentication') if authorization: if 'commands_auth_method' in authorization and not authorization['commands_auth_method']: data['authorization'].pop('commands_auth_method') @@ -476,3 +544,9 @@ def post_process_generated_config(self, data): data['authorization'].pop('login_auth_method') if not data['authorization']: data.pop('authorization') + if name_service: + for method in ('group', 'netgroup', 'passwd', 'shadow', 'sudoers'): + if method in name_service and not name_service[method]: + data['name_service'].pop(method) + if not data['name_service']: + data.pop('name_service') diff --git a/plugins/module_utils/network/sonic/facts/aaa/aaa.py b/plugins/module_utils/network/sonic/facts/aaa/aaa.py index 6f92b0275..8a2b6a3ae 100644 --- a/plugins/module_utils/network/sonic/facts/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/facts/aaa/aaa.py @@ -9,7 +9,7 @@ for a given resource, parsed, and the facts tree is populated based on the configuration. """ -from __future__ import (absolute_import, division, print_function) +from __future__ import absolute_import, division, print_function __metaclass__ = type from copy import deepcopy @@ -48,7 +48,7 @@ def __init__(self, module, subspec='config', options='options'): self.generated_spec = utils.generate_dict(facts_argument_spec) def populate_facts(self, connection, ansible_facts, data=None): - """ Populate the facts for qos_pfc + """ Populate the facts for aaa :param connection: the device connection :param ansible_facts: Facts dictionary :param data: previously collected conf @@ -68,7 +68,7 @@ def populate_facts(self, connection, ansible_facts, data=None): return ansible_facts def get_config(self, module, path, key_name): - """Retrive OC configuration from device""" + """Retrieve OC configuration from device""" cfg = None get_path = '%s/%s' % (AAA_PATH, path) request = {'path': get_path, 'method': 'get'} @@ -101,14 +101,14 @@ def update_aaa(self, module): if auth_method: authentication_dict['auth_method'] = auth_method - if console_auth_local: + if console_auth_local is not None: authentication_dict['console_auth_local'] = console_auth_local if failthrough: authentication_dict['failthrough'] = bool_dict[failthrough] if authentication_dict: config_dict['authentication'] = authentication_dict - # Authoriation configuration handling + # Authorization configuration handling authorization_dict = {} commands_auth_method = self.get_config(module, 'authorization/openconfig-aaa-tacacsplus-ext:commands/config/authorization-method', 'openconfig-aaa-tacacsplus-ext:authorization-method') @@ -122,4 +122,27 @@ def update_aaa(self, module): if authorization_dict: config_dict['authorization'] = authorization_dict + # Name-service configuration handling + name_service_cfg = self.get_config(module, 'openconfig-aaa-ext:name-service/config', 'openconfig-aaa-ext:config') + if name_service_cfg: + name_service_dict = {} + group = name_service_cfg.get('group-method') + netgroup = name_service_cfg.get('netgroup-method') + passwd = name_service_cfg.get('passwd-method') + shadow = name_service_cfg.get('shadow-method') + sudoers = name_service_cfg.get('sudoers-method') + + if group: + name_service_dict['group'] = group + if netgroup: + name_service_dict['netgroup'] = netgroup + if passwd: + name_service_dict['passwd'] = passwd + if shadow: + name_service_dict['shadow'] = shadow + if sudoers: + name_service_dict['sudoers'] = sudoers + if name_service_dict: + config_dict['name_service'] = name_service_dict + return config_dict diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index e4d03ac00..6a91c1f3c 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -63,7 +63,6 @@ description: Enable/disable local authentication on console type: bool - default: False failthrough: description: - Enable/disable failthrough @@ -86,6 +85,42 @@ type: list elements: str choices: ['ldap', 'local'] + name_service: + description: + - AAA name-service configuration + type: dict + version_added: 3.0.0 + suboptions: + group: + description: + - Name-service source for group method + type: list + elements: str + choices: ['ldap', 'local', 'login'] + netgroup: + description: + - Name-service source for netgroup method + type: list + elements: str + choices: ['ldap', 'local'] + passwd: + description: + - Name-service source for passwd method + type: list + elements: str + choices: ['ldap', 'local', 'login'] + shadow: + description: + - Name-service source for shadow method + type: list + elements: str + choices: ['ldap', 'local', 'login'] + sudoers: + description: + - Name-service source for sudoers method + type: list + elements: str + choices: ['ldap', 'local'] state: description: - The state of the configuration after module completion @@ -120,6 +155,17 @@ login_auth_method: - local - ldap + name_service: + group: + - ldap + netgroup: + - local + passwd: + - login + shadow: + - ldap + sudoers: + - local state: merged # After state: @@ -137,6 +183,14 @@ # --------------------------------------------------------- # login : local, ldap # commands : local, tacacs+ +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap +# netgroup-method : local +# passwd-method : login +# shadow-method : ldap +# sudoers-method : local # Using Replaced @@ -156,6 +210,14 @@ # --------------------------------------------------------- # login : local, ldap # commands : local, tacacs+ +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap +# netgroup-method : local +# passwd-method : login +# shadow-method : ldap +# sudoers-method : local - name: Replace AAA configuration dellemc.enterprise_sonic.sonic_aaa: @@ -166,6 +228,9 @@ authorization: commands_auth_method: - local + name_service: + group: + - ldap state: replaced # After state: @@ -182,6 +247,10 @@ # AAA Authorization Information # --------------------------------------------------------- # login : local +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap # Using Overridden @@ -201,6 +270,14 @@ # --------------------------------------------------------- # login : local, ldap # commands : local, tacacs+ +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap +# netgroup-method : local +# passwd-method : login +# shadow-method : ldap +# sudoers-method : local - name: Override AAA configuration dellemc.enterprise_sonic.sonic_aaa: @@ -241,6 +318,14 @@ # --------------------------------------------------------- # login : local, ldap # commands : local, tacacs+ +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap +# netgroup-method : local +# passwd-method : login +# shadow-method : ldap +# sudoers-method : local - name: Delete AAA individual attributes dellemc.enterprise_sonic.sonic_aaa: @@ -260,6 +345,17 @@ login_auth_method: - local - ldap + name_service: + group: + - ldap + netgroup: + - local + passwd: + - login + shadow: + - ldap + sudoers: + - local state: deleted # After state: @@ -286,6 +382,14 @@ # --------------------------------------------------------- # login : local, ldap # commands : local, tacacs+ +# --------------------------------------------------------- +# AAA Name-Service Information +# --------------------------------------------------------- +# group-method : ldap +# netgroup-method : local +# passwd-method : login +# shadow-method : ldap +# sudoers-method : local - name: Delete all AAA configuration dellemc.enterprise_sonic.sonic_aaa: diff --git a/tests/regression/roles/sonic_aaa/defaults/main.yml b/tests/regression/roles/sonic_aaa/defaults/main.yml index d10dc4767..f87a39824 100644 --- a/tests/regression/roles/sonic_aaa/defaults/main.yml +++ b/tests/regression/roles/sonic_aaa/defaults/main.yml @@ -22,6 +22,25 @@ tests: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local - name: test_case_02 description: Modify AAA configuration @@ -42,6 +61,20 @@ tests: login_auth_method: - ldap - local + name_service: + group: + - local + - login + netgroup: + - ldap + passwd: + - login + shadow: + - login + - local + - ldap + sudoers: + - local - name: test_case_03 description: Replace AAA configuration @@ -52,6 +85,10 @@ tests: authorization: login_auth_method: - local + name_service: + group: + - login + - name: test_case_04 description: Override AAA configuration @@ -72,6 +109,25 @@ tests: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local - name: test_case_05 description: Delete AAA individual attributes @@ -92,6 +148,25 @@ tests: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local - name: test_case_06 description: Add AAA configuration for delete all @@ -112,6 +187,25 @@ tests: login_auth_method: - ldap - local + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local - name: test_case_07 description: Delete all AAA configuratiom diff --git a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml index b2af1cabf..9d44f4f4a 100644 --- a/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml +++ b/tests/unit/modules/network/sonic/fixtures/sonic_aaa.yaml @@ -17,6 +17,25 @@ merged_01: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local existing_aaa_config: - path: '/data/openconfig-system:system/aaa/authentication/config' response: @@ -27,6 +46,9 @@ merged_01: - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' response: code: 200 + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + response: + code: 200 expected_config_requests: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'patch' @@ -53,6 +75,28 @@ merged_01: authorization-method: - local - ldap + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'patch' + data: + openconfig-aaa-ext:config: + group-method: + - ldap + - local + - login + netgroup-method: + - ldap + - local + passwd-method: + - ldap + - local + - login + shadow-method: + - ldap + - local + - login + sudoers-method: + - ldap + - local replaced_01: module_args: @@ -62,6 +106,9 @@ replaced_01: authorization: commands_auth_method: - local + name_service: + group: + - local state: replaced existing_aaa_config: - path: '/data/openconfig-system:system/aaa/authentication/config' @@ -91,6 +138,29 @@ replaced_01: openconfig-aaa-ext:authorization-method: - local - ldap + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + response: + code: 200 + value: + openconfig-aaa-ext:config: + group-method: + - ldap + - local + - login + netgroup-method: + - ldap + - local + passwd-method: + - ldap + - local + - login + shadow-method: + - ldap + - local + - login + sudoers-method: + - ldap + - local expected_config_requests: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'delete' @@ -98,6 +168,9 @@ replaced_01: - path: '/data/openconfig-system:system/aaa/authorization' method: 'delete' data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'delete' + data: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'patch' data: @@ -111,6 +184,12 @@ replaced_01: config: authorization-method: - local + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'patch' + data: + openconfig-aaa-ext:config: + group-method: + - local overridden_01: module_args: @@ -130,6 +209,25 @@ overridden_01: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local state: overridden existing_aaa_config: - path: '/data/openconfig-system:system/aaa/authentication/config' @@ -147,6 +245,13 @@ overridden_01: - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' response: code: 200 + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + response: + code: + value: + openconfig-aaa-ext:config: + group-method: + - local expected_config_requests: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'delete' @@ -154,6 +259,9 @@ overridden_01: - path: '/data/openconfig-system:system/aaa/authorization' method: 'delete' data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'delete' + data: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'patch' data: @@ -179,6 +287,28 @@ overridden_01: authorization-method: - local - ldap + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'patch' + data: + openconfig-aaa-ext:config: + group-method: + - ldap + - local + - login + netgroup-method: + - ldap + - local + passwd-method: + - ldap + - local + - login + shadow-method: + - ldap + - local + - login + sudoers-method: + - ldap + - local deleted_01: module_args: @@ -198,6 +328,25 @@ deleted_01: login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local state: deleted existing_aaa_config: - path: '/data/openconfig-system:system/aaa/authentication/config' @@ -227,6 +376,29 @@ deleted_01: openconfig-aaa-ext:authorization-method: - local - ldap + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + response: + code: 200 + value: + openconfig-aaa-ext:config: + group-method: + - ldap + - local + - login + netgroup-method: + - ldap + - local + passwd-method: + - ldap + - local + - login + shadow-method: + - ldap + - local + - login + sudoers-method: + - ldap + - local expected_config_requests: - path: '/data/openconfig-system:system/aaa/authentication/config/authentication-method' method: 'delete' @@ -243,6 +415,21 @@ deleted_01: - path: '/data/openconfig-system:system/aaa/authorization/openconfig-aaa-ext:login/config/authorization-method' method: 'delete' data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config/group-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config/netgroup-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config/passwd-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config/shadow-method' + method: 'delete' + data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config/sudoers-method' + method: 'delete' + data: deleted_02: module_args: @@ -275,6 +462,29 @@ deleted_02: openconfig-aaa-ext:authorization-method: - local - ldap + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + response: + code: 200 + value: + openconfig-aaa-ext:config: + group-method: + - ldap + - local + - login + netgroup-method: + - ldap + - local + passwd-method: + - ldap + - local + - login + shadow-method: + - ldap + - local + - login + sudoers-method: + - ldap + - local expected_config_requests: - path: '/data/openconfig-system:system/aaa/authentication/config' method: 'delete' @@ -282,3 +492,6 @@ deleted_02: - path: '/data/openconfig-system:system/aaa/authorization' method: 'delete' data: + - path: '/data/openconfig-system:system/aaa/openconfig-aaa-ext:name-service/config' + method: 'delete' + data: From 83fee7977acef286df63cab39e639ec0aca6c0bd Mon Sep 17 00:00:00 2001 From: shade Date: Tue, 25 Jun 2024 14:05:48 -0700 Subject: [PATCH 09/12] Update aaa playbook example --- playbooks/common_examples/sonic_aaa.yaml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/playbooks/common_examples/sonic_aaa.yaml b/playbooks/common_examples/sonic_aaa.yaml index 9019a809d..41d434e1e 100644 --- a/playbooks/common_examples/sonic_aaa.yaml +++ b/playbooks/common_examples/sonic_aaa.yaml @@ -52,6 +52,25 @@ login_auth_method: - local - ldap + name_service: + group: + - ldap + - local + - login + netgroup: + - ldap + - local + passwd: + - ldap + - local + - login + shadow: + - ldap + - local + - login + sudoers: + - ldap + - local state: merged - name: Merge tacacs configurations sonic_tacacs_server: From 7b348f584134b40883c392ba4f4ca02f7d709600 Mon Sep 17 00:00:00 2001 From: shade Date: Wed, 3 Jul 2024 13:32:09 -0700 Subject: [PATCH 10/12] Update get_replaced_config method --- .../network/sonic/config/aaa/aaa.py | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/plugins/module_utils/network/sonic/config/aaa/aaa.py b/plugins/module_utils/network/sonic/config/aaa/aaa.py index 761615c99..d7d1b97ef 100644 --- a/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -508,21 +508,19 @@ def get_diff_aaa(self, want, have): def get_replaced_config(self, want, have): config_dict = {} - - if want and have: - authentication = want.get('authentication') - authorization = want.get('authorization') - name_service = want.get('name_service') - cfg_authentication = have.get('authentication') - cfg_authorization = have.get('authorization') - cfg_name_service = have.get('name_service') - - if authentication != cfg_authentication: - config_dict['authentication'] = cfg_authentication - if authorization != cfg_authorization: - config_dict['authorization'] = cfg_authorization - if name_service != cfg_name_service: - config_dict['name_service'] = cfg_name_service + authentication = want.get('authentication') + authorization = want.get('authorization') + name_service = want.get('name_service') + cfg_authentication = have.get('authentication') + cfg_authorization = have.get('authorization') + cfg_name_service = have.get('name_service') + + if authentication and authentication != cfg_authentication: + config_dict['authentication'] = cfg_authentication + if authorization and authorization != cfg_authorization: + config_dict['authorization'] = cfg_authorization + if name_service and name_service != cfg_name_service: + config_dict['name_service'] = cfg_name_service return config_dict From 257e6418e70555fcbfa1d8009db21537b9b7b273 Mon Sep 17 00:00:00 2001 From: Shade Talabi Date: Tue, 23 Jul 2024 14:58:23 -0700 Subject: [PATCH 11/12] Address review comment --- plugins/module_utils/network/sonic/config/aaa/aaa.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/module_utils/network/sonic/config/aaa/aaa.py b/plugins/module_utils/network/sonic/config/aaa/aaa.py index d7d1b97ef..b794a959c 100644 --- a/plugins/module_utils/network/sonic/config/aaa/aaa.py +++ b/plugins/module_utils/network/sonic/config/aaa/aaa.py @@ -515,11 +515,11 @@ def get_replaced_config(self, want, have): cfg_authorization = have.get('authorization') cfg_name_service = have.get('name_service') - if authentication and authentication != cfg_authentication: + if authentication and cfg_authentication and authentication != cfg_authentication: config_dict['authentication'] = cfg_authentication - if authorization and authorization != cfg_authorization: + if authorization and cfg_authorization and authorization != cfg_authorization: config_dict['authorization'] = cfg_authorization - if name_service and name_service != cfg_name_service: + if name_service and cfg_name_service and name_service != cfg_name_service: config_dict['name_service'] = cfg_name_service return config_dict From a1def130350a820c19d2e6b00a45b05707a7ad6e Mon Sep 17 00:00:00 2001 From: Shade Talabi Date: Wed, 24 Jul 2024 09:42:43 -0700 Subject: [PATCH 12/12] Update config description --- plugins/modules/sonic_aaa.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/modules/sonic_aaa.py b/plugins/modules/sonic_aaa.py index 6a91c1f3c..129c63337 100644 --- a/plugins/modules/sonic_aaa.py +++ b/plugins/modules/sonic_aaa.py @@ -45,6 +45,8 @@ config: description: - AAA configuration + - For all lists in the module, the list items should be specified in order of desired priority. + - List items specified first have the highest priority. type: dict suboptions: authentication: