Add nxos_interfaces resource module (#60421)

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
This commit is contained in:
Trishna Guha 2019-08-14 13:38:22 +05:30 committed by GitHub
parent 0f35e4b7b9
commit d5d88f9b11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1058 additions and 15 deletions

View file

@ -70,6 +70,10 @@ The following modules will be removed in Ansible 2.13. Please update update your
* vyos_l3_interface use :ref:`vyos_l3_interfaces <vyos_l3_interfaces_module>` instead.
* nxos_vlan use :ref:`nxos_vlans <nxos_vlans_module>` instead.
* nxos_interface use :ref:`nxos_interfaces <nxos_interfaces_module>` instead.
The following functionality will be removed in Ansible 2.12. Please update update your playbooks accordingly.
* ``vmware_cluster`` DRS, HA and VSAN configuration; use `vmware_cluster_drs <vmware_cluster_drs_module>`, `vmware_cluster_ha <vmware_cluster_ha_module>` and `vmware_cluster_vsan <vmware_cluster_vsan_module>` instead.

View file

@ -14,6 +14,7 @@ CHOICES = [
'vlans',
'lacp',
'lacp_interfaces',
'interfaces',
]

View file

@ -0,0 +1,81 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The arg spec for the nxos_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class InterfacesArgs(object): # pylint: disable=R0903
"""The arg spec for the nxos_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {
'config': {
'elements': 'dict',
'options': {
'description': {
'type': 'str'
},
'duplex': {
'choices': ['full', 'half', 'auto'],
'type': 'str'
},
'enabled': {
'default': True,
'type': 'bool'
},
'fabric_forwarding_anycast_gateway': {
'type': 'bool'
},
'ip_forward': {
'type': 'bool'
},
'mode': {
'choices': ['layer2', 'layer3'],
'type': 'str'
},
'mtu': {
'type': 'str'
},
'name': {
'required': True,
'type': 'str'
},
'speed': {
'type': 'str'
}
},
'type': 'list'
},
'state': {
'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'
}
} # pylint: disable=C0301

View file

@ -0,0 +1,288 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
"""
The nxos_interfaces class
It is in this file where the current configuration (as dict)
is compared to the provided configuration (as dict) and the command set
necessary to bring the current configuration to it's desired end-state is
created
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
from ansible.module_utils.network.common.cfg.base import ConfigBase
from ansible.module_utils.network.common.utils import dict_diff, to_list, remove_empties
from ansible.module_utils.network.nxos.facts.facts import Facts
from ansible.module_utils.network.nxos.utils.utils import get_interface_type, normalize_interface, search_obj_in_list
class Interfaces(ConfigBase):
"""
The nxos_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'interfaces',
]
exclude_params = [
'description',
'mtu',
'speed',
'duplex',
]
def __init__(self, module):
super(Interfaces, self).__init__(module)
def get_interfaces_facts(self):
""" Get the 'facts' (the current configuration)
:rtype: A dictionary
:returns: The current configuration as a dictionary
"""
facts, _warnings = Facts(self._module).get_facts(self.gather_subset, self.gather_network_resources)
interfaces_facts = facts['ansible_network_resources'].get('interfaces')
if not interfaces_facts:
return []
return interfaces_facts
def execute_module(self):
""" Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
result = {'changed': False}
commands = list()
warnings = list()
existing_interfaces_facts = self.get_interfaces_facts()
commands.extend(self.set_config(existing_interfaces_facts))
if commands:
if not self._module.check_mode:
self._connection.edit_config(commands)
result['changed'] = True
result['commands'] = commands
changed_interfaces_facts = self.get_interfaces_facts()
result['before'] = existing_interfaces_facts
if result['changed']:
result['after'] = changed_interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_interfaces_facts):
""" Collect the configuration from the args passed to the module,
collect the current configuration (as a dict from facts)
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
config = self._module.params.get('config')
want = []
if config:
for w in config:
w.update({'name': normalize_interface(w['name'])})
want.append(remove_empties(w))
have = existing_interfaces_facts
resp = self.set_state(want, have)
return to_list(resp)
def set_state(self, want, have):
""" Select the appropriate function based on the state provided
:param want: the desired configuration as a dictionary
:param have: the current configuration as a dictionary
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
state = self._module.params['state']
if state in ('overridden', 'merged', 'replaced') and not want:
self._module.fail_json(msg='config is required for state {0}'.format(state))
commands = list()
if state == 'overridden':
commands.extend(self._state_overridden(want, have))
elif state == 'deleted':
commands.extend(self._state_deleted(want, have))
else:
for w in want:
if state == 'merged':
commands.extend(self._state_merged(w, have))
elif state == 'replaced':
commands.extend(self._state_replaced(w, have))
return commands
def _state_replaced(self, w, have):
""" The command generator when state is replaced
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
obj_in_have = search_obj_in_list(w['name'], have, 'name')
if obj_in_have:
diff = dict_diff(w, obj_in_have)
else:
diff = w
merged_commands = self.set_commands(w, have)
if 'name' not in diff:
diff['name'] = w['name']
wkeys = w.keys()
dkeys = diff.keys()
for k in wkeys:
if k in self.exclude_params and k in dkeys:
del diff[k]
replaced_commands = self.del_attribs(diff)
if merged_commands:
cmds = set(replaced_commands).intersection(set(merged_commands))
for cmd in cmds:
merged_commands.remove(cmd)
commands.extend(replaced_commands)
commands.extend(merged_commands)
return commands
def _state_overridden(self, want, have):
""" The command generator when state is overridden
:rtype: A list
:returns: the commands necessary to migrate the current configuration
to the desired configuration
"""
commands = []
for h in have:
obj_in_want = search_obj_in_list(h['name'], want, 'name')
if h == obj_in_want:
continue
for w in want:
if h['name'] == w['name']:
wkeys = w.keys()
hkeys = h.keys()
for k in wkeys:
if k in self.exclude_params and k in hkeys:
del h[k]
commands.extend(self.del_attribs(h))
for w in want:
commands.extend(self.set_commands(w, have))
return commands
def _state_merged(self, w, have):
""" The command generator when state is merged
:rtype: A list
:returns: the commands necessary to merge the provided into
the current configuration
"""
return self.set_commands(w, have)
def _state_deleted(self, want, have):
""" The command generator when state is deleted
:rtype: A list
:returns: the commands necessary to remove the current configuration
of the provided objects
"""
commands = []
if want:
for w in want:
obj_in_have = search_obj_in_list(w['name'], have, 'name')
commands.extend(self.del_attribs(obj_in_have))
else:
if not have:
return commands
for h in have:
commands.extend(self.del_attribs(h))
return commands
def del_attribs(self, obj):
commands = []
if not obj or len(obj.keys()) == 1:
return commands
commands.append('interface ' + obj['name'])
if 'description' in obj:
commands.append('no description')
if 'speed' in obj:
commands.append('no speed')
if 'duplex' in obj:
commands.append('no duplex')
if 'enabled' in obj and obj['enabled'] is False:
commands.append('no shutdown')
if 'mtu' in obj:
commands.append('no mtu')
if 'ip_forward' in obj and obj['ip_forward'] is True:
commands.append('no ip forward')
if 'fabric_forwarding_anycast_gateway' in obj and obj['fabric_forwarding_anycast_gateway'] is True:
commands.append('no fabric forwarding mode anycast-gateway')
if 'mode' in obj and obj['mode'] != 'layer2':
commands.append('switchport')
return commands
def diff_of_dicts(self, w, obj):
diff = set(w.items()) - set(obj.items())
diff = dict(diff)
if diff and w['name'] == obj['name']:
diff.update({'name': w['name']})
return diff
def add_commands(self, d):
commands = []
if not d:
return commands
commands.append('interface' + ' ' + d['name'])
if 'description' in d:
commands.append('description ' + d['description'])
if 'speed' in d:
commands.append('speed ' + str(d['speed']))
if 'duplex' in d:
commands.append('duplex ' + d['duplex'])
if 'enabled' in d:
if d['enabled'] is True:
commands.append('no shutdown')
else:
commands.append('shutdown')
if 'mtu' in d:
commands.append('mtu ' + str(d['mtu']))
if 'ip_forward' in d:
if d['ip_forward'] is True:
commands.append('ip forward')
else:
commands.append('no ip forward')
if 'fabric_forwarding_anycast_gateway' in d:
if d['fabric_forwarding_anycast_gateway'] is True:
commands.append('fabric forwarding mode anycast-gateway')
else:
commands.append('no fabric forwarding mode anycast-gateway')
if 'mode' in d:
if d['mode'] == 'layer2':
commands.append('switchport')
elif d['mode'] == 'layer3':
commands.append('no switchport')
return commands
def set_commands(self, w, have):
commands = []
obj_in_have = search_obj_in_list(w['name'], have, 'name')
if not obj_in_have:
commands = self.add_commands(w)
else:
diff = self.diff_of_dicts(w, obj_in_have)
commands = self.add_commands(diff)
return commands

View file

@ -12,6 +12,7 @@ calls the appropriate facts gathering function
from ansible.module_utils.network.nxos.argspec.facts.facts import FactsArgs
from ansible.module_utils.network.common.facts.facts import FactsBase
from ansible.module_utils.network.nxos.facts.legacy.base import Default, Legacy, Hardware, Config, Interfaces, Features
from ansible.module_utils.network.nxos.facts.interfaces.interfaces import InterfacesFacts
from ansible.module_utils.network.nxos.facts.lacp.lacp import LacpFacts
from ansible.module_utils.network.nxos.facts.lag_interfaces.lag_interfaces import Lag_interfacesFacts
from ansible.module_utils.network.nxos.facts.telemetry.telemetry import TelemetryFacts
@ -33,6 +34,7 @@ FACT_RESOURCE_SUBSETS = dict(
vlans=VlansFacts,
lacp=LacpFacts,
lacp_interfaces=Lacp_interfacesFacts,
interfaces=InterfacesFacts,
)

View file

@ -0,0 +1,97 @@
#
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)#!/usr/bin/python
"""
The nxos interfaces fact class
It is in this file the configuration is collected from the device
for a given resource, parsed, and the facts tree is populated
based on the configuration.
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
import re
from copy import deepcopy
from ansible.module_utils.network.common import utils
from ansible.module_utils.network.nxos.argspec.interfaces.interfaces import InterfacesArgs
from ansible.module_utils.network.nxos.utils.utils import get_interface_type
class InterfacesFacts(object):
""" The nxos interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = InterfacesArgs.argument_spec
spec = deepcopy(self.argument_spec)
if subspec:
if options:
facts_argument_spec = spec[subspec][options]
else:
facts_argument_spec = spec[subspec]
else:
facts_argument_spec = spec
self.generated_spec = utils.generate_dict(facts_argument_spec)
def populate_facts(self, connection, ansible_facts, data=None):
""" Populate the facts for interfaces
:param connection: the device connection
:param data: previously collected conf
:rtype: dictionary
:returns: facts
"""
objs = []
if not data:
data = connection.get('show running-config | section ^interface')
config = data.split('interface ')
for conf in config:
conf = conf.strip()
if conf:
obj = self.render_config(self.generated_spec, conf)
if obj and len(obj.keys()) > 1:
objs.append(obj)
ansible_facts['ansible_network_resources'].pop('interfaces', None)
facts = {}
if objs:
facts['interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def render_config(self, spec, conf):
"""
Render config as dictionary structure and delete keys
from spec for null values
:param spec: The facts tree, generated from the argspec
:param conf: The configuration
:rtype: dictionary
:returns: The generated config
"""
config = deepcopy(spec)
match = re.search(r'^(\S+)', conf)
intf = match.group(1)
if get_interface_type(intf) == 'unknown':
return {}
config['name'] = intf
config['description'] = utils.parse_conf_arg(conf, 'description')
config['speed'] = utils.parse_conf_arg(conf, 'speed')
config['mtu'] = utils.parse_conf_arg(conf, 'mtu')
config['duplex'] = utils.parse_conf_arg(conf, 'duplex')
config['mode'] = utils.parse_conf_cmd_arg(conf, 'switchport', 'layer2', 'layer3')
config['enabled'] = utils.parse_conf_cmd_arg(conf, 'shutdown', False, True)
config['fabric_forwarding_anycast_gateway'] = utils.parse_conf_arg(conf, 'fabric forwarding mode anycast-gateway')
config['ip_forward'] = utils.parse_conf_arg(conf, 'ip forward')
interfaces_cfg = utils.remove_empties(config)
return interfaces_cfg

View file

@ -3,8 +3,11 @@
# 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'],
'status': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
@ -15,6 +18,10 @@ version_added: "2.1"
short_description: Manages physical attributes of interfaces.
description:
- Manages physical attributes of interfaces of NX-OS switches.
deprecated:
removed_in: '2.13'
alternative: nxos_interfaces
why: Updated modules released with more functionality
author:
- Jason Edelman (@jedelman8)
- Trishna Guha (@trishnaguha)

View file

@ -57,7 +57,7 @@ options:
to a given subset. Possible values for this argument include
all and the resources like interfaces, vlans etc.
Can specify a list of values to include a larger subset.
choices: ['all', 'lag_interfaces', 'telemetry', 'vlans', 'lacp', 'lacp_interfaces']
choices: ['all', 'lag_interfaces', 'telemetry', 'vlans', 'lacp', 'lacp_interfaces', 'interfaces']
required: false
version_added: "2.9"
"""

