fix nxos_aaa_server_host issues (#38188)

This commit is contained in:
saichint 2018-04-02 22:53:43 -07:00 committed by Trishna Guha
parent 35b7b608bf
commit 24cc6b80bd
3 changed files with 188 additions and 87 deletions

View file

@ -33,7 +33,7 @@ description:
author: Jason Edelman (@jedelman8) author: Jason Edelman (@jedelman8)
notes: notes:
- Tested against NXOSv 7.3.(0)D1(1) on VIRL - Tested against NXOSv 7.3.(0)D1(1) on VIRL
- Changes to the AAA server host key (shared secret) are not idempotent. - Changes to the host key (shared secret) are not idempotent for type 0.
- If C(state=absent) removes the whole host configuration. - If C(state=absent) removes the whole host configuration.
options: options:
server_type: server_type:
@ -47,7 +47,7 @@ options:
required: true required: true
key: key:
description: description:
- Shared secret for the specified host. - Shared secret for the specified host or keyword 'default'.
encrypt_type: encrypt_type:
description: description:
- The state of encryption applied to the entered key. - The state of encryption applied to the entered key.
@ -56,16 +56,17 @@ options:
choices: ['0', '7'] choices: ['0', '7']
host_timeout: host_timeout:
description: description:
- Timeout period for specified host, in seconds. Range is 1-60. - Timeout period for specified host, in seconds or keyword 'default.
Range is 1-60.
auth_port: auth_port:
description: description:
- Alternate UDP port for RADIUS authentication. - Alternate UDP port for RADIUS authentication or keyword 'default'.
acct_port: acct_port:
description: description:
- Alternate UDP port for RADIUS accounting. - Alternate UDP port for RADIUS accounting or keyword 'default'.
tacacs_port: tacacs_port:
description: description:
- Alternate TCP port TACACS Server. - Alternate TCP port TACACS Server or keyword 'default'.
state: state:
description: description:
- Manage the state of the resource. - Manage the state of the resource.
@ -147,13 +148,11 @@ from ansible.module_utils.network.nxos.nxos import get_capabilities, nxos_argume
from ansible.module_utils.basic import AnsibleModule from ansible.module_utils.basic import AnsibleModule
def execute_show_command(command, module, command_type='cli_show'): def execute_show_command(command, module):
device_info = get_capabilities(module) device_info = get_capabilities(module)
network_api = device_info.get('network_api', 'nxapi') network_api = device_info.get('network_api', 'nxapi')
if network_api == 'cliconf': if network_api == 'cliconf':
if 'show run' not in command:
command += ' | json'
cmds = [command] cmds = [command]
body = run_commands(module, cmds) body = run_commands(module, cmds)
elif network_api == 'nxapi': elif network_api == 'nxapi':
@ -173,40 +172,36 @@ def flatten_list(command_lists):
return flat_command_list return flat_command_list
def _match_dict(match_list, key_map):
no_blanks = []
match_dict = {}
for match_set in match_list:
match_set = tuple(v for v in match_set if v)
no_blanks.append(match_set)
for info in no_blanks:
words = info[0].strip().split()
length = len(words)
alt_key = key_map.get(words[0])
first = alt_key or words[0]
last = words[length - 1]
match_dict[first] = last.replace('\"', '')
return match_dict
def get_aaa_host_info(module, server_type, address): def get_aaa_host_info(module, server_type, address):
aaa_host_info = {} aaa_host_info = {}
command = 'show run | inc {0}-server.host.{1}'.format(server_type, address) command = 'show run | inc {0}-server.host.{1}'.format(server_type, address)
body = execute_show_command(command, module, command_type='cli_show_ascii') body = execute_show_command(command, module)[0]
if body:
if body[0]:
try: try:
pattern = (r'(acct-port \d+)|(timeout \d+)|(auth-port \d+)|' if 'radius' in body:
r'(key 7 "\w+")|( port \d+)') pattern = (r'\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+auth-port (\d+))?'
raw_match = re.findall(pattern, body[0]) r'(?:\s+acct-port (\d+))?(?:\s+authentication)?'
aaa_host_info = _match_dict(raw_match, {'acct-port': 'acct_port', r'(?:\s+accounting)?(?:\s+timeout (\d+))?')
'auth-port': 'auth_port', match = re.search(pattern, body)
'port': 'tacacs_port', aaa_host_info['key'] = match.group(1)
'timeout': 'host_timeout'}) if aaa_host_info['key']:
aaa_host_info['key'] = aaa_host_info['key'].replace('"', '')
aaa_host_info['encrypt_type'] = '7'
aaa_host_info['auth_port'] = match.group(2)
aaa_host_info['acct_port'] = match.group(3)
aaa_host_info['host_timeout'] = match.group(4)
elif 'tacacs' in body:
pattern = (r'\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+port (\d+))?'
r'(?:\s+timeout (\d+))?')
match = re.search(pattern, body)
aaa_host_info['key'] = match.group(1)
if aaa_host_info['key']:
aaa_host_info['key'] = aaa_host_info['key'].replace('"', '')
aaa_host_info['encrypt_type'] = '7'
aaa_host_info['tacacs_port'] = match.group(2)
aaa_host_info['host_timeout'] = match.group(3)
aaa_host_info['server_type'] = server_type aaa_host_info['server_type'] = server_type
aaa_host_info['address'] = address aaa_host_info['address'] = address
except TypeError: except TypeError:
@ -217,35 +212,41 @@ def get_aaa_host_info(module, server_type, address):
return aaa_host_info return aaa_host_info
def config_aaa_host(server_type, address, params, clear=False): def config_aaa_host(server_type, address, params, existing):
cmds = [] cmds = []
if clear:
cmds.append('no {0}-server host {1}'.format(server_type, address))
cmd_str = '{0}-server host {1}'.format(server_type, address) cmd_str = '{0}-server host {1}'.format(server_type, address)
cmd_no_str = 'no ' + cmd_str
key = params.get('key') key = params.get('key')
enc_type = params.get('encrypt_type', '') enc_type = params.get('encrypt_type', '')
host_timeout = params.get('host_timeout')
auth_port = params.get('auth_port')
acct_port = params.get('acct_port')
port = params.get('tacacs_port')
if auth_port: defval = False
cmd_str += ' auth-port {0}'.format(auth_port) nondef = False
if acct_port:
cmd_str += ' acct-port {0}'.format(acct_port)
if port:
cmd_str += ' port {0}'.format(port)
if host_timeout:
cmd_str += ' timeout {0}'.format(host_timeout)
if key: if key:
cmds.append('{0}-server host {1} key {2} {3}'.format(server_type, if key != 'default':
address, cmds.append(cmd_str + ' key {0} {1}'.format(enc_type, key))
enc_type, key)) else:
cmds.append(cmd_no_str + ' key 7 {0}'.format(existing.get('key')))
locdict = {'auth_port': 'auth-port', 'acct_port': 'acct-port',
'tacacs_port': 'port', 'host_timeout': 'timeout'}
# platform CLI needs the keywords in the following order
for key in ['auth_port', 'acct_port', 'tacacs_port', 'host_timeout']:
item = params.get(key)
if item:
if item != 'default':
cmd_str += ' {0} {1}'.format(locdict.get(key), item)
nondef = True
else:
cmd_no_str += ' {0} 1'.format(locdict.get(key))
defval = True
if defval:
cmds.append(cmd_no_str)
if nondef or not existing:
cmds.append(cmd_str)
cmds.append(cmd_str)
return cmds return cmds
@ -302,24 +303,19 @@ def main():
end_state = existing end_state = existing
commands = [] commands = []
delta = {}
if state == 'present': if state == 'present':
host_timeout = proposed.get('host_timeout') if not existing:
if host_timeout: delta = proposed
try: else:
if int(host_timeout) < 1 or int(host_timeout) > 60: for key, value in proposed.items():
raise ValueError if value != existing.get(key):
except ValueError: if value != 'default' or existing.get(key):
module.fail_json( delta[key] = value
msg='host_timeout must be an integer between 1 and 60')
delta = dict( command = config_aaa_host(server_type, address, delta, existing)
set(proposed.items()).difference(existing.items())) if command:
if delta: commands.append(command)
union = existing.copy()
union.update(delta)
command = config_aaa_host(server_type, address, union)
if command:
commands.append(command)
elif state == 'absent': elif state == 'absent':
intersect = dict( intersect = dict(

View file

@ -43,7 +43,7 @@
register: result register: result
- assert: *false - assert: *false
- name: "Configure radius server non defaults" - name: "Configure radius server non defaults"
nxos_aaa_server_host: &configure_radius_non_default nxos_aaa_server_host: &configure_radius_non_default
server_type: radius server_type: radius
@ -54,7 +54,7 @@
state: present state: present
provider: "{{ connection }}" provider: "{{ connection }}"
register: result register: result
- assert: *true - assert: *true
- name: "Check Idempotence" - name: "Check Idempotence"
@ -63,25 +63,38 @@
- assert: *false - assert: *false
- name: "Remove radius server configuration" - name: "Configure some defaults on radius server"
nxos_aaa_server_host: *remove nxos_aaa_server_host: &configure_some_radius_default
server_type: radius
address: 8.8.8.8
host_timeout: default
auth_port: 1000
acct_port: default
state: present
provider: "{{ connection }}"
register: result register: result
- assert: *true - assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_some_radius_default
register: result
- assert: *false
- name: "Configure radius server with clear text pwd" - name: "Configure radius server with clear text pwd"
nxos_aaa_server_host: &configure_radius_clear_text nxos_aaa_server_host: &configure_radius_clear_text
server_type: radius server_type: radius
address: 8.8.8.8 address: 8.8.8.8
host_timeout: 25 host_timeout: 25
auth_port: 2083 auth_port: default
acct_port: 2084 acct_port: 2084
encrypt_type: 0 encrypt_type: 0
key: hello key: hello
state: present state: present
provider: "{{ connection }}" provider: "{{ connection }}"
register: result register: result
- assert: *true - assert: *true
- name: "Check NOT Idempotent" - name: "Check NOT Idempotent"
@ -115,8 +128,49 @@
nxos_aaa_server_host: *configure_radius_type7 nxos_aaa_server_host: *configure_radius_type7
register: result register: result
- assert: *false
- name: "Configure radius server with default key"
nxos_aaa_server_host: &configure_radius_defkey
server_type: radius
address: 8.8.8.8
host_timeout: default
auth_port: 1000
acct_port: default
encrypt_type: 7
key: default
state: present
provider: "{{ connection }}"
register: result
- assert: *true - assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_radius_defkey
register: result
- assert: *false
- name: "Configure radius server with all def"
nxos_aaa_server_host: &configure_radius_alldef
server_type: radius
address: 8.8.8.8
host_timeout: default
auth_port: default
acct_port: default
key: default
state: present
provider: "{{ connection }}"
register: result
- assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_radius_alldef
register: result
- assert: *false
rescue: rescue:
- debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected" - debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected"
@ -127,4 +181,4 @@
nxos_aaa_server_host: *remove nxos_aaa_server_host: *remove
register: result register: result
- debug: msg="END connection={{ ansible_connection }} nxos_aaa_server_host radius.yaml sanity test" - debug: msg="END connection={{ ansible_connection }} nxos_aaa_server_host radius.yaml sanity test"

View file

@ -60,7 +60,7 @@
state: present state: present
provider: "{{ connection }}" provider: "{{ connection }}"
register: result register: result
- assert: *true - assert: *true
- name: "Check Idempotence" - name: "Check Idempotence"
@ -69,18 +69,30 @@
- assert: *false - assert: *false
- name: "Remove tacacs server configuration" - name: "Configure some defaults on tacacs server"
nxos_aaa_server_host: *remove nxos_aaa_server_host: &configure_some_tacacs_default
server_type: tacacs
address: 8.8.8.8
host_timeout: default
tacacs_port: 100
state: present
provider: "{{ connection }}"
register: result register: result
- assert: *true - assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_some_tacacs_default
register: result
- assert: *false
- name: "Configure tacacs server with clear text pwd" - name: "Configure tacacs server with clear text pwd"
nxos_aaa_server_host: &configure_tacacs_clear_text nxos_aaa_server_host: &configure_tacacs_clear_text
server_type: tacacs server_type: tacacs
address: 8.8.8.8 address: 8.8.8.8
host_timeout: 25 host_timeout: 25
tacacs_port: 89 tacacs_port: default
encrypt_type: 0 encrypt_type: 0
key: hello key: hello
state: present state: present
@ -119,8 +131,47 @@
nxos_aaa_server_host: *configure_tacacs_type7 nxos_aaa_server_host: *configure_tacacs_type7
register: result register: result
- assert: *false
- name: "Configure tacacs server with default key"
nxos_aaa_server_host: &configure_tacacs_defkey
server_type: tacacs
address: 8.8.8.8
host_timeout: default
tacacs_port: 89
encrypt_type: 7
key: default
state: present
provider: "{{ connection }}"
register: result
- assert: *true - assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_tacacs_defkey
register: result
- assert: *false
- name: "Configure tacacs server with all def"
nxos_aaa_server_host: &configure_tacacs_alldef
server_type: tacacs
address: 8.8.8.8
host_timeout: default
tacacs_port: default
key: default
state: present
provider: "{{ connection }}"
register: result
- assert: *true
- name: "Check Idempotence"
nxos_aaa_server_host: *configure_tacacs_alldef
register: result
- assert: *false
rescue: rescue:
- debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected" - debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected"
@ -131,7 +182,7 @@
nxos_aaa_server_host: *remove nxos_aaa_server_host: *remove
register: result register: result
- name: "Enable feature tacacs+" - name: "Disable feature tacacs+"
nxos_feature: nxos_feature:
feature: tacacs+ feature: tacacs+
state: disabled state: disabled