Add nxos_lag_interfaces resource module (#59031)

* Add nxos_lag_interfaces resource module

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

* ix CI failure

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

* module_utils nxos transport-provider fix

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

* CI failure

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

* nxos_lag_interfaces tests

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

* Integration test

Signed-off-by: Trishna Guha <trishnaguha17@gmail.com>
pull/59333/merge
Trishna Guha 5 years ago committed by GitHub
parent 04445f184b
commit 3bad4d6a50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -60,7 +60,9 @@ The following modules no longer exist:
Deprecation notices
-------------------
No notable changes
The following modules will be removed in Ansible 2.13. Please update update your playbooks accordingly.
* nxos_linkagg use :ref:`nxos_lag_interfaces <nxos_lag_interfaces_module>` instead.
Noteworthy module changes

@ -8,6 +8,7 @@ The arg spec for the nxos facts module.
"""
CHOICES = [
'all',
'lag_interfaces',
]

@ -0,0 +1,49 @@
# -*- 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_lag_interfaces module
"""
from __future__ import absolute_import, division, print_function
__metaclass__ = type
class Lag_interfacesArgs(object):
"""The arg spec for the nxos_lag_interfaces module
"""
def __init__(self, **kwargs):
pass
argument_spec = {'config': {'elements': 'dict',
'options': {'members': {'elements': 'dict',
'options': {'member': {'type': 'str'},
'mode': {'type': 'str',
'choices': ['active', 'on', 'passive']},
'force': {'type': 'bool'}},
'type': 'list'},
'name': {'required': True, 'type': 'str'}},
'type': 'list'},
'state': {'choices': ['merged', 'replaced', 'overridden', 'deleted'],
'default': 'merged',
'type': 'str'}}

@ -0,0 +1,258 @@
#
# -*- 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 junos_lag_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 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 Lag_interfaces(ConfigBase):
"""
The nxos_lag_interfaces class
"""
gather_subset = [
'!all',
'!min',
]
gather_network_resources = [
'lag_interfaces',
]
def __init__(self, module):
super(Lag_interfaces, self).__init__(module)
def get_lag_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)
lag_interfaces_facts = facts['ansible_network_resources'].get('lag_interfaces')
if not lag_interfaces_facts:
return []
return lag_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_lag_interfaces_facts = self.get_lag_interfaces_facts()
commands.extend(self.set_config(existing_lag_interfaces_facts))
if commands:
if not self._module.check_mode:
resp = self._connection.edit_config(commands)
if 'response' in resp:
for item in resp['response']:
if item:
err_str = item
if err_str.lower().startswith('cannot add'):
self._module.fail_json(msg=err_str)
result['changed'] = True
result['commands'] = commands
changed_lag_interfaces_facts = self.get_lag_interfaces_facts()
result['before'] = existing_lag_interfaces_facts
if result['changed']:
result['after'] = changed_lag_interfaces_facts
result['warnings'] = warnings
return result
def set_config(self, existing_lag_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
"""
want = self._module.params.get('config')
if want:
for w in want:
w.update(remove_empties(w))
if 'members' in w and w['members']:
for item in w['members']:
item.update({'member': normalize_interface(item['member'])})
have = existing_lag_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']
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))
if 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 = []
merged_commands = self.set_commands(w, have)
replaced_commands = self.del_intf_commands(w, have)
if merged_commands:
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
commands.extend(self.del_all_commands(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_all_commands(obj_in_have))
else:
if not have:
return commands
for h in have:
commands.extend(self.del_all_commands(h))
return commands
def diff_list_of_dicts(self, w, h):
diff = []
set_w = set(tuple(d.items()) for d in w)
set_h = set(tuple(d.items()) for d in h)
difference = set_w.difference(set_h)
for element in difference:
diff.append(dict((x, y) for x, y in element))
return diff
def intersect_list_of_dicts(self, w, h):
intersect = []
wmem = []
hmem = []
for d in w:
wmem.append({'member': d['member']})
for d in h:
hmem.append({'member': d['member']})
set_w = set(tuple(sorted(d.items())) for d in wmem)
set_h = set(tuple(sorted(d.items())) for d in hmem)
intersection = set_w.intersection(set_h)
for element in intersection:
intersect.append(dict((x, y) for x, y in element))
return intersect
def add_commands(self, diff, name):
commands = []
name = name.strip('port-channel')
for d in diff:
commands.append('interface' + ' ' + d['member'])
cmd = ''
group_cmd = 'channel-group {0}'.format(name)
if 'force' in d:
cmd = group_cmd + ' force ' + d['force']
if 'mode' in d:
if cmd:
cmd = cmd + ' mode ' + d['mode']
else:
cmd = group_cmd + ' mode ' + d['mode']
if not cmd:
cmd = group_cmd
commands.append(cmd)
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['members'], w['name'])
else:
diff = self.diff_list_of_dicts(w['members'], obj_in_have['members'])
commands = self.add_commands(diff, w['name'])
return commands
def del_all_commands(self, obj_in_have):
commands = []
if not obj_in_have:
return commands
for m in obj_in_have['members']:
commands.append('interface' + ' ' + m['member'])
commands.append('no channel-group')
return commands
def del_intf_commands(self, w, have):
commands = []
obj_in_have = search_obj_in_list(w['name'], have, 'name')
if obj_in_have:
lst_to_del = self.intersect_list_of_dicts(w['members'], obj_in_have['members'])
if lst_to_del:
for item in lst_to_del:
commands.append('interface' + ' ' + item['member'])
commands.append('no channel-group')
return commands

@ -12,6 +12,8 @@ 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.lag_interfaces.lag_interfaces import Lag_interfacesFacts
FACT_LEGACY_SUBSETS = dict(
default=Default,
@ -22,6 +24,7 @@ FACT_LEGACY_SUBSETS = dict(
features=Features,
)
FACT_RESOURCE_SUBSETS = dict(
lag_interfaces=Lag_interfacesFacts,
)

@ -0,0 +1,124 @@
#
# -*- 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 lag_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.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.nxos.utils.utils import get_interface_type, normalize_interface
class Lag_interfacesFacts(object):
""" The nxos lag_interfaces fact class
"""
def __init__(self, module, subspec='config', options='options'):
self._module = module
self.argument_spec = Lag_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 lag_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 | include channel-group')
config = re.split('(\n |)channel-group ', data)
config = list(dict.fromkeys(config))
for conf in config:
if conf:
obj = self.render_config(self.generated_spec, conf, connection)
if obj and len(obj.keys()) > 1:
objs.append(obj)
ansible_facts['ansible_network_resources'].pop('lag_interfaces', None)
facts = {}
if objs:
facts['lag_interfaces'] = []
params = utils.validate_config(self.argument_spec, {'config': objs})
for cfg in params['config']:
facts['lag_interfaces'].append(utils.remove_empties(cfg))
ansible_facts['ansible_network_resources'].update(facts)
return ansible_facts
def get_members(self, id, connection):
"""
Returns members associated with a channel-group
:param name: The channel group
:rtype: list
:returns: Members
"""
members = []
data = connection.get('show port-channel summary')
match = re.search(r'{0} (.+)(|\n)'.format(id), data)
if match:
interfaces = re.search(r'Eth\d(.+)$', match.group())
if interfaces:
for i in interfaces.group().split():
if get_interface_type(i[:-3]) != 'unknown':
members.append(normalize_interface(i[:-3]))
return members
def render_config(self, spec, conf, connection):
"""
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'(\d+)( |)(force )?(mode \S+)?', conf, re.M)
if match:
matches = match.groups()
config['name'] = 'port-channel' + str(matches[0])
config['members'] = []
members = self.get_members(config['name'].strip('port-channel'), connection)
if members:
for m in members:
m_dict = {}
if matches[2]:
m_dict['force'] = matches[2]
if matches[3]:
m_dict['mode'] = matches[3][5:]
m_dict['member'] = m
config['members'].append(m_dict)
else:
config = {}
lag_intf_cfg = utils.remove_empties(config)
# if lag interfaces config is not present return empty dict
if len(lag_intf_cfg) == 1:
return {}
else:
return lag_intf_cfg

