From ad0a01ffb5f84755ad914858f957ed5c74166433 Mon Sep 17 00:00:00 2001 From: Trishna Guha Date: Fri, 5 May 2017 15:38:30 +0530 Subject: [PATCH] nxos_vrf refactor (#24280) * nxos_vrf refactor Signed-off-by: Trishna Guha * Unit test for nxos_vrf Remove unnecessary keys() method Signed-off-by: Trishna Guha --- lib/ansible/modules/network/nxos/nxos_vrf.py | 205 +++++++----------- .../modules/network/nxos/test_nxos_vrf.py | 64 ++++++ 2 files changed, 142 insertions(+), 127 deletions(-) create mode 100644 test/units/modules/network/nxos/test_nxos_vrf.py diff --git a/lib/ansible/modules/network/nxos/nxos_vrf.py b/lib/ansible/modules/network/nxos/nxos_vrf.py index 3d25fb4f1c3..1acfcf7af19 100644 --- a/lib/ansible/modules/network/nxos/nxos_vrf.py +++ b/lib/ansible/modules/network/nxos/nxos_vrf.py @@ -16,9 +16,11 @@ # along with Ansible. If not, see . # -ANSIBLE_METADATA = {'metadata_version': '1.0', - 'status': ['preview'], - 'supported_by': 'community'} +ANSIBLE_METADATA = { + 'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'community' +} DOCUMENTATION = ''' @@ -28,96 +30,71 @@ extends_documentation_fragment: nxos version_added: "2.1" short_description: Manages global VRF configuration. description: - - Manages global VRF configuration. + - Manages global VRF configuration. author: - - Jason Edelman (@jedelman8) - - Gabriele Gerbino (@GGabriele) + - Jason Edelman (@jedelman8) + - Gabriele Gerbino (@GGabriele) notes: - - Cisco NX-OS creates the default VRF by itself. Therefore, - you're not allowed to use default as I(vrf) name in this module. - - C(vrf) name must be shorter than 32 chars. - - VRF names are not case sensible in NX-OS. Anyway, the name is stored - just like it's inserted by the user and it'll not be changed again - unless the VRF is removed and re-created. i.e. C(vrf=NTC) will create - a VRF named NTC, but running it again with C(vrf=ntc) will not cause - a configuration change. + - Cisco NX-OS creates the default VRF by itself. Therefore, + you're not allowed to use default as I(vrf) name in this module. + - C(vrf) name must be shorter than 32 chars. + - VRF names are not case sensible in NX-OS. Anyway, the name is stored + just like it's inserted by the user and it'll not be changed again + unless the VRF is removed and re-created. i.e. C(vrf=NTC) will create + a VRF named NTC, but running it again with C(vrf=ntc) will not cause + a configuration change. options: - vrf: - description: - - Name of VRF to be managed. - required: true - admin_state: - description: - - Administrative state of the VRF. - required: false - default: up - choices: ['up','down'] - vni: - description: - - Specify virtual network identifier. Valid values are Integer - or keyword 'default'. - required: false - default: null - version_added: "2.2" - route_distinguisher: - description: - - VPN Route Distinguisher (RD). Valid values are a string in - one of the route-distinguisher formats (ASN2:NN, ASN4:NN, or - IPV4:NN); the keyword 'auto', or the keyword 'default'. - required: false - default: null - version_added: "2.2" - state: - description: - - Manages desired state of the resource. - required: false - default: present - choices: ['present','absent'] + vrf: description: - description: - - Description of the VRF. - required: false - default: null + - Name of VRF to be managed. + required: true + admin_state: + description: + - Administrative state of the VRF. + required: false + default: up + choices: ['up','down'] + vni: + description: + - Specify virtual network identifier. Valid values are Integer + or keyword 'default'. + required: false + default: null + version_added: "2.2" + route_distinguisher: + description: + - VPN Route Distinguisher (RD). Valid values are a string in + one of the route-distinguisher formats (ASN2:NN, ASN4:NN, or + IPV4:NN); the keyword 'auto', or the keyword 'default'. + required: false + default: null + version_added: "2.2" + state: + description: + - Manages desired state of the resource. + required: false + default: present + choices: ['present','absent'] + description: + description: + - Description of the VRF. + required: false + default: null ''' EXAMPLES = ''' - name: Ensure ntc VRF exists on switch nxos_vrf: vrf: ntc - username: "{{ un }}" - password: "{{ pwd }}" - host: "{{ inventory_hostname }}" + state: present ''' RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: always - type: dict - sample: {"admin_state": "Up", "description": "Test test", - "vrf": "ntc"} -existing: - description: k/v pairs of existing vrf - returned: always - type: dict - sample: {"admin_state": "Up", "description": "Old test", - "vrf": "old_ntc"} -end_state: - description: k/v pairs of vrf info after module execution - returned: always - type: dict - sample: {"admin_state": "Up", "description": "Test test", - "vrf": "ntc"} -updates: +commands: description: commands sent to the device returned: always type: list sample: ["vrf context ntc", "shutdown"] -changed: - description: check to see if a change was made on the device - returned: always - type: boolean - sample: true ''' import re @@ -139,14 +116,10 @@ def execute_show_command(command, module): def apply_key_map(key_map, table): new_dict = {} - for key, value in table.items(): + for key in table: new_key = key_map.get(key) if new_key: - value = table.get(key) - if value: - new_dict[new_key] = str(value) - else: - new_dict[new_key] = value + new_dict[new_key] = str(table.get(key)) return new_dict @@ -177,29 +150,29 @@ def get_vrf_description(vrf, module): description = '' descr_regex = r".*description\s(?P[\S+\s]+).*" - body = execute_show_command(command, module) try: - body = body[0] - splitted_body = body.split('\n') - except (AttributeError, IndexError): + body = execute_show_command(command, module)[0] + except IndexError: return description - for element in splitted_body: - if 'description' in element: - match_description = re.match(descr_regex, element, - re.DOTALL) - group_description = match_description.groupdict() - description = group_description["descr"] + if body: + splitted_body = body.split('\n') + for element in splitted_body: + if 'description' in element: + match_description = re.match(descr_regex, element, + re.DOTALL) + group_description = match_description.groupdict() + description = group_description["descr"] return description def get_value(arg, config, module): - REGEX = re.compile(r'(?:{0}\s)(?P.*)$'.format(arg), re.M) + extra_arg_regex = re.compile(r'(?:{0}\s)(?P.*)$'.format(arg), re.M) value = '' if arg in config: - value = REGEX.search(config).group('value') + value = extra_arg_regex.search(config).group('value') return value @@ -210,19 +183,19 @@ def get_vrf(vrf, module): 'vrf_state': 'admin_state' } - body = execute_show_command(command, module) try: - vrf_table = body[0]['TABLE_vrf']['ROW_vrf'] + body = execute_show_command(command, module)[0] + vrf_table = body['TABLE_vrf']['ROW_vrf'] except (TypeError, IndexError): return {} parsed_vrf = apply_key_map(vrf_key, vrf_table) command = 'show run all | section vrf.context.{0}'.format(vrf) - body = execute_show_command(command, module) + body = execute_show_command(command, module)[0] extra_params = ['vni', 'rd', 'description'] for param in extra_params: - parsed_vrf[param] = get_value(param, body[0], module) + parsed_vrf[param] = get_value(param, body, module) return parsed_vrf @@ -233,10 +206,8 @@ def main(): description=dict(default=None, required=False), vni=dict(required=False, type='str'), rd=dict(required=False, type='str'), - admin_state=dict(default='up', choices=['up', 'down'], - required=False), - state=dict(default='present', choices=['present', 'absent'], - required=False), + admin_state=dict(default='up', choices=['up', 'down'], required=False), + state=dict(default='present', choices=['present', 'absent'], required=False), include_defaults=dict(default=False), config=dict(), save=dict(type='bool', default=False) @@ -244,11 +215,11 @@ def main(): argument_spec.update(nxos_argument_spec) - module = AnsibleModule(argument_spec=argument_spec, - supports_check_mode=True) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) warnings = list() check_args(module, warnings) + results = dict(changed=False, warnings=warnings) vrf = module.params['vrf'] @@ -261,27 +232,15 @@ def main(): if vrf == 'default': module.fail_json(msg='cannot use default as name of a VRF') elif len(vrf) > 32: - module.fail_json(msg='VRF name exceeded max length of 32', - vrf=vrf) + module.fail_json(msg='VRF name exceeded max length of 32', vrf=vrf) existing = get_vrf(vrf, module) args = dict(vrf=vrf, description=description, vni=vni, admin_state=admin_state, rd=rd) - end_state = existing - changed = False proposed = dict((k, v) for k, v in args.items() if v is not None) - """Since 'admin_state' is either 'Up' or 'Down' from outputs, - we use the following to make sure right letter case is used so that delta - results will be consistent to the actual configuration.""" - if existing: - if existing['admin_state'].lower() == admin_state: - proposed['admin_state'] = existing['admin_state'] - delta = dict(set(proposed.items()).difference(existing.items())) - changed = False - end_state = existing commands = [] if state == 'absent': if existing: @@ -304,22 +263,14 @@ def main(): module.exit_json(changed=True, commands=commands) else: load_config(module, commands) - changed = True - end_state = get_vrf(vrf, module) + results['changed'] = True if 'configure' in commands: commands.pop(0) - results = {} - results['proposed'] = proposed - results['existing'] = existing - results['end_state'] = end_state - results['updates'] = commands - results['changed'] = changed - results['warnings'] = warnings + results['commands'] = commands module.exit_json(**results) if __name__ == '__main__': main() - diff --git a/test/units/modules/network/nxos/test_nxos_vrf.py b/test/units/modules/network/nxos/test_nxos_vrf.py new file mode 100644 index 00000000000..174fa1cd539 --- /dev/null +++ b/test/units/modules/network/nxos/test_nxos_vrf.py @@ -0,0 +1,64 @@ +# (c) 2016 Red Hat Inc. +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import json + +from ansible.compat.tests.mock import patch +from ansible.modules.network.nxos import nxos_vrf +from .nxos_module import TestNxosModule, load_fixture, set_module_args + + +class TestNxosVrfModule(TestNxosModule): + + module = nxos_vrf + + def setUp(self): + self.mock_run_commands = patch('ansible.modules.network.nxos.nxos_vrf.run_commands') + self.run_commands = self.mock_run_commands.start() + + self.mock_load_config = patch('ansible.modules.network.nxos.nxos_vrf.load_config') + self.load_config = self.mock_load_config.start() + + self.mock_get_config = patch('ansible.modules.network.nxos.nxos_vrf.get_config') + self.get_config = self.mock_get_config.start() + + def tearDown(self): + self.mock_run_commands.stop() + self.mock_load_config.stop() + self.mock_get_config.stop() + + def load_fixtures(self, commands=None): + self.load_config.return_value = None + + def test_nxos_vrf_present(self): + set_module_args(dict(vrf='ntc', state='present', admin_state='up')) + result = self.execute_module(changed=True) + self.assertEqual(result['commands'], ['vrf context ntc', 'no shutdown']) + + def test_nxos_vrf_absent(self): + set_module_args(dict(vrf='ntc', state='absent')) + result = self.execute_module(changed=True) + self.assertEqual(result['commands'], ['no vrf context ntc']) + + def test_nxos_vrf_default(self): + set_module_args(dict(vrf='default')) + result = self.execute_module(failed=True) + self.assertEqual(result['msg'], 'cannot use default as name of a VRF')