diff --git a/lib/ansible/modules/network/nxos/nxos_evpn_global.py b/lib/ansible/modules/network/nxos/nxos_evpn_global.py index befbb64c35..9d9a0f0412 100644 --- a/lib/ansible/modules/network/nxos/nxos_evpn_global.py +++ b/lib/ansible/modules/network/nxos/nxos_evpn_global.py @@ -16,9 +16,11 @@ # along with Ansible. If not, see . # -ANSIBLE_METADATA = {'status': ['preview'], - 'supported_by': 'community', - 'version': '1.0'} +ANSIBLE_METADATA = { + 'status': ['preview'], + 'supported_by': 'community', + 'version': '1.0' +} DOCUMENTATION = ''' --- @@ -29,116 +31,45 @@ description: - Handles the EVPN control plane for VXLAN. author: Gabriele Gerbino (@GGabriele) options: - nv_overlay_evpn: - description: - - EVPN control plane. - required: true - choices: ['true', 'false'] + nv_overlay_evpn: + description: + - EVPN control plane. + required: true + choices: ['true', 'false'] ''' + EXAMPLES = ''' - nxos_evpn_global: nv_overlay_evpn: true - username: "{{ un }}" - password: "{{ pwd }}" - host: "{{ inventory_hostname }}" ''' RETURN = ''' -proposed: - description: k/v pairs of parameters passed into module - returned: verbose mode - type: dict - sample: {"nv_overlay_evpn": true} -existing: - description: k/v pairs of existing configuration - returned: verbose mode - type: dict - sample: {"nv_overlay_evpn": false} -end_state: - description: k/v pairs of configuration after module execution - returned: verbose mode - type: dict - sample: {"nv_overlay_evpn": true} -updates: - description: commands sent to the device +commands: + description: The set of commands to be sent to the remote device returned: always type: list - sample: ["nv overlay evpn"] -changed: - description: check to see if a change was made on the device - returned: always - type: boolean - sample: true + sample: ['nv overlay evpn'] ''' - - -import re -from ansible.module_utils.nxos import get_config, load_config, run_commands -from ansible.module_utils.nxos import nxos_argument_spec, check_args from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.netcfg import CustomNetworkConfig +from ansible.module_utils.nxos import get_config, load_config +from ansible.module_utils.nxos import nxos_argument_spec +from ansible.module_utils.nxos import check_args as nxos_check_args -PARAM_TO_COMMAND_KEYMAP = { - 'nv_overlay_evpn': 'nv overlay evpn', -} - - -def invoke(name, *args, **kwargs): - func = globals().get(name) - if func: - return func(*args, **kwargs) - - -def get_value(arg, config, module): - REGEX = re.compile(r'(?:{0}\s)(?P.*)$'.format(PARAM_TO_COMMAND_KEYMAP[arg]), re.M) - value = False - if REGEX.search(config): - value = True - return value - - -def get_existing(module): - existing = {} - config = str(get_config(module)) - - existing['nv_overlay_evpn'] = get_value('nv_overlay_evpn', config, module) - return existing - - -def apply_key_map(key_map, table): - new_dict = {} - for key, value in table.items(): - new_key = key_map.get(key) - if new_key: - value = table.get(key) - if value: - new_dict[new_key] = value - else: - new_dict[new_key] = value - return new_dict - - -def get_commands(module, existing, proposed, candidate): - commands = list() - proposed_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, proposed) - existing_commands = apply_key_map(PARAM_TO_COMMAND_KEYMAP, existing) - - for key, value in proposed_commands.items(): - if value is True: - commands.append(key) - elif value is False: - commands.append('no {0}'.format(key)) - - if commands: - candidate.add(commands, parents=[]) +def check_args(module, warnings): + nxos_check_args(module, warnings) + for key in ('include_defaults', 'config', 'save'): + if module.params[key] is not None: + warnings.append('argument %s is no longer supported, ignoring value' % key) def main(): argument_spec = dict( nv_overlay_evpn=dict(required=True, type='bool'), - include_defaults=dict(default=True), + + # deprecated in Ans2.3 + include_defaults=dict(), config=dict(), - save=dict(type='bool', default=False) + save=dict() ) argument_spec.update(nxos_argument_spec) @@ -146,36 +77,28 @@ def main(): module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + result = {'changed': False} + warnings = list() check_args(module, warnings) + if warnings: + result['warnings'] = warnings + config = get_config(module) + commands = list() - existing = invoke('get_existing', module) - end_state = existing - proposed = dict(nv_overlay_evpn=module.params['nv_overlay_evpn']) + if module.params['nv_overlay_evpn'] is True: + if 'nv overlay evpn' not in config: + commands.append('nv overlay evpn') + elif 'nv overlay evpn' in config: + commands.append('no nv overlay evpn') - result = {} - candidate = CustomNetworkConfig(indent=3) - invoke('get_commands', module, existing, proposed, candidate) + if commands: + if not module.check_mode: + load_config(module, commands) + result['changed'] = True - if proposed != existing: - try: - response = load_config(module, candidate) - result.update(response) - except ShellError: - exc = get_exception() - module.fail_json(msg=str(exc)) - else: - result['updates'] = [] - - result['connected'] = module.connected - if module._verbosity > 0: - end_state = invoke('get_existing', module) - result['end_state'] = end_state - result['existing'] = existing - result['proposed'] = proposed - - result['warnings'] = warnings + result['commands'] = commands module.exit_json(**result) diff --git a/lib/ansible/plugins/action/nxos.py b/lib/ansible/plugins/action/nxos.py index 0daff67a45..5660699c3a 100644 --- a/lib/ansible/plugins/action/nxos.py +++ b/lib/ansible/plugins/action/nxos.py @@ -78,7 +78,7 @@ class ActionModule(_ActionModule): # enable mode and not config module rc, out, err = connection.exec_command('prompt()') while str(out).strip().endswith(')#'): - display.debug('wrong context, sending exit to device', self._play_context.remote_addr) + display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr) connection.exec_command('exit') rc, out, err = connection.exec_command('prompt()') diff --git a/test/integration/nxos.yaml b/test/integration/nxos.yaml index 3dcbe5e112..4c73ca02ef 100644 --- a/test/integration/nxos.yaml +++ b/test/integration/nxos.yaml @@ -13,3 +13,4 @@ - { role: nxos_facts, when: "limit_to in ['*', 'nxos_facts']" } - { role: nxos_template, when: "limit_to in ['*', 'nxos_template']" } - { role: nxos_nxapi, when: "limit_to in ['*', 'nxos_nxapi']" } + - { role: nxos_evpn_global, when: "limit_to in ['*', 'nxos_evpn_global']" } diff --git a/test/integration/targets/nxos_evpn_global/meta/main.yml b/test/integration/targets/nxos_evpn_global/meta/main.yml new file mode 100644 index 0000000000..ae741cbdc7 --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - prepare_nxos_tests diff --git a/test/integration/targets/nxos_evpn_global/tasks/cli.yaml b/test/integration/targets/nxos_evpn_global/tasks/cli.yaml new file mode 100644 index 0000000000..d675462dd0 --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/tasks/cli.yaml @@ -0,0 +1,15 @@ +--- +- name: collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + register: test_cases + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: run test case + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/nxos_evpn_global/tasks/main.yaml b/test/integration/targets/nxos_evpn_global/tasks/main.yaml new file mode 100644 index 0000000000..4b0f8c64d9 --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/tasks/main.yaml @@ -0,0 +1,3 @@ +--- +- { include: cli.yaml, tags: ['cli'] } +- { include: nxapi.yaml, tags: ['nxapi'] } diff --git a/test/integration/targets/nxos_evpn_global/tasks/nxapi.yaml b/test/integration/targets/nxos_evpn_global/tasks/nxapi.yaml new file mode 100644 index 0000000000..ea525379f7 --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/tasks/nxapi.yaml @@ -0,0 +1,28 @@ +--- +- name: collect all nxapi test cases + find: + paths: "{{ role_path }}/tests/nxapi" + patterns: "{{ testcase }}.yaml" + register: test_cases + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: enable nxapi + nxos_config: + lines: + - feature nxapi + - nxapi http port 80 + provider: "{{ cli }}" + +- name: run test case + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run + +- name: disable nxapi + nxos_config: + lines: + - no feature nxapi + provider: "{{ cli }}" diff --git a/test/integration/targets/nxos_evpn_global/tests/cli/nv_overlay_evpn.yaml b/test/integration/targets/nxos_evpn_global/tests/cli/nv_overlay_evpn.yaml new file mode 100644 index 0000000000..5c5f06072c --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/tests/cli/nv_overlay_evpn.yaml @@ -0,0 +1,50 @@ +--- +- debug: msg="START cli/nv_overlay_evpn" + +- name: setup + nxos_config: + lines: no nv overlay evpn + match: none + provider: "{{ cli }}" + +- name: enable nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - "result.changed == true" + +- name: verify nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - "result.changed == false" + +- name: disable nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: no + provider: "{{ cli }}" + register: result + +- assert: + that: + - "result.changed == true" + +- name: verify nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: no + provider: "{{ cli }}" + register: result + +- assert: + that: + - "result.changed == false" + +- debug: msg="END cli/nv_overlay_evpn" diff --git a/test/integration/targets/nxos_evpn_global/tests/nxapi/nv_overlay_evpn.yaml b/test/integration/targets/nxos_evpn_global/tests/nxapi/nv_overlay_evpn.yaml new file mode 100644 index 0000000000..af058f1192 --- /dev/null +++ b/test/integration/targets/nxos_evpn_global/tests/nxapi/nv_overlay_evpn.yaml @@ -0,0 +1,50 @@ +--- +- debug: msg="START nxapi/nv_overlay_evpn" + +- name: setup + nxos_config: + lines: no nv overlay evpn + match: none + provider: "{{ nxapi }}" + +- name: enable nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: yes + provider: "{{ nxapi }}" + register: result + +- assert: + that: + - "result.changed == true" + +- name: verify nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: yes + provider: "{{ nxapi }}" + register: result + +- assert: + that: + - "result.changed == false" + +- name: disable nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: no + provider: "{{ nxapi }}" + register: result + +- assert: + that: + - "result.changed == true" + +- name: verify nv overlay evpn + nxos_evpn_global: + nv_overlay_evpn: no + provider: "{{ nxapi }}" + register: result + +- assert: + that: + - "result.changed == false" + +- debug: msg="END nxapi/nv_overlay_evpn" diff --git a/test/units/modules/network/nxos/fixtures/nxos_evpn_global/configured.cfg b/test/units/modules/network/nxos/fixtures/nxos_evpn_global/configured.cfg new file mode 100644 index 0000000000..32504f6bfb --- /dev/null +++ b/test/units/modules/network/nxos/fixtures/nxos_evpn_global/configured.cfg @@ -0,0 +1,3 @@ +hostname switch01 +nv overlay evpn + diff --git a/test/units/modules/network/nxos/fixtures/nxos_evpn_global/unconfigured.cfg b/test/units/modules/network/nxos/fixtures/nxos_evpn_global/unconfigured.cfg new file mode 100644 index 0000000000..c555eaaf96 --- /dev/null +++ b/test/units/modules/network/nxos/fixtures/nxos_evpn_global/unconfigured.cfg @@ -0,0 +1 @@ +hostname switch01 diff --git a/test/units/modules/network/nxos/test_nxos_evpn_global.py b/test/units/modules/network/nxos/test_nxos_evpn_global.py new file mode 100644 index 0000000000..7f84e7271a --- /dev/null +++ b/test/units/modules/network/nxos/test_nxos_evpn_global.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_evpn_global +from .nxos_module import TestNxosModule, load_fixture, set_module_args + + +class TestNxosEvpnGlobalModule(TestNxosModule): + + module = nxos_evpn_global + + def setUp(self): + self.mock_get_config = patch('ansible.modules.network.nxos.nxos_evpn_global.get_config') + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch('ansible.modules.network.nxos.nxos_evpn_global.load_config') + self.load_config = self.mock_load_config.start() + + def tearDown(self): + self.mock_get_config.stop() + self.mock_load_config.stop() + + def load_fixtures(self, commands=None): + self.load_config.return_value = None + + def start_configured(self, *args, **kwargs): + self.get_config.return_value = load_fixture('nxos_evpn_global/configured.cfg') + return self.execute_module(*args, **kwargs) + + def start_unconfigured(self, *args, **kwargs): + self.get_config.return_value = load_fixture('nxos_evpn_global/unconfigured.cfg') + return self.execute_module(*args, **kwargs) + + def test_nxos_evpn_global_enable(self): + set_module_args(dict(nv_overlay_evpn=True)) + commands = ['nv overlay evpn'] + self.start_unconfigured(changed=True, commands=commands) + + def test_nxos_evpn_global_disable(self): + set_module_args(dict(nv_overlay_evpn=False)) + commands = ['no nv overlay evpn'] + self.start_configured(changed=True, commands=commands) +