@ -1135,8 +1135,9 @@ def is_text(cmd):
def is_local_nxapi(module):
transport = module.params['transport']
provider_transport = (module.params['provider'] or {}).get('transport')
transport = module.params.get('transport')
provider = module.params.get('provider')
provider_transport = provider['transport'] if provider else None
return 'nxapi' in (transport, provider_transport)

@ -3,37 +3,13 @@ import socket
from ansible.module_utils.six import iteritems
def search_obj_in_list(name, lst):
def search_obj_in_list(name, lst, identifier):
for o in lst:
if o['name'] == name:
if o[identifier] == name:
return o
return None
def eliminate_null_keys(cfg_dict):
if not cfg_dict:
return None
if isinstance(cfg_dict, list):
final_cfg = []
for cfg in cfg_dict:
final_cfg.append(_null_keys(cfg))
else:
final_cfg = _null_keys(cfg_dict)
return final_cfg
def _null_keys(cfg):
final_cfg = {}
for k, v in iteritems(cfg):
if v:
if isinstance(v, dict):
final_cfg.update(eliminate_null_keys(v))
else:
final_cfg.update({k: v})
return final_cfg
def validate_ipv4_addr(address):
address = address.split('/')[0]
try:

@ -9,7 +9,7 @@ __metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'status': ['deprecated'],
'supported_by': 'network'}
DOCUMENTATION = """
@ -21,6 +21,10 @@ short_description: Manage link aggregation groups on Cisco NXOS devices.
description:
- This module provides declarative management of link aggregation groups
on Cisco NXOS devices.
deprecated:
removed_in: '2.13'
alternative: nxos_lag_interfaces
why: Updated modules released with more functionality.
author:
- Trishna Guha (@trishnaguha)
notes:
@ -42,13 +46,16 @@ options:
- Mode for the link aggregation group.
choices: [ active, 'on', passive ]
default: 'on'
type: str
min_links:
description:
- Minimum number of ports required up
before bringing up the link aggregation group.
type: int
members:
description:
- List of interfaces that will be managed in the link aggregation group.
type: list
force:
description:
- When true it forces link aggregation group members to match what
@ -57,11 +64,13 @@ options:
default: 'no'
aggregate:
description: List of link aggregation definitions.
type: list
state:
description:
- State of the link aggregation group.
default: present
choices: ['present','absent']
type: str
purge:
description:
- Purge links not defined in the I(aggregate) parameter.

@ -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']
choices: ['all', 'lag_interfaces']
required: false
version_added: "2.9"
"""

@ -0,0 +1,231 @@
#!/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_lag_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_lag_interfaces
version_added: 2.9
short_description: Manages link aggregation groups of NX-OS Interfaces
description: This module manages attributes of link aggregation groups of NX-OS Interfaces.
author: Trishna Guha (@trishnaguha)
options:
config:
description: A list of link aggregation group configurations.
type: list
suboptions:
name:
description:
- Name of the link aggregation group (LAG).
type: str
required: true
members:
description:
- The list of interfaces that are part of the group.
type: list
suboptions:
member:
description:
- The interface name.
type: str
mode:
description:
- Link aggregation group (LAG).
type: str
choices:
- active
- on
- passive
force:
description:
- When true it forces link aggregation group members to match what
is declared in the members param. This can be used to remove members.
type: bool
state:
description:
- The state the configuration should be left in.
type: str
choices:
- merged
- replaced
- overridden
- deleted
default: merged
notes:
- Tested against NXOS 7.3.(0)D1(1) on VIRL.
- This module works with connection C(network_cli).
"""
EXAMPLES = """
# Using merged
# Before state:
# -------------
#
# interface Ethernet1/4
- name: Merge provided configuration with device configuration.
nxos_lag_interfaces:
config:
- name: port-channel99
members:
- member: Ethernet1/4
state: merged
# After state:
# ------------
#
# interface Ethernet1/4
# channel-group 99
# Using replaced
# Before state:
# -------------
#
# interface Ethernet1/4
# channel-group 99 mode active
- name: Replace device configuration of specified LAG attributes of given interfaces with provided configuration.
nxos_lag_interfaces:
config:
- name: port-channel10
members:
- member: Ethernet1/4
state: replaced
# After state:
# ------------
#
# interface Ethernet1/4
# channel-group 10
# Using overridden
# Before state:
# -------------
#
# interface Ethernet1/4
# channel-group 10
# interface Ethernet1/2
# channel-group 99 mode passive
- name: Override device configuration of all LAG attributes of given interfaces on device with provided configuration.
nxos_lag_interfaces:
config:
- name: port-channel20
members:
- member: Ethernet1/6
force: True
state: overridden
# After state:
# ------------
# interface Ethernet1/2
# interface Ethernet1/4
# interface Ethernet1/6
# channel-group 20 force
# Using deleted
# Before state:
# -------------
#
# interface Ethernet1/4
# channel-group 99 mode active
- name: Delete LAG attributes of given interface (This won't delete the port-channel itself).
nxos_lag_interfaces:
config:
- port-channel: port-channel99
state: deleted
- name: Delete LAG attributes of all the interfaces
nxos_lag_interfaces:
state: deleted
# After state:
# ------------
#
# interface Ethernet1/4
# no channel-group 99
"""
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: ['command 1', 'command 2', 'command 3']
"""
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.nxos.argspec.lag_interfaces.lag_interfaces import Lag_interfacesArgs
from ansible.module_utils.network.nxos.config.lag_interfaces.lag_interfaces import Lag_interfaces
def main():
"""
Main entry point for module execution
:returns: the result form module invocation
"""
module = AnsibleModule(argument_spec=Lag_interfacesArgs.argument_spec,
supports_check_mode=True)
result = Lag_interfaces(module).execute_module()
module.exit_json(**result)
if __name__ == '__main__':
main()

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

@ -0,0 +1,20 @@
---
- name: collect cli 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

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

@ -0,0 +1,66 @@
---
- debug:
msg: "Start nxos_lag_interfaces merged integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- name: enable feature lacp
nxos_feature:
feature: lacp
- name: setup
nxos_config:
lines:
- "channel-group 10"
parents: "{{ item }}"
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: Gather LAG interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: lag_interfaces
- name: deleted
nxos_lag_interfaces: &deleted
state: deleted
register: result
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.before)|length == 0"
- name: Gather LAG interfaces post facts
nxos_facts: *facts
- assert:
that:
- "result.after|length == 0"
- "result.changed == true"
- name: Idempotence - deleted
nxos_lag_interfaces: *deleted
register: result
- assert:
that:
- "result.changed == false"
- name: teardown
nxos_config:
lines:
- "no channel-group 10"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: disable feature lacp
nxos_feature:
feature: lacp
state: disabled

@ -0,0 +1,69 @@
---
- debug:
msg: "Start nxos_lag_interfaces merged integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- name: enable feature lacp
nxos_feature:
feature: lacp
- name: setup
nxos_config:
lines:
- "no channel-group"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: Merged
nxos_lag_interfaces: &merged
config:
- name: port-channel10
members:
- member: "{{ test_int1 }}"
- member: "{{ test_int2 }}"
state: merged
register: result
- assert:
that:
- "result.before|length == 0"
- "result.changed == true"
- name: Gather LAG interfaces facts
nxos_facts:
gather_subset:
- '!all'
- '!min'
gather_network_resources: lag_interfaces
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Merged
nxos_lag_interfaces: *merged
register: result
- assert:
that:
- "result.changed == false"
- name: teardown
nxos_config:
lines:
- "no channel-group"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: disable feature lacp
nxos_feature:
feature: lacp
state: disabled

@ -0,0 +1,78 @@
---
- debug:
msg: "Start nxos_lag_interfaces merged integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- set_fact: test_int3="{{ nxos_int3 }}"
- name: enable feature lacp
nxos_feature:
feature: lacp
- name: setup
nxos_config:
lines:
- "channel-group 10"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: Gather LAG interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: lag_interfaces
- name: Merged
nxos_lag_interfaces: &overridden
config:
- name: port-channel19
members:
- member: "{{ test_int3 }}"
state: merged
register: result
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.before)|length == 0"
- "result.changed == true"
- name: Gather LAG interfaces post facts
nxos_facts: *facts
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Overridden
nxos_lag_interfaces: *overridden
register: result
- assert:
that:
- "result.changed == false"
- name: teardown1
nxos_config:
lines:
- "no channel-group 10"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: teardown2
nxos_config:
lines:
- "no channel-group 19"
parents: "interface {{ test_int3 }}"
- name: disable feature lacp
nxos_feature:
feature: lacp
state: disabled

@ -0,0 +1,73 @@
---
- debug:
msg: "Start nxos_lag_interfaces merged integration tests connection={{ ansible_connection }}"
- set_fact: test_int1="{{ nxos_int1 }}"
- set_fact: test_int2="{{ nxos_int2 }}"
- name: enable feature lacp
nxos_feature:
feature: lacp
- name: setup
nxos_config:
lines:
- "channel-group 10"
parents: "{{ item }}"
ignore_errors: yes
loop:
- "interface {{ test_int1 }}"
- "interface {{ test_int2 }}"
- name: Gather LAG interfaces facts
nxos_facts: &facts
gather_subset:
- '!all'
- '!min'
gather_network_resources: lag_interfaces
- name: Replaced
nxos_lag_interfaces: &replaced
config:
- name: port-channel11
members:
- member: "{{ test_int2 }}"
mode: active
state: replaced
register: result
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.before)|length == 0"
- name: Gather LAG interfaces post facts
nxos_facts: *facts
- assert:
that:
- "ansible_facts.network_resources.lag_interfaces|symmetric_difference(result.after)|length == 0"
- name: Idempotence - Replaced
nxos_lag_interfaces: *replaced
register: result
- assert:
that:
- "result.changed == false"
- name: teardown1
nxos_config:
lines:
- "no channel-group 10"
parents: "interface {{ test_int1 }}"
- name: teardown2
nxos_config:
lines:
- "no channel-group 11"
parents: "interface {{ test_int2 }}"
- name: disable feature lacp
nxos_feature:
feature: lacp
state: disabled

@ -2649,8 +2649,6 @@ lib/ansible/modules/network/nxos/nxos_l2_interface.py E337
lib/ansible/modules/network/nxos/nxos_l2_interface.py E338
lib/ansible/modules/network/nxos/nxos_l3_interface.py E337
lib/ansible/modules/network/nxos/nxos_l3_interface.py E338
lib/ansible/modules/network/nxos/nxos_linkagg.py E337
lib/ansible/modules/network/nxos/nxos_linkagg.py E338
lib/ansible/modules/network/nxos/nxos_lldp.py E326
lib/ansible/modules/network/nxos/nxos_lldp.py E338
lib/ansible/modules/network/nxos/nxos_logging.py E337

Loading…
Cancel
Save