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
This commit is contained in:
parent
bd47e64bc7
commit
8c56c116e5
2 changed files with 98 additions and 31 deletions
|
@ -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 <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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue