Deprecate nxos_ip_interface and Add nxos_l3_interface DI module (#33866)

* Deprecate nxos_ip_interface module

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add nxos_l3_interface DI module

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* Add integration test

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>

* pep8 fixes

* Add nxapi cli test

* Improve complexity

* manage layer3 interface

* fix unit test
This commit is contained in:
Trishna Guha 2017-12-20 11:07:23 +05:30 committed by GitHub
commit 3cce8dfafd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 523 additions and 8 deletions

View file

@ -32,6 +32,7 @@ Ansible Changes By Release
This behaviour is the current and future default so most custom modules can This behaviour is the current and future default so most custom modules can
simply remove check_invalid_arguments if they set it to the default of True. simply remove check_invalid_arguments if they set it to the default of True.
check_invalid_arguments will be removed in Ansible-2.9. check_invalid_arguments will be removed in Ansible-2.9.
* nxos_ip_interface module is deprecated in Ansible 2.5. Use nxos_l3_interface module instead.
### Minor Changes ### Minor Changes
* added a few new magic vars corresponding to configuration/command line options: * added a few new magic vars corresponding to configuration/command line options:

View file

@ -79,6 +79,7 @@ Deprecation notices
The following modules will be removed in Ansible 2.9. Please update update your playbooks accordingly. The following modules will be removed in Ansible 2.9. Please update update your playbooks accordingly.
* :ref:`fixme <fixme>` * :ref:`fixme <fixme>`
* :ref:`nxos_ip_interface <nxos_ip_interface>` use :ref:`nxos_l3_interface <nxos_l3_interface>` instead.
Noteworthy module changes Noteworthy module changes
------------------------- -------------------------

View file

@ -17,13 +17,14 @@
# #
ANSIBLE_METADATA = {'metadata_version': '1.1', ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'], 'status': ['deprecated'],
'supported_by': 'network'} 'supported_by': 'network'}
DOCUMENTATION = ''' DOCUMENTATION = '''
--- ---
module: nxos_ip_interface module: nxos_ip_interface
version_added: "2.1" version_added: "2.1"
deprecated: Deprecated in 2.5. Use M(nxos_l3_interface) instead.
short_description: Manages L3 attributes for IPv4 and IPv6 interfaces. short_description: Manages L3 attributes for IPv4 and IPv6 interfaces.
description: description:
- Manages Layer 3 attributes for IPv4 and IPv6 interfaces. - Manages Layer 3 attributes for IPv4 and IPv6 interfaces.

View file

