diff --git a/lib/ansible/modules/network/nxos/nxos_snmp_user.py b/lib/ansible/modules/network/nxos/nxos_snmp_user.py index 4264197abb..c98051e589 100644 --- a/lib/ansible/modules/network/nxos/nxos_snmp_user.py +++ b/lib/ansible/modules/network/nxos/nxos_snmp_user.py @@ -90,6 +90,7 @@ commands: sample: ["snmp-server user ntc network-operator auth md5 test_password"] ''' +import re from ansible.module_utils.network.nxos.nxos import load_config, run_commands from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args @@ -134,6 +135,7 @@ def get_snmp_groups(module): def get_snmp_user(user, module): command = 'show snmp user {0}'.format(user) body = execute_show_command(command, module, text=True) + body_text = body[0] if 'No such entry' not in body[0]: body = execute_show_command(command, module) @@ -177,28 +179,74 @@ def get_snmp_user(user, module): else: resource['encrypt'] = 'none' - group_table = resource_table[tablegrpkey][rowgrpkey] - groups = [] - try: - for group in group_table: - groups.append(str(group[grpkey]).strip()) - except TypeError: - groups.append(str(group_table[grpkey]).strip()) + if tablegrpkey in resource_table: + group_table = resource_table[tablegrpkey][rowgrpkey] + try: + for group in group_table: + groups.append(str(group[grpkey]).strip()) + except TypeError: + groups.append(str(group_table[grpkey]).strip()) - # Now for the platform bug case, get the groups - if isinstance(rt, list): - # remove 1st element from the list as this is parsed already - rt.pop(0) - # iterate through other elements indexed by - # 'user' and add it to groups. - for each in rt: - groups.append(each['user'].strip()) + # Now for the platform bug case, get the groups + if isinstance(rt, list): + # remove 1st element from the list as this is parsed already + rt.pop(0) + # iterate through other elements indexed by + # 'user' and add it to groups. + for each in rt: + groups.append(each['user'].strip()) + + # Some 'F' platforms use 'group' key instead + elif 'group' in resource_table: + # single group is a string, multiple groups in a list + groups = resource_table['group'] + if isinstance(groups, str): + groups = [groups] resource['group'] = groups except (KeyError, AttributeError, IndexError, TypeError): + if not resource and body_text and 'No such entry' not in body_text: + # 6K and other platforms may not return structured output; + # attempt to get state from text output + resource = get_non_structured_snmp_user(body_text) + + return resource + + +def get_non_structured_snmp_user(body_text): + # This method is a workaround for platforms that don't support structured + # output for 'show snmp user '. This workaround may not work on all + # platforms. Sample non-struct output: + # + # User Auth Priv(enforce) Groups acl_filter + # ____ ____ _____________ ______ __________ + # sample1 no no network-admin ipv4:my_acl + # network-operator + # priv-11 + # -OR- + # sample2 md5 des(no) priv-15 + # -OR- + # sample3 md5 aes-128(no) network-admin + resource = {} + output = body_text.rsplit('__________')[-1] + pat = re.compile(r'^(?P\S+)\s+' + r'(?P\S+)\s+' + r'(?P[\w\d-]+)(?P\([\w\d-]+\))*\s+' + r'(?P\S+)', + re.M) + m = re.search(pat, output) + if not m: return resource + resource['user'] = m.group('user') + resource['auth'] = m.group('auth') + resource['encrypt'] = 'aes-128' if 'aes' in str(m.group('priv')) else 'none' + + resource['group'] = [m.group('group')] + more_groups = re.findall(r'^\s+([\w\d-]+)\s*$', output, re.M) + if more_groups: + resource['group'] += more_groups return resource diff --git a/test/integration/targets/nxos_snmp_user/tests/common/sanity.yaml b/test/integration/targets/nxos_snmp_user/tests/common/sanity.yaml index b9a78b9f82..6ac8291b47 100644 --- a/test/integration/targets/nxos_snmp_user/tests/common/sanity.yaml +++ b/test/integration/targets/nxos_snmp_user/tests/common/sanity.yaml @@ -4,10 +4,21 @@ when: ansible_connection == "local" - name: Remove snmp user - nxos_snmp_user: &remove + nxos_snmp_user: &remove_snmp_user user: ntc provider: "{{ connection }}" state: absent + ignore_errors: yes + when: platform is not search('N5K|N6K|N9K-F') + +- name: Remove user workaround + # Some platforms will not allow snmp_user to remove the last role + nxos_user: &workaround_remove_user + name: ntc + provider: "{{ connection }}" + state: absent + ignore_errors: yes + when: platform is search('N5K|N6K|N9K-F') - pause: seconds: 5 @@ -64,27 +75,35 @@ - assert: *false - - name: delete snmp user - nxos_snmp_user: &remove1 - user: ntc - group: network-operator - provider: "{{ connection }}" - state: absent - register: result + - block: + # Some platforms will not allow snmp_user to remove the last role + - name: delete snmp user + nxos_snmp_user: &remove1 + user: ntc + group: network-operator + provider: "{{ connection }}" + state: absent + register: result - - assert: *true + - assert: *true - - pause: - seconds: 5 + - pause: + seconds: 5 - - name: "Remove Idempotence" - nxos_snmp_user: *remove1 - register: result + - name: "Remove Idempotence" + nxos_snmp_user: *remove1 + register: result - - assert: *false + - assert: *false + when: platform is not search('N5K|N6K|N9K-F') always: - name: delete snmp user - nxos_snmp_user: *remove + nxos_snmp_user: *remove_snmp_user + when: platform is not search('N5K|N6K|N9K-F') + + - name: remove user workaround + nxos_user: *workaround_remove_user + when: platform is search('N5K|N6K|N9K-F') - debug: msg="END connection={{ ansible_connection }} nxos_snmp_user sanity test"