diff options
3 files changed, 190 insertions, 89 deletions
diff --git a/lib/ansible/modules/network/nxos/nxos_aaa_server_host.py b/lib/ansible/modules/network/nxos/nxos_aaa_server_host.py index 8cfb2951cd..0c1d6bc6f4 100644 --- a/lib/ansible/modules/network/nxos/nxos_aaa_server_host.py +++ b/lib/ansible/modules/network/nxos/nxos_aaa_server_host.py @@ -33,7 +33,7 @@ description: author: Jason Edelman (@jedelman8) notes: - 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. options: server_type: @@ -47,7 +47,7 @@ options: required: true key: description: - - Shared secret for the specified host. + - Shared secret for the specified host or keyword 'default'. encrypt_type: description: - The state of encryption applied to the entered key. @@ -56,16 +56,17 @@ options: choices: ['0', '7'] host_timeout: 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: description: - - Alternate UDP port for RADIUS authentication. + - Alternate UDP port for RADIUS authentication or keyword 'default'. acct_port: description: - - Alternate UDP port for RADIUS accounting. + - Alternate UDP port for RADIUS accounting or keyword 'default'. tacacs_port: description: - - Alternate TCP port TACACS Server. + - Alternate TCP port TACACS Server or keyword 'default'. state: description: - 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 -def execute_show_command(command, module, command_type='cli_show'): +def execute_show_command(command, module): device_info = get_capabilities(module) network_api = device_info.get('network_api', 'nxapi') if network_api == 'cliconf': - if 'show run' not in command: - command += ' | json' cmds = [command] body = run_commands(module, cmds) elif network_api == 'nxapi': @@ -173,40 +172,36 @@ def flatten_list(command_lists): 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): aaa_host_info = {} command = 'show run | inc {0}-server.host.{1}'.format(server_type, address) - body = execute_show_command(command, module, command_type='cli_show_ascii') - - if body[0]: + body = execute_show_command(command, module)[0] + if body: try: - pattern = (r'(acct-port \d+)|(timeout \d+)|(auth-port \d+)|' - r'(key 7 "\w+")|( port \d+)') - raw_match = re.findall(pattern, body[0]) - aaa_host_info = _match_dict(raw_match, {'acct-port': 'acct_port', - 'auth-port': 'auth_port', - 'port': 'tacacs_port', - 'timeout': 'host_timeout'}) + if 'radius' in body: + pattern = (r'\S+ host \S+(?:\s+key 7\s+(\S+))?(?:\s+auth-port (\d+))?' + r'(?:\s+acct-port (\d+))?(?:\s+authentication)?' + r'(?:\s+accounting)?(?:\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['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['address'] = address except TypeError: @@ -217,35 +212,41 @@ def get_aaa_host_info(module, server_type, address): return aaa_host_info -def config_aaa_host(server_type, address, params, clear=False): +def config_aaa_host(server_type, address, params, existing): 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_no_str = 'no ' + cmd_str key = params.get('key') 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: - cmd_str += ' auth-port {0}'.format(auth_port) - 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) + + defval = False + nondef = False + if key: - cmds.append('{0}-server host {1} key {2} {3}'.format(server_type, - address, - enc_type, key)) + if key != 'default': + cmds.append(cmd_str + ' key {0} {1}'.format(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 @@ -302,24 +303,19 @@ def main(): end_state = existing commands = [] + delta = {} if state == 'present': - host_timeout = proposed.get('host_timeout') - if host_timeout: - try: - if int(host_timeout) < 1 or int(host_timeout) > 60: - raise ValueError - except ValueError: - module.fail_json( - msg='host_timeout must be an integer between 1 and 60') - - delta = dict( - set(proposed.items()).difference(existing.items())) - if delta: - union = existing.copy() - union.update(delta) - command = config_aaa_host(server_type, address, union) - if command: - commands.append(command) + if not existing: + delta = proposed + else: + for key, value in proposed.items(): + if value != existing.get(key): + if value != 'default' or existing.get(key): + delta[key] = value + + command = config_aaa_host(server_type, address, delta, existing) + if command: + commands.append(command) elif state == 'absent': intersect = dict( diff --git a/test/integration/targets/nxos_aaa_server_host/tests/common/radius.yaml b/test/integration/targets/nxos_aaa_server_host/tests/common/radius.yaml index 572650e97e..cf9a65556b 100644 --- a/test/integration/targets/nxos_aaa_server_host/tests/common/radius.yaml +++ b/test/integration/targets/nxos_aaa_server_host/tests/common/radius.yaml @@ -43,7 +43,7 @@ register: result - assert: *false - + - name: "Configure radius server non defaults" nxos_aaa_server_host: &configure_radius_non_default server_type: radius @@ -54,7 +54,7 @@ state: present provider: "{{ connection }}" register: result - + - assert: *true - name: "Check Idempotence" @@ -63,25 +63,38 @@ - assert: *false - - name: "Remove radius server configuration" - nxos_aaa_server_host: *remove + - name: "Configure some defaults on radius server" + 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 - 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" nxos_aaa_server_host: &configure_radius_clear_text server_type: radius address: 8.8.8.8 host_timeout: 25 - auth_port: 2083 + auth_port: default acct_port: 2084 encrypt_type: 0 key: hello state: present provider: "{{ connection }}" register: result - + - assert: *true - name: "Check NOT Idempotent" @@ -115,8 +128,49 @@ nxos_aaa_server_host: *configure_radius_type7 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 + + - 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: - debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected" @@ -127,4 +181,4 @@ nxos_aaa_server_host: *remove 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" diff --git a/test/integration/targets/nxos_aaa_server_host/tests/common/tacacs.yaml b/test/integration/targets/nxos_aaa_server_host/tests/common/tacacs.yaml index 4229f9affb..6307316734 100644 --- a/test/integration/targets/nxos_aaa_server_host/tests/common/tacacs.yaml +++ b/test/integration/targets/nxos_aaa_server_host/tests/common/tacacs.yaml @@ -60,7 +60,7 @@ state: present provider: "{{ connection }}" register: result - + - assert: *true - name: "Check Idempotence" @@ -69,18 +69,30 @@ - assert: *false - - name: "Remove tacacs server configuration" - nxos_aaa_server_host: *remove + - name: "Configure some defaults on tacacs server" + 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 - 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" nxos_aaa_server_host: &configure_tacacs_clear_text server_type: tacacs address: 8.8.8.8 host_timeout: 25 - tacacs_port: 89 + tacacs_port: default encrypt_type: 0 key: hello state: present @@ -119,8 +131,47 @@ nxos_aaa_server_host: *configure_tacacs_type7 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 + + - 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: - debug: msg="connection={{ ansible_connection }} nxos_aaa_server_host failure detected" @@ -131,7 +182,7 @@ nxos_aaa_server_host: *remove register: result - - name: "Enable feature tacacs+" + - name: "Disable feature tacacs+" nxos_feature: feature: tacacs+ state: disabled |