@ -0,0 +1,242 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: nxos_l3_interface
version_added: "2.5"
author: "Trishna Guha (@trishnaguha)"
short_description: Manage L3 interfaces on Cisco NXOS network devices
description:
- This module provides declarative management of L3 interfaces
on Cisco NXOS network devices.
notes:
- Tested against NXOSv 7.0(3)I5(1).
options:
name:
description:
- Name of the L3 interface.
ipv4:
description:
- IPv4 of the L3 interface.
ipv6:
description:
- IPv6 of the L3 interface.
aggregate:
description: List of L3 interfaces definitions.
state:
description:
- State of the L3 interface configuration.
default: present
choices: ['present', 'absent']
"""
EXAMPLES = """
- name: Set interface IPv4 address
nxos_l3_interface:
name: Ethernet2/3
ipv4: 192.168.0.1/24
- name: Remove interface IPv4 address
nxos_l3_interface:
name: Ethernet2/3
state: absent
- name: Set IP addresses on aggregate
nxos_l3_interface:
aggregate:
- { name: Ethernet2/1, ipv4: 192.168.2.10/24 }
- { name: Ethernet2/5, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" }
- name: Remove IP addresses on aggregate
nxos_l3_interface:
aggregate:
- { name: Ethernet2/1, ipv4: 192.168.2.10/24 }
- { name: Ethernet2/5, ipv4: 192.168.3.10/24, ipv6: "fd5d:12c9:2201:1::1/64" }
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- interface ethernet2/3
- no switchport
- ip address 192.168.22.1/24
- ipv6 address "fd5d:12c9:2201:1::1/64"
- no ip address 192.168.22.1/24
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.config import CustomNetworkConfig
from ansible.module_utils.network.common.utils import remove_default_spec
from ansible.module_utils.network.nxos.nxos import get_config, load_config
from ansible.module_utils.network.nxos.nxos import nxos_argument_spec
def search_obj_in_list(name, lst):
for o in lst:
if o['name'] == name:
return o
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w['name']
ipv4 = w['ipv4']
ipv6 = w['ipv6']
state = w['state']
del w['state']
obj_in_have = search_obj_in_list(name, have)
if state == 'absent' and obj_in_have:
command = []
if obj_in_have['name'] == name:
if ipv4 and obj_in_have['ipv4']:
command.append('no ip address {0}'.format(ipv4))
if ipv6 and obj_in_have['ipv6']:
command.append('no ipv6 address {0}'.format(ipv6))
if command:
command.append('exit')
command.insert(0, 'interface {0}'.format(name))
command.insert(1, 'no switchport')
commands.extend(command)
elif state == 'present' and obj_in_have:
command = []
if obj_in_have['name'] == name:
if ipv4 and ipv4 != obj_in_have['ipv4']:
command.append('ip address {0}'.format(ipv4))
if ipv6 and ipv6 != obj_in_have['ipv6']:
command.append('ipv6 address {0}'.format(ipv6))
if command:
command.append('exit')
command.insert(0, 'interface {0}'.format(name))
command.insert(1, 'no switchport')
elif not ipv4 and not ipv6:
command.append('interface {0}'.format(name))
command.append('no switchport')
commands.extend(command)
return commands
def map_params_to_obj(module):
obj = []
aggregate = module.params.get('aggregate')
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append({
'name': module.params['name'],
'ipv4': module.params['ipv4'],
'ipv6': module.params['ipv6'],
'state': module.params['state']
})
return obj
def map_config_to_obj(want, module):
objs = list()
netcfg = CustomNetworkConfig(indent=2, contents=get_config(module))
for w in want:
parents = ['interface {0}'.format(w['name'])]
config = netcfg.get_section(parents)
obj = dict(name=None, ipv4=None, ipv6=None)
if config:
match_name = re.findall(r'interface (\S+)', config, re.M)
if match_name:
obj['name'] = match_name[0]
match_ipv4 = re.findall(r'ip address (\S+)', config, re.M)
if match_ipv4:
obj['ipv4'] = match_ipv4[0]
match_ipv6 = re.findall(r'ipv6 address (\S+)', config, re.M)
if match_ipv6:
obj['ipv6'] = match_ipv6[0]
objs.append(obj)
return objs
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
ipv4=dict(),
ipv6=dict(),
state=dict(default='present', choices=['present', 'absent'])
)
aggregate_spec = deepcopy(element_spec)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type='list', elements='dict', options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(nxos_argument_spec)
required_one_of = [['name', 'aggregate']]
mutually_exclusive = [['name', 'aggregate']]
module = AnsibleModule(argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
warnings = list()
result = {'changed': False}
if warnings:
result['warnings'] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(want, module)
commands = map_obj_to_commands((want, have), module)
result['commands'] = commands
if commands:
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -554,6 +554,15 @@
failed_modules: "{{ failed_modules }} + [ 'nxos_snmp_host' ]" failed_modules: "{{ failed_modules }} + [ 'nxos_snmp_host' ]"
test_failed: true test_failed: true
- block:
- include_role:
name: nxos_l3_interface
when: "limit_to in ['*', 'nxos_l3_interface']"
rescue:
- set_fact:
failed_modules: "{{ failed_modules }} + [ 'nxos_l3_interface' ]"
test_failed: true
########### ###########
- debug: var=failed_modules - debug: var=failed_modules
when: test_failed when: test_failed

View file

@ -0,0 +1,2 @@
---
testcase: "*"

View file

@ -0,0 +1,2 @@
dependencies:
- prepare_nxos_tests

View file

@ -0,0 +1,25 @@
---
- name: collect common cli test cases
find:
paths: "{{ role_path }}/tests/common"
patterns: "{{ testcase }}.yaml"
register: test_cases
- name: collect cli test cases
find:
paths: "{{ role_path }}/tests/cli"
patterns: "{{ testcase }}.yaml"
register: cli_cases
- set_fact:
test_cases:
files: "{{ test_cases.files }} + {{ cli_cases.files }}"
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test case
include: "{{ test_case_to_run }} connection={{ cli }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,3 @@
---
- { include: cli.yaml, tags: ['cli'] }
- { include: nxapi.yaml, tags: ['nxapi'] }

View file

@ -0,0 +1,25 @@
---
- name: collect common nxapi test cases
find:
paths: "{{ role_path }}/tests/common"
patterns: "{{ testcase }}.yaml"
register: test_cases
- name: collect nxapi test cases
find:
paths: "{{ role_path }}/tests/nxapi"
patterns: "{{ testcase }}.yaml"
register: nxapi_cases
- set_fact:
test_cases:
files: "{{ test_cases.files }} + {{ nxapi_cases.files }}"
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test case
include: "{{ test_case_to_run }} connection={{ nxapi }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,107 @@
---
- debug: msg="START TRANSPORT:CLI nxos_l3_interface sanity test"
# Select interface for test
- set_fact: testint2="{{ nxos_int2 }}"
- set_fact: testint3="{{ nxos_int3 }}"
- name: Setup - remove address from interface prior to testing(Part1)
nxos_config:
lines:
- no ip address 192.168.22.1/24
parents: no switchport
before: "interface {{ testint2 }}"
provider: "{{ cli }}"
ignore_errors: yes
- name: Setup - remove address from interface prior to testing(Part2)
nxos_config:
lines:
- no ip address 192.168.20.1/24
- no ipv6 address 33:db::2/8
parents: no switchport
before: "interface {{ testint3 }}"
provider: "{{ cli }}"
ignore_errors: yes
- name: Configure ipv4 address to interface
nxos_l3_interface: &conf
name: "{{ testint2 }}"
ipv4: 192.168.22.1/24
provider: "{{ cli }}"
register: result
- assert:
that:
- "result.changed == true"
- name: Configure ipv4 address to interface(Idempotence)
nxos_l3_interface: *conf
register: result
- assert:
that:
- "result.changed == false"
- name: Remove ipv4 address from interface
nxos_l3_interface: &rm
name: "{{ testint2 }}"
ipv4: 192.168.22.1/24
provider: "{{ cli }}"
state: absent
register: result
- assert:
that:
- "result.changed == true"
- name: Remove ipv4 address from interface(Idempotence)
nxos_l3_interface: *rm
register: result
- assert:
that:
- "result.changed == false"
- name: Configure address to interfaces aggregate
nxos_l3_interface: &conf_agg
aggregate:
- { name: "{{ testint2 }}", ipv4: 192.168.22.1/24 }
- { name: "{{ testint3 }}", ipv4: 192.168.20.1/24, ipv6: "33:db::2/8" }
provider: "{{ cli }}"
register: result
- assert:
that:
- "result.changed == true"
- name: Configure address to interfaces aggregate(Idempotence)
nxos_l3_interface: *conf_agg
register: result
- assert:
that:
- "result.changed == false"
- name: Remove address from interfaces aggregate
nxos_l3_interface: &rm_agg
aggregate:
- { name: "{{ testint2 }}", ipv4: 192.168.22.1/24 }
- { name: "{{ testint3 }}", ipv4: 192.168.20.1/24, ipv6: "33:db::2/8" }
provider: "{{ cli }}"
state: absent
register: result
- assert:
that:
- "result.changed == true"
- name: Remove address from interfaces aggregate(Idempotence)
nxos_l3_interface: *rm_agg
register: result
- assert:
that:
- "result.changed == false"
- debug: msg="END TRANSPORT:CLI nxos_l3_interface sanity test"

View file

@ -0,0 +1,97 @@
---
- debug: msg="START TRANSPORT:NXAPI nxos_l3_interface sanity test"
# Select interface for test
- set_fact: testint2="{{ nxos_int2 }}"
- set_fact: testint3="{{ nxos_int3 }}"
- name: Setup - Remove address from interfaces aggregate
nxos_l3_interface:
aggregate:
- { name: "{{ testint2 }}", ipv4: 192.168.22.1/24 }
- { name: "{{ testint3 }}", ipv4: 192.168.20.1/24, ipv6: "33:db::2/8" }
provider: "{{ nxapi }}"
state: absent
ignore_errors: yes
- name: Configure ipv4 address to interface
nxos_l3_interface: &conf
name: "{{ testint2 }}"
ipv4: 192.168.22.1/24
provider: "{{ nxapi }}"
register: result
- assert:
that:
- "result.changed == true"
- name: Configure ipv4 address to interface(Idempotence)
nxos_l3_interface: *conf
register: result
- assert:
that:
- "result.changed == false"
- name: Remove ipv4 address from interface
nxos_l3_interface: &rm
name: "{{ testint2 }}"
ipv4: 192.168.22.1/24
provider: "{{ nxapi }}"
state: absent
register: result
- assert:
that:
- "result.changed == true"
- name: Remove ipv4 address from interface(Idempotence)
nxos_l3_interface: *rm
register: result
- assert:
that:
- "result.changed == false"
- name: Configure address to interfaces aggregate
nxos_l3_interface: &conf_agg
aggregate:
- { name: "{{ testint2 }}", ipv4: 192.168.22.1/24 }
- { name: "{{ testint3 }}", ipv4: 192.168.20.1/24, ipv6: "33:db::2/8" }
provider: "{{ nxapi }}"
register: result
- assert:
that:
- "result.changed == true"
- name: Configure address to interfaces aggregate(Idempotence)
nxos_l3_interface: *conf_agg
register: result
- assert:
that:
- "result.changed == false"
- name: Remove address from interfaces aggregate
nxos_l3_interface: &rm_agg
aggregate:
- { name: "{{ testint2 }}", ipv4: 192.168.22.1/24 }
- { name: "{{ testint3 }}", ipv4: 192.168.20.1/24, ipv6: "33:db::2/8" }
provider: "{{ nxapi }}"
state: absent
register: result
- assert:
that:
- "result.changed == true"
- name: Remove address from interfaces aggregate(Idempotence)
nxos_l3_interface: *rm_agg
register: result
- assert:
that:
- "result.changed == false"
- debug: msg="END TRANSPORT:NXAPI nxos_l3_interface sanity test"

View file

@ -20,29 +20,29 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type __metaclass__ = type
from ansible.compat.tests.mock import patch from ansible.compat.tests.mock import patch
from ansible.modules.network.nxos import nxos_ip_interface from ansible.modules.network.nxos import _nxos_ip_interface
from .nxos_module import TestNxosModule, load_fixture, set_module_args from .nxos_module import TestNxosModule, load_fixture, set_module_args
class TestNxosIPInterfaceModule(TestNxosModule): class TestNxosIPInterfaceModule(TestNxosModule):
module = nxos_ip_interface module = _nxos_ip_interface
def setUp(self): def setUp(self):
super(TestNxosIPInterfaceModule, self).setUp() super(TestNxosIPInterfaceModule, self).setUp()
self.mock_get_interface_mode = patch( self.mock_get_interface_mode = patch(
'ansible.modules.network.nxos.nxos_ip_interface.get_interface_mode') 'ansible.modules.network.nxos._nxos_ip_interface.get_interface_mode')
self.get_interface_mode = self.mock_get_interface_mode.start() self.get_interface_mode = self.mock_get_interface_mode.start()
self.mock_send_show_command = patch( self.mock_send_show_command = patch(
'ansible.modules.network.nxos.nxos_ip_interface.send_show_command') 'ansible.modules.network.nxos._nxos_ip_interface.send_show_command')
self.send_show_command = self.mock_send_show_command.start() self.send_show_command = self.mock_send_show_command.start()
self.mock_load_config = patch('ansible.modules.network.nxos.nxos_ip_interface.load_config') self.mock_load_config = patch('ansible.modules.network.nxos._nxos_ip_interface.load_config')
self.load_config = self.mock_load_config.start() self.load_config = self.mock_load_config.start()
self.mock_get_capabilities = patch('ansible.modules.network.nxos.nxos_ip_interface.get_capabilities') self.mock_get_capabilities = patch('ansible.modules.network.nxos._nxos_ip_interface.get_capabilities')
self.get_capabilities = self.mock_get_capabilities.start() self.get_capabilities = self.mock_get_capabilities.start()
self.get_capabilities.return_value = {'network_api': 'cliconf'} self.get_capabilities.return_value = {'network_api': 'cliconf'}
@ -55,7 +55,7 @@ class TestNxosIPInterfaceModule(TestNxosModule):
def load_fixtures(self, commands=None, device=''): def load_fixtures(self, commands=None, device=''):
self.get_interface_mode.return_value = 'layer3' self.get_interface_mode.return_value = 'layer3'
self.send_show_command.return_value = [load_fixture('', 'nxos_ip_interface.cfg')] self.send_show_command.return_value = [load_fixture('', '_nxos_ip_interface.cfg')]
self.load_config.return_value = None self.load_config.return_value = None
def test_nxos_ip_interface_ip_present(self): def test_nxos_ip_interface_ip_present(self):