View file

@ -0,0 +1,281 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright 2019 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#############################################
# WARNING #
#############################################
#
# This file is auto generated by the resource
# module builder playbook.
#
# Do not edit this file manually.
#
# Changes to this file will be over written
# by the resource module builder.
#
# Changes should be made in the model used to
# generate this file or in the resource module
# builder template.
#
#############################################
"""
The module file for nxos_interfaces
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: nxos_interfaces
version_added: 2.9
short_description: 'Manages interface attributes of NX-OS Interfaces'
description: This module manages the interface attributes of NX-OS interfaces.
author: Trishna Guha (@trishnaguha)
notes:
- Tested against NXOS 7.3.(0)D1(1) on VIRL
options:
config:
description: A dictionary of interface options
type: list
elements: dict
suboptions:
name:
description:
- Full name of interface, e.g. Ethernet1/1, port-channel10.
type: str
required: true
description:
description:
- Interface description.
type: str
enabled:
description:
- Administrative state of the interface.
Set the value to C(true) to administratively enable the interface
or C(false) to disable it
type: bool
default: true
speed:
description:
- Interface link speed. Applicable for Ethernet interfaces only.
type: str
mode:
description:
- Manage Layer2 or Layer3 state of the interface.
Applicable for Ethernet and port channel interfaces only.
choices: ['layer2','layer3']
type: str
mtu:
description:
- MTU for a specific interface. Must be an even number between 576 and 9216.
Applicable for Ethernet interfaces only.
type: str
duplex:
description:
- Interface link status. Applicable for Ethernet interfaces only.
type: str
choices: ['full', 'half', 'auto']
ip_forward:
description:
- Enable or disable IP forward feature on SVIs.
Set the value to C(true) to enable or C(false) to disable.
type: bool
fabric_forwarding_anycast_gateway:
description:
- Associate SVI with anycast gateway under VLAN configuration mode.
Applicable for SVI interfaces only.
type: bool
state:
description:
- The state the configuration should be left in
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
#
# interface Ethernet1/1
# description testing
# mtu 1800
- name: Merge provided configuration with device configuration
nxos_interfaces:
config:
- name: Ethernet1/1
description: 'Configured by Ansible'
enabled: True
- name: Ethernet1/2
description: 'Configured by Ansible Network'
enabled: False
state: merged
# After state:
# ------------
#
# interface Ethernet1/1
# description Configured by Ansible
# no shutdown
# mtu 1800
# interface Ethernet2
# description Configured by Ansible Network
# shutdown
# Using replaced
# Before state:
# -------------
#
# interface Ethernet1/1
# description Interface 1/1
# interface Ethernet1/2
- name: Replaces device configuration of listed interfaces with provided configuration
nxos_interfaces:
config:
- name: Ethernet1/1
description: 'Configured by Ansible'
enabled: True
mtu: 2000
- name: Ethernet1/2
description: 'Configured by Ansible Network'
enabled: False
mode: layer2
state: replaced
# After state:
# ------------
#
# interface Ethernet1/1
# description Configured by Ansible
# no shutdown
# mtu 1500
# interface Ethernet2/2
# description Configured by Ansible Network
# shutdown
# switchport
# Using overridden
# Before state:
# -------------
#
# interface Ethernet1/1
# description Interface Ethernet1/1
# interface Ethernet1/2
# interface mgmt0
# description Management interface
# ip address dhcp
- name: Override device configuration of all interfaces with provided configuration
nxos_interfaces:
config:
- name: Ethernet1/1
enabled: True
- name: Ethernet1/2
description: 'Configured by Ansible Network'
enabled: False
state: overridden
# After state:
# ------------
#
# interface Ethernet1/1
# interface Ethernet1/2
# description Configured by Ansible Network
# shutdown
# interface mgmt0
# ip address dhcp
# Using deleted
# Before state:
# -------------
#
# interface Ethernet1/1
# description Interface Ethernet1/1
# interface Ethernet1/2
# interface mgmt0
# description Management interface
# ip address dhcp
- name: Delete or return interface parameters to default settings
nxos_interfaces:
config:
- name: Ethernet1/1
state: deleted
# After state:
# ------------
#
# interface Ethernet1/1
# interface Ethernet1/2
# interface mgmt0
# description Management interface
# ip address dhcp
"""
RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
type: list
sample: >
The configuration returned will always be in the same format
of the parameters above.
commands:
description: The set of commands pushed to the remote device.
returned: always
type: list
sample: ['interface Ethernet1/1', 'mtu 1800']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.nxos.argspec.interfaces.interfaces import InterfacesArgs
from ansible.module_utils.network.nxos.config.interfaces.interfaces import Interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
supports_check_mode=True)
result = Interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

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

