nxos_snmp_user: platform fixes for get_snmp_user (#55832)
* nxos_snmp_user: platform fixes for get_snmp_user
snmp user output behavior varies quite a bit for the different nxos platforms and required several workarounds:
- N5K/N6k
- These platforms do not support structured output for `show snmp user`.
- The current code lands in an `except` clause when the output is not structured; so I added a new `get_non_structured_snmp_user` method to scrape the state from the regular cli output if it's present.
- N9K-F
- The `group` data in the JSON output is different for this platform; it has a different key (just `group` instead of `TABLE_groups` or `group_names`) and it is not indexed
- For a single group the value is a string, for multiple groups it's a list
- sanity
- N5K/N6K/N9K-F platforms will reject `no snmp user <name> <role>` when it's the last role defined for the user.
- workaround is to use `nxos_user` to remove the user
- Changes validated on:
- `N3K, N3K-F, N35, N6K, N7K, N9K, N9K-F`
- `6.0(2)A8`
- `7.0(3)I2, 7.0(3)I4, 7.0(3)I5, 7.0(3)I6, 7.0(3)I7`
- `7.3(2)D1`
- `7.3(3)N1, 7.3(4)N1`
- `8.3(2)`
- `9.2(2), 9.2(3)`
* fix lint warning
(cherry picked from commit 8c56c116e5
)
This commit is contained in:
parent
f279ce5209
commit
bf8a838702
3 changed files with 100 additions and 31 deletions
2
changelogs/fragments/nxos_snmp_user28.yaml
Normal file
2
changelogs/fragments/nxos_snmp_user28.yaml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
bugfixes:
|
||||||
|
- nxos_snmp_user fix platform fixes for get_snmp_user (https://github.com/ansible/ansible/pull/55832).
|
|
@ -90,6 +90,7 @@ commands:
|
||||||
sample: ["snmp-server user ntc network-operator auth md5 test_password"]
|
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 load_config, run_commands
|
||||||
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args
|
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):
|
def get_snmp_user(user, module):
|
||||||
command = 'show snmp user {0}'.format(user)
|
command = 'show snmp user {0}'.format(user)
|
||||||
body = execute_show_command(command, module, text=True)
|
body = execute_show_command(command, module, text=True)
|
||||||
|
body_text = body[0]
|
||||||
|
|
||||||
if 'No such entry' not in body[0]:
|
if 'No such entry' not in body[0]:
|
||||||
body = execute_show_command(command, module)
|
body = execute_show_command(command, module)
|
||||||
|
@ -177,9 +179,9 @@ def get_snmp_user(user, module):
|
||||||
else:
|
else:
|
||||||
resource['encrypt'] = 'none'
|
resource['encrypt'] = 'none'
|
||||||
|
|
||||||
group_table = resource_table[tablegrpkey][rowgrpkey]
|
|
||||||
|
|
||||||
groups = []
|
groups = []
|
||||||
|
if tablegrpkey in resource_table:
|
||||||
|
group_table = resource_table[tablegrpkey][rowgrpkey]
|
||||||
try:
|
try:
|
||||||
for group in group_table:
|
for group in group_table:
|
||||||
groups.append(str(group[grpkey]).strip())
|
groups.append(str(group[grpkey]).strip())
|
||||||
|
@ -195,11 +197,57 @@ def get_snmp_user(user, module):
|
||||||
for each in rt:
|
for each in rt:
|
||||||
groups.append(each['user'].strip())
|
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
|
resource['group'] = groups
|
||||||
|
|
||||||
except (KeyError, AttributeError, IndexError, TypeError):
|
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
|
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 <foo>'. 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<user>\S+)\s+'
|
||||||
|
r'(?P<auth>\S+)\s+'
|
||||||
|
r'(?P<priv>[\w\d-]+)(?P<enforce>\([\w\d-]+\))*\s+'
|
||||||
|
r'(?P<group>\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
|
return resource
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,21 @@
|
||||||
when: ansible_connection == "local"
|
when: ansible_connection == "local"
|
||||||
|
|
||||||
- name: Remove snmp user
|
- name: Remove snmp user
|
||||||
nxos_snmp_user: &remove
|
nxos_snmp_user: &remove_snmp_user
|
||||||
user: ntc
|
user: ntc
|
||||||
provider: "{{ connection }}"
|
provider: "{{ connection }}"
|
||||||
state: absent
|
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:
|
- pause:
|
||||||
seconds: 5
|
seconds: 5
|
||||||
|
@ -64,6 +75,8 @@
|
||||||
|
|
||||||
- assert: *false
|
- assert: *false
|
||||||
|
|
||||||
|
- block:
|
||||||
|
# Some platforms will not allow snmp_user to remove the last role
|
||||||
- name: delete snmp user
|
- name: delete snmp user
|
||||||
nxos_snmp_user: &remove1
|
nxos_snmp_user: &remove1
|
||||||
user: ntc
|
user: ntc
|
||||||
|
@ -82,9 +95,15 @@
|
||||||
register: result
|
register: result
|
||||||
|
|
||||||
- assert: *false
|
- assert: *false
|
||||||
|
when: platform is not search('N5K|N6K|N9K-F')
|
||||||
|
|
||||||
always:
|
always:
|
||||||
- name: delete snmp user
|
- 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"
|
- debug: msg="END connection={{ ansible_connection }} nxos_snmp_user sanity test"
|
||||||
|
|
Loading…
Reference in a new issue