diff --git a/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py b/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py index e831bc8d3da..202577efac6 100644 --- a/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py +++ b/lib/ansible/module_utils/network/nxos/argspec/facts/facts.py @@ -13,6 +13,7 @@ CHOICES = [ 'telemetry', 'vlans', 'lacp', + 'lacp_interfaces', ] diff --git a/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py b/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000000..5f72f21a1b8 --- /dev/null +++ b/lib/ansible/module_utils/network/nxos/argspec/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,90 @@ +# +# -*- 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_lacp_interfaces module +""" +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +class Lacp_interfacesArgs(object): + """The arg spec for the nxos_lacp_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + 'config': { + 'elements': 'dict', + 'options': { + 'convergence': { + 'options': { + 'graceful': { + 'type': 'bool' + }, + 'vpc': { + 'type': 'bool' + } + }, + 'type': 'dict' + }, + 'links': { + 'options': { + 'max': { + 'type': 'int' + }, + 'min': { + 'type': 'int' + } + }, + 'type': 'dict' + }, + 'mode': { + 'choices': ['delay'], + 'type': 'str' + }, + 'name': { + 'required': True, + 'type': 'str' + }, + 'port_priority': { + 'type': 'int' + }, + 'rate': { + 'choices': ['fast', 'normal'], + 'type': 'str' + }, + 'suspend_individual': { + 'type': 'bool' + } + }, + 'type': 'list' + }, + 'state': { + 'choices': ['merged', 'replaced', 'overridden', 'deleted'], + 'default': 'merged', + 'type': 'str' + } + } diff --git a/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/__init__.py b/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000000..891d2466c8d --- /dev/null +++ b/lib/ansible/module_utils/network/nxos/config/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,284 @@ +# +# -*- 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_lacp_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, dict_diff, remove_empties +from ansible.module_utils.network.nxos.facts.facts import Facts +from ansible.module_utils.network.nxos.utils.utils import flatten_dict, search_obj_in_list, get_interface_type, normalize_interface + + +class Lacp_interfaces(ConfigBase): + """ + The nxos_lacp_interfaces class + """ + + gather_subset = [ + '!all', + '!min', + ] + + gather_network_resources = [ + 'lacp_interfaces', + ] + + exclude_params = [ + 'port_priority', + 'rate', + 'min', + 'max', + ] + + def __init__(self, module): + super(Lacp_interfaces, self).__init__(module) + + def get_lacp_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) + lacp_interfaces_facts = facts['ansible_network_resources'].get('lacp_interfaces') + if not lacp_interfaces_facts: + return [] + return lacp_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_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + commands.extend(self.set_config(existing_lacp_interfaces_facts)) + if commands: + if not self._module.check_mode: + self._connection.edit_config(commands) + result['changed'] = True + result['commands'] = commands + + changed_lacp_interfaces_facts = self.get_lacp_interfaces_facts() + + result['before'] = existing_lacp_interfaces_facts + if result['changed']: + result['after'] = changed_lacp_interfaces_facts + + result['warnings'] = warnings + return result + + def set_config(self, existing_lacp_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: + if get_interface_type(w['name']) not in ('portchannel', 'ethernet'): + self._module.fail_json(msg='This module works with either portchannel or ethernet') + w.update({'name': normalize_interface(w['name'])}) + want.append(remove_empties(w)) + have = existing_lacp_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(flatten_dict(w), have)) + elif state == 'replaced': + commands.extend(self._state_replaced(flatten_dict(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 = flatten_dict(search_obj_in_list(w['name'], have, 'name')) + diff = dict_diff(w, obj_in_have) + 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: + h = flatten_dict(h) + obj_in_want = flatten_dict(search_obj_in_list(h['name'], want, 'name')) + if h == obj_in_want: + continue + for w in want: + w = flatten_dict(w) + 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(flatten_dict(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 = flatten_dict(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(flatten_dict(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 'graceful' in obj: + commands.append('lacp graceful-convergence') + if 'vpc' in obj: + commands.append('no lacp vpn-convergence') + if 'suspend_individual' in obj: + commands.append('lacp suspend_individual') + if 'mode' in obj: + commands.append('no lacp mode ' + obj['mode']) + if 'max' in obj: + commands.append('no lacp max-bundle') + if 'min' in obj: + commands.append('no lacp min-links') + if 'port_priority' in obj: + commands.append('no lacp port-priority') + if 'rate' in obj: + commands.append('no lacp rate') + 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 'port_priority' in d: + commands.append('lacp port-priority ' + str(d['port_priority'])) + if 'rate' in d: + commands.append('lacp rate ' + str(d['rate'])) + if 'min' in d: + commands.append('lacp min-links ' + str(d['min'])) + if 'max' in d: + commands.append('lacp max-bundle ' + str(d['max'])) + if 'mode' in d: + commands.append('lacp mode ' + d['mode']) + if 'suspend_individual' in d: + if d['suspend_individual'] is True: + commands.append('lacp suspend-individual') + else: + commands.append('no lacp suspend-individual') + if 'graceful' in d: + if d['graceful'] is True: + commands.append('lacp graceful-convergence') + else: + commands.append('no lacp graceful-convergence') + if 'vpc' in d: + if d['vpc'] is True: + commands.append('lacp vpc-convergence') + else: + commands.append('no lacp vpc-convergence') + return commands + + def set_commands(self, w, have): + commands = [] + obj_in_have = flatten_dict(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 diff --git a/lib/ansible/module_utils/network/nxos/facts/facts.py b/lib/ansible/module_utils/network/nxos/facts/facts.py index 2d43e5103e2..2f15e1b9ff7 100644 --- a/lib/ansible/module_utils/network/nxos/facts/facts.py +++ b/lib/ansible/module_utils/network/nxos/facts/facts.py @@ -16,6 +16,7 @@ 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 from ansible.module_utils.network.nxos.facts.vlans.vlans import VlansFacts +from ansible.module_utils.network.nxos.facts.lacp_interfaces.lacp_interfaces import Lacp_interfacesFacts FACT_LEGACY_SUBSETS = dict( @@ -31,6 +32,7 @@ FACT_RESOURCE_SUBSETS = dict( telemetry=TelemetryFacts, vlans=VlansFacts, lacp=LacpFacts, + lacp_interfaces=Lacp_interfacesFacts, ) diff --git a/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/__init__.py b/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py b/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py new file mode 100644 index 00000000000..000e1de72bb --- /dev/null +++ b/lib/ansible/module_utils/network/nxos/facts/lacp_interfaces/lacp_interfaces.py @@ -0,0 +1,108 @@ +# +# -*- 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 lacp_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.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs +from ansible.module_utils.network.nxos.utils.utils import get_interface_type + + +class Lacp_interfacesFacts(object): + """ The nxos lacp_interfaces fact class + """ + + def __init__(self, module, subspec='config', options='options'): + self._module = module + self.argument_spec = Lacp_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 lacp_interfaces + :param connection: the device connection + :param ansible_facts: Facts dictionary + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + objs = [] + + if not data: + data = connection.get('show running-config | section ^interface') + + resources = data.split('interface ') + for resource in resources: + if resource and re.search(r'lacp', resource): + obj = self.render_config(self.generated_spec, resource) + if obj and len(obj.keys()) > 1: + objs.append(obj) + + ansible_facts['ansible_network_resources'].pop('lacp_interfaces', None) + facts = {} + if objs: + facts['lacp_interfaces'] = [] + params = utils.validate_config(self.argument_spec, {'config': objs}) + for cfg in params['config']: + facts['lacp_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['port_priority'] = utils.parse_conf_arg(conf, 'lacp port-priority') + config['rate'] = utils.parse_conf_arg(conf, 'lacp rate') + config['mode'] = utils.parse_conf_arg(conf, 'mode') + suspend_individual = re.search(r'no lacp suspend-individual', conf) + if suspend_individual: + config['suspend_individual'] = False + max_links = utils.parse_conf_arg(conf, 'lacp max-bundle') + if max_links: + config['links']['max'] = max_links + min_links = utils.parse_conf_arg(conf, 'lacp min-links') + if min_links: + config['links']['min'] = min_links + graceful = re.search(r'no lacp graceful-convergence', conf) + if graceful: + config['convergence']['gracefule'] = False + vpc = re.search(r'lacp vpc-convergence', conf) + if vpc: + config['convergence']['vpc'] = True + + return utils.remove_empties(config) diff --git a/lib/ansible/module_utils/network/nxos/utils/utils.py b/lib/ansible/module_utils/network/nxos/utils/utils.py index 10f83d6f941..90d2bf494a1 100644 --- a/lib/ansible/module_utils/network/nxos/utils/utils.py +++ b/lib/ansible/module_utils/network/nxos/utils/utils.py @@ -10,6 +10,20 @@ def search_obj_in_list(name, lst, identifier): return None +def flatten_dict(x): + result = {} + if not isinstance(x, dict): + return result + + for key, value in iteritems(x): + if isinstance(value, dict): + result.update(flatten_dict(value)) + else: + result[key] = value + + return result + + def validate_ipv4_addr(address): address = address.split('/')[0] try: diff --git a/lib/ansible/modules/network/nxos/nxos_facts.py b/lib/ansible/modules/network/nxos/nxos_facts.py index a168efbf4a0..78ce43b56ac 100644 --- a/lib/ansible/modules/network/nxos/nxos_facts.py +++ b/lib/ansible/modules/network/nxos/nxos_facts.py @@ -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'] + choices: ['all', 'lag_interfaces', 'telemetry', 'vlans', 'lacp', 'lacp_interfaces'] required: false version_added: "2.9" """ diff --git a/lib/ansible/modules/network/nxos/nxos_lacp_interfaces.py b/lib/ansible/modules/network/nxos/nxos_lacp_interfaces.py new file mode 100644 index 00000000000..a97a5159bf5 --- /dev/null +++ b/lib/ansible/modules/network/nxos/nxos_lacp_interfaces.py @@ -0,0 +1,258 @@ +#!/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_lacp_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_lacp_interfaces +version_added: 2.9 +short_description: Manage Link Aggregation Control Protocol (LACP) attributes of interfaces on Cisco NX-OS devices. +description: This module manages Link Aggregation Control Protocol (LACP) 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 LACP interfaces options. + type: list + elements: dict + suboptions: + name: + description: + - Name of the interface. + required: true + type: str + port_priority: + description: + - LACP port priority for the interface. Range 1-65535. + Applicable only for Ethernet. + type: int + rate: + description: + - Rate at which PDUs are sent by LACP. Applicable only for Ethernet. + At fast rate LACP is transmitted once every 1 second. + At normal rate LACP is transmitted every 30 seconds after the link is bundled. + type: str + choices: ['fast', 'normal'] + links: + description: + - This dict contains configurable options related to max and min port-channel links. + Applicable only for Port-channel. + type: dict + suboptions: + max: + description: + - Port-channel max bundle. + type: int + min: + description: + - Port-channel min links. + type: int + mode: + description: + - LACP mode. Applicable only for Port-channel. + type: str + choices: ['delay'] + suspend_individual: + description: + - port-channel lacp state. Disabling this will cause lacp to put the + port to individual state and not suspend the port in case it does not get + LACP BPDU from the peer ports in the port-channel. + type: bool + convergence: + description: + - This dict contains configurable options related to convergence. + Applicable only for Port-channel. + type: dict + suboptions: + graceful: + description: + - port-channel lacp graceful convergence. Disable this only with lacp ports + connected to Non-Nexus peer. Disabling this with Nexus peer can lead + to port suspension. + type: bool + vpc: + description: + - Enable lacp convergence for vPC port channels. + 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: +# ------------- +# + +- name: Merge provided configuration with device configuration. + nxos_lacp_interfaces: + config: + - name: Ethernet1/3 + port_priority: 5 + rate: fast + state: merged + +# After state: +# ------------ +# +# interface Ethernet1/3 +# lacp port-priority 5 +# lacp rate fast + + +# Using replaced + +# Before state: +# ------------- +# +# interface Ethernet1/3 +# lacp port-priority 5 +# interface port-channel11 +# lacp mode delay + +- name: Replace device lacp interfaces configuration with the given configuration. + nxos_lacp_interfaces: + config: + - name: port-channel11 + links: + min: 4 + state: replaced + +# After state: +# ------------ +# +# interface Ethernet1/3 +# lacp port-priority 5 +# interface port-channel11 +# lacp min-links 4 + + +# Using overridden + +# Before state: +# ------------- +# +# interface Ethernet1/3 +# lacp port-priority 5 +# interface port-channel11 +# lacp mode delay + +- name: Override device configuration of all LACP interfaces attributes of given interfaces on device with provided configuration. + nxos_lacp_interfaces: + config: + - name: port-channel11 + links: + min: 4 + state: overridden + +# After state: +# ------------ +# +# interface port-channel11 +# lacp min-links 4 + + +# Using deleted + +# Before state: +# ------------- +# +# interface Ethernet1/3 +# lacp port-priority 5 +# interface port-channel11 +# lacp mode delay + +- name: Delete LACP interfaces configurations. + nxos_lacp_interfaces: + state: deleted + +# After state: +# ------------ +# + + +""" +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 port-channel10', 'lacp min-links 5', 'lacp mode delay'] +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.network.nxos.argspec.lacp_interfaces.lacp_interfaces import Lacp_interfacesArgs +from ansible.module_utils.network.nxos.config.lacp_interfaces.lacp_interfaces import Lacp_interfaces + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + module = AnsibleModule(argument_spec=Lacp_interfacesArgs.argument_spec, + supports_check_mode=True) + + result = Lacp_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/nxos_lacp_interfaces/defaults/main.yaml b/test/integration/targets/nxos_lacp_interfaces/defaults/main.yaml new file mode 100644 index 00000000000..5f709c5aac1 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/test/integration/targets/nxos_lacp_interfaces/meta/main.yml b/test/integration/targets/nxos_lacp_interfaces/meta/main.yml new file mode 100644 index 00000000000..32cf5dda7ed --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/test/integration/targets/nxos_lacp_interfaces/tasks/cli.yaml b/test/integration/targets/nxos_lacp_interfaces/tasks/cli.yaml new file mode 100644 index 00000000000..6c7ea4a7f94 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tasks/cli.yaml @@ -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 diff --git a/test/integration/targets/nxos_lacp_interfaces/tasks/main.yaml b/test/integration/targets/nxos_lacp_interfaces/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/nxos_lacp_interfaces/tasks/nxapi.yaml b/test/integration/targets/nxos_lacp_interfaces/tasks/nxapi.yaml new file mode 100644 index 00000000000..04c99602e6b --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tasks/nxapi.yaml @@ -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 diff --git a/test/integration/targets/nxos_lacp_interfaces/tests/cli/deleted.yaml b/test/integration/targets/nxos_lacp_interfaces/tests/cli/deleted.yaml new file mode 100644 index 00000000000..7973a408248 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tests/cli/deleted.yaml @@ -0,0 +1,56 @@ +--- +- debug: + msg: "Start nxos_lacp_interfaces deleted integration tests connection={{ ansible_connection }}" + +- name: setup1 + cli_config: &cleanup + config: | + no interface port-channel5 + no interface port-channel10 + no feature lacp + +- block: + - name: setup2 + cli_config: + config: | + feature lacp + interface port-channel5 + lacp min-links 5 + interface port-channel10 + lacp mode delay + + - name: Gather lacp_interfaces facts + nxos_facts: &facts + gather_subset: + - '!all' + - '!min' + gather_network_resources: lacp_interfaces + + - name: deleted + nxos_lacp_interfaces: &deleted + state: deleted + register: result + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.before)|length == 0" + - "result.after|length == 0" + - "result.changed == true" + - "'interface port-channel5' in result.commands" + - "'no lacp min-links' in result.commands" + - "'interface port-channel10' in result.commands" + - "'no lacp mode delay' in result.commands" + - "result.commands|length == 4" + + - name: Idempotence - deleted + nxos_lacp_interfaces: *deleted + register: result + + - assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + always: + - name: teardown + cli_config: *cleanup diff --git a/test/integration/targets/nxos_lacp_interfaces/tests/cli/merged.yaml b/test/integration/targets/nxos_lacp_interfaces/tests/cli/merged.yaml new file mode 100644 index 00000000000..9aba9497e89 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tests/cli/merged.yaml @@ -0,0 +1,61 @@ +--- +- debug: + msg: "Start nxos_lacp_interfaces merged integration tests connection={{ ansible_connection }}" + +- name: setup1 + cli_config: &cleanup + config: | + no interface port-channel5 + no interface port-channel10 + no feature lacp + +- block: + - name: setup2 + cli_config: + config: | + feature lacp + + - name: Merged + nxos_lacp_interfaces: &merged + config: + - name: port-channel10 + links: + min: 5 + - name: port-channel5 + mode: delay + state: merged + register: result + + - assert: + that: + - "result.changed == true" + - "result.before|length == 0" + - "'interface port-channel10' in result.commands" + - "'lacp min-links 5' in result.commands" + - "'interface port-channel5' in result.commands" + - "'lacp mode delay' in result.commands" + - "result.commands|length == 4" + + - name: Gather lacp_interfaces facts + nxos_facts: + gather_subset: + - '!all' + - '!min' + gather_network_resources: lacp_interfaces + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.after)|length == 0" + + - name: Idempotence - Merged + nxos_lacp_interfaces: *merged + register: result + + - assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + always: + - name: teardown + cli_config: *cleanup diff --git a/test/integration/targets/nxos_lacp_interfaces/tests/cli/overridden.yaml b/test/integration/targets/nxos_lacp_interfaces/tests/cli/overridden.yaml new file mode 100644 index 00000000000..7b6b31cf4b7 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tests/cli/overridden.yaml @@ -0,0 +1,68 @@ +--- +- debug: + msg: "Start nxos_lacp_interfaces overridden integration tests connection={{ ansible_connection }}" + +- name: setup1 + cli_config: &cleanup + config: | + no interface port-channel5 + no interface port-channel10 + no interface port-channel11 + no feature lacp + +- block: + - name: setup2 + cli_config: + config: | + feature lacp + interface port-channel10 + lacp min-links 5 + interface port-channel5 + lacp max-bundle 10 + + - name: Gather lacp_interfaces facts + nxos_facts: &facts + gather_subset: + - '!all' + - '!min' + gather_network_resources: lacp_interfaces + + - name: Overridden + nxos_lacp_interfaces: &overridden + config: + - name: port-channel11 + mode: delay + state: overridden + register: result + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.before)|length == 0" + - "result.changed == true" + - "'interface port-channel10' in result.commands" + - "'no lacp min-links' in result.commands" + - "'interface port-channel5' in result.commands" + - "'no lacp max-bundle' in result.commands" + - "'interface port-channel11' in result.commands" + - "'lacp mode delay' in result.commands" + - "result.commands|length == 6" + + - name: Gather lacp_interfaces post facts + nxos_facts: *facts + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.after)|length == 0" + + - name: Idempotence - Overridden + nxos_lacp_interfaces: *overridden + register: result + + - assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + always: + - name: teardown + cli_config: *cleanup diff --git a/test/integration/targets/nxos_lacp_interfaces/tests/cli/replaced.yaml b/test/integration/targets/nxos_lacp_interfaces/tests/cli/replaced.yaml new file mode 100644 index 00000000000..024a9bb4db4 --- /dev/null +++ b/test/integration/targets/nxos_lacp_interfaces/tests/cli/replaced.yaml @@ -0,0 +1,62 @@ +--- +- debug: + msg: "Start nxos_lacp_interfaces replaced integration tests connection={{ ansible_connection }}" + +- name: setup1 + cli_config: &cleanup + config: | + no interface port-channel10 + no feature lacp + +- block: + - name: setup2 + cli_config: + config: | + feature lacp + interface port-channel10 + lacp min-links 5 + + - name: Gather lacp_interfaces facts + nxos_facts: &facts + gather_subset: + - '!all' + - '!min' + gather_network_resources: lacp_interfaces + + - name: Replaced + nxos_lacp_interfaces: &replaced + config: + - name: port-channel10 + links: + max: 10 + state: replaced + register: result + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.before)|length == 0" + - "result.changed == true" + - "'interface port-channel10' in result.commands" + - "'no lacp min-links' in result.commands" + - "'lacp max-bundle 10' in result.commands" + - "result.commands|length == 3" + + - name: Gather lacp_interfaces post facts + nxos_facts: *facts + + - assert: + that: + - "ansible_facts.network_resources.lacp_interfaces|symmetric_difference(result.after)|length == 0" + + - name: Idempotence - Replaced + nxos_lacp_interfaces: *replaced + register: result + + - assert: + that: + - "result.changed == false" + - "result.commands|length == 0" + + always: + - name: teardown + cli_config: *cleanup