View file

@ -0,0 +1 @@
dependencies: []

View file

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

View file

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

View file

@ -0,0 +1,33 @@
---
- name: collect common test cases
find:
paths: "{{ role_path }}/tests/common"
patterns: "{{ testcase }}.yaml"
connection: local
register: test_cases
- name: collect nxapi test cases
find:
paths: "{{ role_path }}/tests/nxapi"
patterns: "{{ testcase }}.yaml"
connection: local
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 cases (connection=httpapi)
include: "{{ test_case_to_run }} ansible_connection=httpapi connection={{ nxapi }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run
- name: run test cases (connection=local)
include: "{{ test_case_to_run }} ansible_connection=local connection={{ nxapi }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,53 @@
---
- debug:
msg: "Start nxos_interfaces deleted integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- name: setup1
cli_config: &cleanup
config: |
default interface {{ test_int1 }}
- block:
- name: setup2
cli_config:
config: |
interface {{ test_int1 }}
description Test-interface1
shutdown
- name: Gather interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: interfaces
- name: deleted
nxos_interfaces: &deleted
state: deleted
register: result
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.before)|length == 0"
- "result.after|length == 0"
- "result.changed == true"
- "'interface {{ test_int1 }}' in result.commands"
- "'no description' in result.commands"
- "'no shutdown' in result.commands"
- name: Idempotence - deleted
nxos_interfaces: *deleted
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
always:
- name: teardown
cli_config: *cleanup

View file

@ -0,0 +1,50 @@
---
- debug:
msg: "Start nxos_interfaces merged integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- name: setup
cli_config: &cleanup
config: |
default interface {{ test_int1 }}
- block:
- name: Merged
nxos_interfaces: &merged
config:
- name: "{{ test_int1 }}"
description: Configured by Ansible
state: merged
register: result
- assert:
that:
- "result.changed == true"
- "result.before|length == 0"
- "'interface {{ test_int1 }}' in result.commands"
- "'description Configured by Ansible' in result.commands"
- name: Gather interfaces facts
nxos_facts:
gather_subset:
- '!all'
- '!min'
gather_network_resources: interfaces
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Merged
nxos_interfaces: *merged
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
always:
- name: teardown
cli_config: *cleanup

View file

@ -0,0 +1,63 @@
---
- debug:
msg: "Start nxos_interfaces overridden integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- name: setup1
cli_config: &cleanup
config: |
default interface {{ test_int1 }}
default interface {{ test_int2 }}
- block:
- name: setup2
cli_config:
config: |
interface {{ test_int1 }}
shutdown
- name: Gather interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: interfaces
- name: Overridden
nxos_interfaces: &overridden
config:
- name: "{{ test_int2 }}"
description: Configured by Ansible
state: overridden
register: result
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.before)|length == 0"
- "result.changed == true"
- "'interface {{ test_int1 }}' in result.commands"
- "'no shutdown' in result.commands"
- "'interface {{ test_int2 }}' in result.commands"
- "'description Configured by Ansible' in result.commands"
- name: Gather interfaces post facts
nxos_facts: *facts
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Overridden
nxos_interfaces: *overridden
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
always:
- name: teardown
cli_config: *cleanup

View file

@ -0,0 +1,60 @@
---
- debug:
msg: "Start nxos_interfaces replaced integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- name: setup1
cli_config: &cleanup
config: |
default interface {{ test_int1 }}
- block:
- name: setup2
cli_config:
config: |
interface {{ test_int1 }}
description Configured by Ansible
- name: Gather interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: interfaces
- name: Replaced
nxos_interfaces: &replaced
config:
- name: "{{ test_int1 }}"
mode: layer3
state: replaced
register: result
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.before)|length == 0"
- "result.changed == true"
- "'interface {{ test_int1 }}' in result.commands"
- "'no description' in result.commands"
- "'no switchport' in result.commands"
- name: Gather interfaces post facts
nxos_facts: *facts
- assert:
that:
- "ansible_facts.network_resources.interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Replaced
nxos_interfaces: *replaced
register: result
- assert:
that:
- "result.changed == false"
- "result.commands|length == 0"
always:
- name: teardown
cli_config: *cleanup

View file

@ -4689,15 +4689,13 @@ lib/ansible/modules/network/nxos/nxos_install_os.py metaclass-boilerplate
lib/ansible/modules/network/nxos/nxos_install_os.py validate-modules:E324
lib/ansible/modules/network/nxos/nxos_install_os.py validate-modules:E327
lib/ansible/modules/network/nxos/nxos_install_os.py validate-modules:E338
lib/ansible/modules/network/nxos/nxos_interface.py future-import-boilerplate
lib/ansible/modules/network/nxos/nxos_interface.py metaclass-boilerplate
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E322
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E324
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E326
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E327
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E337
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E338
lib/ansible/modules/network/nxos/nxos_interface.py validate-modules:E340
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E322
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E324
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E326
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E327
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E337
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E338
lib/ansible/modules/network/nxos/_nxos_interface.py validate-modules:E340
lib/ansible/modules/network/nxos/nxos_interface_ospf.py future-import-boilerplate
lib/ansible/modules/network/nxos/nxos_interface_ospf.py metaclass-boilerplate
lib/ansible/modules/network/nxos/nxos_interface_ospf.py validate-modules:E324

View file

@ -20,20 +20,20 @@ from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from units.compat.mock import patch
from ansible.modules.network.nxos import nxos_interface
from ansible.modules.network.nxos import _nxos_interface
from .nxos_module import TestNxosModule, load_fixture, set_module_args
class TestNxosInterfaceModule(TestNxosModule):
module = nxos_interface
module = _nxos_interface
def setUp(self):
super(TestNxosInterfaceModule, self).setUp()
self.mock_run_commands = patch('ansible.modules.network.nxos.nxos_interface.run_commands')
self.mock_run_commands = patch('ansible.modules.network.nxos._nxos_interface.run_commands')
self.run_commands = self.mock_run_commands.start()
self.mock_load_config = patch('ansible.modules.network.nxos.nxos_interface.load_config')
self.mock_load_config = patch('ansible.modules.network.nxos._nxos_interface.load_config')
self.load_config = self.mock_load_config.start()
def tearDown(self):