From 667ef79435201e3f7ae2ad2d53df1f2127f261e6 Mon Sep 17 00:00:00 2001 From: Sabari Jaganathan Date: Fri, 21 Jan 2022 21:02:51 +0530 Subject: [PATCH 1/4] Added aci_bulk_static_binding_to_epg to bind multiple interfaces in one go and added test cases. --- .../modules/aci_bulk_static_binding_to_epg.py | 623 ++++++++++++++++++ .../aci_bulk_static_binding_to_epg/aliases | 2 + .../tasks/main.yml | 502 ++++++++++++++ 3 files changed, 1127 insertions(+) create mode 100644 plugins/modules/aci_bulk_static_binding_to_epg.py create mode 100644 tests/integration/targets/aci_bulk_static_binding_to_epg/aliases create mode 100644 tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml diff --git a/plugins/modules/aci_bulk_static_binding_to_epg.py b/plugins/modules/aci_bulk_static_binding_to_epg.py new file mode 100644 index 000000000..a64ebf357 --- /dev/null +++ b/plugins/modules/aci_bulk_static_binding_to_epg.py @@ -0,0 +1,623 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Bruno Calogero +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} + +DOCUMENTATION = r""" +--- +module: aci_bulk_static_binding_to_epg +short_description: Bind static paths to EPGs (fv:RsPathAtt) +description: +- Bind static paths to EPGs on Cisco ACI fabrics. +options: + tenant: + description: + - Name of the tenant. + type: str + aliases: [ tenant_name ] + ap: + description: + - The name of the application profile. + type: str + aliases: [ app_profile, app_profile_name ] + epg: + description: + - The name of the end point group. + type: str + aliases: [ epg_name ] + interface_configs: + description: + - List of interface configurations, elements in the form dictionary. + type: list + elements: dict + suboptions: + status: + description: + - Status of the individual interface object, This status can be overridden by interfaces_status. + - C(created) is used to bind an interface, C(deleted) is used to delete an interface and C(modified) is used to update an interface. + type: str + choices: [ created, deleted, modified ] + default: created + description: + description: + - Description for the static path to EPG binding. + type: str + aliases: [ descr ] + encap_id: + description: + - The encapsulation ID associating the C(epg) with the interface path. + - This acts as the secondary C(encap_id) when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096). + type: int + aliases: [ vlan, vlan_id ] + primary_encap_id: + description: + - Determines the primary encapsulation ID associating the C(epg) + with the interface path when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown. + - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation. + type: str + aliases: [ primary_vlan, primary_vlan_id ] + deploy_immediacy: + description: + - The Deployment Immediacy of Static EPG on PC, VPC or Interface. + - The APIC defaults to C(lazy) when unset during creation. + type: str + choices: [ immediate, lazy ] + interface_mode: + description: + - Determines how layer 2 tags will be read from and added to frames. + - Values C(802.1p) and C(native) are identical. + - Values C(access) and C(untagged) are identical. + - Values C(regular), C(tagged) and C(trunk) are identical. + - The APIC defaults to C(trunk) when unset during creation. + type: str + choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ] + aliases: [ interface_mode_name, mode ] + interface_type: + description: + - The type of interface for the static EPG deployment. + type: str + choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ] + default: switch_port + pod_id: + description: + - The pod number part of the tDn. + - C(pod_id) is usually an integer below C(10). + type: int + aliases: [ pod, pod_number ] + leafs: + description: + - The switch ID(s) that the C(interface) belongs to. + - When C(interface_type) is C(switch_port), C(port_channel), or C(fex), then C(leafs) is a string of the leaf ID. + - When C(interface_type) is C(vpc), then C(leafs) is a list with both leaf IDs. + - The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type). + type: list + elements: str + aliases: [ leaves, nodes, paths, switches ] + interface: + description: + - The C(interface) string value part of the tDn. + - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type). + type: str + extpaths: + description: + - The C(extpaths) integer value part of the tDn. + - C(extpaths) is only used if C(interface_type) is C(fex), C(fex_vpc) or C(fex_port_channel). + - When C(interface_type) is C(fex_vpc), then C(extpaths) is a list with both fex IDs. + - Usually something like C(1011). + type: list + elements: str + interfaces_status: + description: + - Interfaces status is used to override the entire interface_configs objects status. + - C(created) is used to bind interfaces, C(deleted) is used to delete interfaces and C(modified) is used to update interfaces. + type: str + choices: [ created, deleted, modified ] + state: + description: + - Use C(present) or C(absent) for adding or removing. + - Use C(query) for listing an object or multiple objects. + type: str + choices: [ absent, present, query ] + default: present +extends_documentation_fragment: +- cisco.aci.aci +- cisco.aci.annotation + +notes: +- The C(tenant), C(ap), C(epg) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_ap), M(cisco.aci.aci_epg) modules can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_ap +- module: cisco.aci.aci_epg +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fv:RsPathAtt). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Bruno Calogero (@brunocalogero) +- Marcel Zehnder (@maercu) +- Sabari Jaganathan (@sajagana) +""" + +EXAMPLES = r""" +- name: Add list of interfaces to an EPG + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_configs: [ + { + "encap_id": 221, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": switch_port, + "pod": 1, + "leafs": 101, + "interface": '1/7', + }, + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7' + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8' + }, + ] + delegate_to: localhost + +- name: Add, Modify and Delete list of interfaces to an EPG + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": deleted, + "descr": "Interface Deleted" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "status": created, + "descr": "Interface Created" + } + ] + delegate_to: localhost + +- name: Add list of interfaces without changing individual object status + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": deleted, + "descr": "Interface Deleted" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "status": created, + "descr": "Interface Created" + } + ] + interfaces_status: created + delegate_to: localhost + +- name: Query all interfaces of the EPG + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + state: query + delegate_to: localhost + +- name: Delete list of interfaces without changing individual object status + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + tenant: accessport-code-cert + ap: accessport_code_app + epg: accessport_epg1 + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": deleted, + "descr": "Interface Deleted" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "status": created, + "descr": "Interface Created" + } + ] + interfaces_status: deleted + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec + +INTERFACE_MODE_MAPPING = { + "802.1p": "native", + "access": "untagged", + "native": "native", + "regular": "regular", + "tagged": "regular", + "trunk": "regular", + "untagged": "untagged", +} + +INTERFACE_TYPE_MAPPING = dict( + fex="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]", + fex_port_channel="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[{interface}]", + fex_vpc="topology/pod-{pod_id}/protpaths-{leafs}/extprotpaths-{extpaths}/pathep-[{interface}]", + port_channel="topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]", + switch_port="topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]", + vpc="topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]", +) + +# TODO: change 'deploy_immediacy' to 'resolution_immediacy' (as seen in aci_epg_to_domain)? + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update( + tenant=dict(type="str", aliases=["tenant_name"]), + ap=dict(type="str", aliases=["app_profile", "app_profile_name"]), + epg=dict(type="str", aliases=["epg_name"]), + interface_configs=dict( + type="list", + elements="dict", + options=dict( + status=dict(type="str", default="created", choices=["created", "deleted", "modified"]), + description=dict(type="str", aliases=["descr"]), + encap_id=dict(type="int", aliases=["vlan", "vlan_id"]), + primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]), + deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]), + interface_mode=dict( + type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["interface_mode_name", "mode"] + ), + interface_type=dict(type="str", default="switch_port", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), + pod_id=dict(type="int", aliases=["pod", "pod_number"]), + leafs=dict(type="list", elements="str", aliases=["leaves", "nodes", "paths", "switches"]), + interface=dict(type="str"), + extpaths=dict(type="list", elements="str"), + ), + ), + interfaces_status=dict(type="str", choices=["created", "deleted", "modified"]), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["ap", "epg", "tenant"]], + ["state", "present", ["ap", "epg", "tenant"]], + ], + ) + + tenant = module.params.get("tenant") + ap = module.params.get("ap") + epg = module.params.get("epg") + interface_configs = module.params.get("interface_configs") + interfaces_status = module.params.get("interfaces_status") + state = module.params.get("state") + + aci = ACIModule(module) + children = [] + if state == "present": + for interface_config in interface_configs: + description = interface_config.get("description") + encap_id = interface_config.get("encap_id") + primary_encap_id = interface_config.get("primary_encap_id") + deploy_immediacy = interface_config.get("deploy_immediacy") + interface_mode = interface_config.get("interface_mode") + interface_type = interface_config.get("interface_type") + pod_id = interface_config.get("pod_id") + leafs = interface_config.get("leafs") + interface = interface_config.get("interface") + extpaths = interface_config.get("extpaths") + + if interface_type in ["fex", "fex_vpc", "fex_port_channel"] and extpaths is None: + aci.fail_json(msg="extpaths is required when interface_type is: {0}".format(interface_type)) + + if leafs is not None: + # Process leafs, and support dash-delimited leafs + leafs = [] + for leaf in interface_config.get("leafs"): + # Users are likely to use integers for leaf IDs, which would raise an exception when using the join method + leafs.extend(str(leaf).split("-")) + if len(leafs) == 1: + if interface_type in ["vpc", "fex_vpc"]: + aci.fail_json(msg='A interface_type of "vpc" requires 2 leafs') + leafs = leafs[0] + elif len(leafs) == 2: + if interface_type not in ["vpc", "fex_vpc"]: + aci.fail_json( + msg='The interface_types "switch_port", "port_channel", and "fex" do not support using multiple leafs for a single binding' + ) + leafs = "-".join(leafs) + else: + aci.fail_json(msg='The "leafs" parameter must not have more than 2 entries') + + if extpaths is not None: + # Process extpaths, and support dash-delimited extpaths + extpaths = [] + for extpath in interface_config.get("extpaths"): + # Users are likely to use integers for extpaths IDs, which would raise an exception when using the join method + extpaths.extend(str(extpath).split("-")) + if len(extpaths) == 1: + if interface_type == "fex_vpc": + aci.fail_json(msg='A interface_type of "fex_vpc" requires 2 extpaths') + extpaths = extpaths[0] + elif len(extpaths) == 2: + if interface_type != "fex_vpc": + aci.fail_json(msg='The interface_types "fex" and "fex_port_channel" do not support using multiple extpaths for a single binding') + extpaths = "-".join(extpaths) + else: + aci.fail_json(msg='The "extpaths" parameter must not have more than 2 entries') + + if encap_id is not None: + if encap_id not in range(1, 4097): + aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096") + encap_id = "vlan-{0}".format(encap_id) + + if primary_encap_id is not None: + try: + primary_encap_id = int(primary_encap_id) + if isinstance(primary_encap_id, int) and primary_encap_id in range(1, 4097): + primary_encap_id = "vlan-{0}".format(primary_encap_id) + else: + aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown.") + except Exception as e: + if isinstance(primary_encap_id, str) and primary_encap_id != "unknown": + aci.fail_json(msg="Valid VLAN assignments are from 1 to 4096 or unknown. %s" % e) + + static_path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leafs=leafs, extpaths=extpaths, interface=interface) + + if interface_mode is not None: + interface_mode = INTERFACE_MODE_MAPPING[interface_mode] + + # Override individual interface object status with interfaces_status + if interfaces_status is None: + interface_status = interface_config.get("status") + else: + interface_status = interfaces_status + + children.append( + dict( + fvRsPathAtt=dict( + attributes=dict( + descr=description, + encap=encap_id, + primaryEncap=primary_encap_id, + instrImedcy=deploy_immediacy, + mode=interface_mode, + tDn=static_path, + status=interface_status, + ) + ) + ) + ) + + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter=dict(name=tenant), + ), + subclass_1=dict( + aci_class="fvAp", + aci_rn="ap-{0}".format(ap), + module_object=ap, + target_filter=dict(name=ap), + ), + subclass_2=dict( + aci_class="fvAEPg", + aci_rn="epg-{0}".format(epg), + module_object=epg, + target_filter=dict(name=epg), + ), + child_classes=["fvRsPathAtt"], + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="fvAEPg", + class_config=dict(), + child_configs=children, + ) + + aci.get_diff(aci_class="fvAEPg") + + aci.post_config() + + elif state == "absent": + aci.fail_json(msg="This module does not support delete operation") + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_bulk_static_binding_to_epg/aliases b/tests/integration/targets/aci_bulk_static_binding_to_epg/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_bulk_static_binding_to_epg/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml new file mode 100644 index 000000000..3240e2ce3 --- /dev/null +++ b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml @@ -0,0 +1,502 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Sabari Jaganathan (@sajagana) + +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +- name: Test that we have an ACI APIC host, ACI username and ACI password + fail: + msg: 'Please define the following variables: aci_hostname, aci_username and aci_password.' + when: aci_hostname is not defined or aci_username is not defined or aci_password is not defined + +- name: Set vars + set_fact: + aci_info: &aci_info + host: "{{ aci_hostname }}" + username: "{{ aci_username }}" + password: "{{ aci_password }}" + validate_certs: '{{ aci_validate_certs | default(false) }}' + use_ssl: '{{ aci_use_ssl | default(true) }}' + use_proxy: '{{ aci_use_proxy | default(true) }}' + output_level: debug + +- name: Ensure anstest tenant does not exists + cisco.aci.aci_tenant: &tenant_absent + <<: *aci_info + tenant: anstest + state: absent + +- name: Ensure anstest tenant exists + cisco.aci.aci_tenant: &tenant_present + <<: *tenant_absent + state: present + +- name: Ensure anstest ap exists + cisco.aci.aci_ap: &ap_present + <<: *tenant_present + ap: anstest + +- name: Ensure anstest epg exists + cisco.aci.aci_epg: &epg_present + <<: *ap_present + epg: anstest + +- name: Add list of interfaces with individual objects + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + interface_configs: [ + { + "encap_id": 221, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": switch_port, + "pod": 1, + "leafs": 101, + "interface": '1/7', + }, + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7' + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8' + }, + ] + register: created_interfaces + +- name: Assertions check for add list of interfaces with individual objects + assert: + that: + - created_interfaces is changed + - created_interfaces.current.0.fvAEPg.children | length == 3 + - '"children" not in created_interfaces.previous.0.fvAEPg' + - created_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" + - created_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + - created_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" + +- name: Update list of interfaces with individual object status of the anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + interface_configs: [ + { + "encap_id": 221, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": switch_port, + "pod": 1, + "leafs": 101, + "interface": '1/7', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": modified, + "descr": "Interface Modified" + }, + ] + register: modified_interfaces + +- name: Assertions check for update list of interfaces with individual object status + assert: + that: + - modified_interfaces is changed + - modified_interfaces.current.0.fvAEPg.children | length == 3 + - modified_interfaces.previous.0.fvAEPg.children | length == 3 + - modified_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Interface Modified" + - modified_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Interface Modified" + - modified_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Interface Modified" + - modified_interfaces.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" + - modified_interfaces.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + - modified_interfaces.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" + +- name: Use different statues to perform create, delete and modify the interface objects + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": deleted, + "descr": "Interface Deleted" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": modified, + "descr": "Interface Modified" + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "status": created, + "descr": "Interface Created" + } + ] + register: multi_operation_interfaces + +- name: Assertions check for multi status interface objects validation + assert: + that: + - multi_operation_interfaces is changed + - multi_operation_interfaces.current.0.fvAEPg.children | length == 3 + - multi_operation_interfaces.previous.0.fvAEPg.children | length == 3 + +- name: Query all interfaces with EPG name to check individual interface properties + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + state: query + register: all_interfaces_result_1 + +- name: Assertions check for query all interfaces with EPG name to check individual interface properties + assert: + that: + - all_interfaces_result_1 is not changed + - all_interfaces_result_1.current.0.fvAEPg.children | length == 3 + +- name: Remove list of interfaces with individual object status of the anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + interface_configs: [ + { + "encap_id": 221, + "pod": 1, + "leafs": 101, + "interface": '1/7', + "status": deleted, + }, + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "status": deleted, + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "status": deleted, + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "status": deleted, + } + ] + register: deleted_interfaces_with_status + +- name: Assertions check for remove list of interfaces with individual object status of the anstest epg + assert: + that: + - deleted_interfaces_with_status is changed + - '"children" not in deleted_interfaces_with_status.current.0.fvAEPg' + - deleted_interfaces_with_status.previous.0.fvAEPg.children | length == 3 + - deleted_interfaces_with_status.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "deleted" + - deleted_interfaces_with_status.sent.fvAEPg.children.1.fvRsPathAtt.attributes.status == "deleted" + - deleted_interfaces_with_status.sent.fvAEPg.children.2.fvRsPathAtt.attributes.status == "deleted" + - deleted_interfaces_with_status.sent.fvAEPg.children.3.fvRsPathAtt.attributes.status == "deleted" + +- name: Add list of interfaces with interfaces_status to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: &cm_created_interfaces + <<: *epg_present + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + } + ] + interfaces_status: created + check_mode: yes + register: cm_created_interfaces + +- name: Assertions check for add list of interfaces with interfaces_status - check mode + assert: + that: + - cm_created_interfaces is changed + - '"children" not in cm_created_interfaces.current.0.fvAEPg' + - '"children" not in cm_created_interfaces.previous.0.fvAEPg' + - cm_created_interfaces.sent.fvAEPg.children | length == 3 + +- name: Add list of interfaces with interfaces_status to anstest epg - normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_created_interfaces + <<: *cm_created_interfaces + register: nm_created_interfaces + +- name: Assertions check for add list of interfaces with interfaces_status - normal mode + assert: + that: + - nm_created_interfaces is changed + - nm_created_interfaces.current.0.fvAEPg.children | length == 3 + - '"children" not in nm_created_interfaces.previous.0.fvAEPg' + - nm_created_interfaces.sent.fvAEPg.children | length == 3 + +- name: Add list of interfaces with interfaces_status to anstest epg - normal mode idempotency works + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *nm_created_interfaces + register: idempotency_created_interfaces + ignore_errors: yes + +- name: Idempotency assertions check for add list of interfaces with interfaces_status - normal mode + assert: + that: + - idempotency_created_interfaces is not changed + - idempotency_created_interfaces.response == "HTTP Error 400: Bad Request" + - '"children" in idempotency_created_interfaces.previous.0.fvAEPg' + - idempotency_created_interfaces.previous.0.fvAEPg.children | length == 3 + - idempotency_created_interfaces.sent.fvAEPg.children | length == 3 + +- name: Update list of interfaces with interfaces_status + cisco.aci.aci_bulk_static_binding_to_epg: &updated_interfaces + <<: *epg_present + interface_configs: [ + { + "vlan_id": 107, + "pod": 7, + "leafs": 107, + "interface": '1/7', + "descr": "Interface Modified" + }, + { + "vlan_id": 108, + "pod": 8, + "leafs": 108, + "interface": '1/8', + "descr": "Interface Modified" + }, + { + "vlan_id": 109, + "pod": 9, + "leafs": 109, + "interface": '1/9', + "descr": "Interface Modified" + } + ] + interfaces_status: modified + register: updated_interfaces + +- name: Assertions check for update list of interfaces with interfaces_status + assert: + that: + - updated_interfaces is changed + - updated_interfaces.current.0.fvAEPg.children | length == 3 + - updated_interfaces.previous.0.fvAEPg.children | length == 3 + - updated_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Interface Modified" + - updated_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Interface Modified" + - updated_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Interface Modified" + - updated_interfaces.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" + - updated_interfaces.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + - updated_interfaces.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" + +- name: Query all interfaces with EPG name - which is added by interfaces_status + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + state: query + register: all_interfaces_result_2 + +- name: Assertions check for query all interfaces with EPG name - which is added by interfaces_status + assert: + that: + - all_interfaces_result_2 is not changed + - all_interfaces_result_2.current.0.fvAEPg.children | length == 3 + - all_interfaces_result_2.filter_string == "?rsp-subtree=full&rsp-subtree-class=fvRsPathAtt" + +- name: Remove list of interfaces with interfaces_status from anstest epg - check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_deleted_list_of_interfaces + <<: *updated_interfaces + interfaces_status: deleted + check_mode: yes + register: cm_deleted_list_of_interfaces + +- name: Assertions check for remove list of interfaces with interfaces_status from anstest epg - check mode + assert: + that: + - cm_deleted_list_of_interfaces is changed + - cm_deleted_list_of_interfaces.current.0.fvAEPg.children | length == 3 + - cm_deleted_list_of_interfaces.previous.0.fvAEPg.children | length == 3 + - cm_deleted_list_of_interfaces.sent.fvAEPg.children | length == 3 + +- name: Remove list of interfaces with interfaces_status from anstest epg - normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_deleted_list_of_interfaces + <<: *cm_deleted_list_of_interfaces + register: nm_deleted_list_of_interfaces + +- name: Assertions check for remove list of interfaces with interfaces_status from anstest epg - normal mode + assert: + that: + - nm_deleted_list_of_interfaces is changed + - '"children" not in nm_deleted_list_of_interfaces.current.0.fvAEPg' + - nm_deleted_list_of_interfaces.previous.0.fvAEPg.children | length == 3 + - nm_deleted_list_of_interfaces.sent.fvAEPg.children | length == 3 + +- name: Remove list of interfaces with interfaces_status from anstest epg - normal mode idempotency works + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *nm_deleted_list_of_interfaces + register: idempotency_deleted_list_of_interfaces + +- name: Idempotency assertions check for remove list of interfaces with interfaces_status from anstest epg - normal mode + assert: + that: + - idempotency_deleted_list_of_interfaces is changed + - '"children" not in idempotency_deleted_list_of_interfaces.current.0.fvAEPg' + - '"children" not in idempotency_deleted_list_of_interfaces.previous.0.fvAEPg' + +- name: Add fex_port_channel interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *aci_info + tenant: anstest + ap: anstest + epg: anstest + interface_configs: [ + { + "encap_id": 222, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": fex_port_channel, + "pod": 2, + "leafs": 102, + "interface": '2/7', + "extpaths": [ + 1012 + ], + "descr": "fex_port_channel - interface created" + } + ] + register: fex_port_channel_present + +- name: Assertions check for add fex_port_channel interfaces to anstest epg + assert: + that: + - fex_port_channel_present is changed + - fex_port_channel_present.current.0.fvAEPg.children | length == 1 + - '"children" not in fex_port_channel_present.previous.0.fvAEPg' + - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-222" + - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" + - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-2/paths-102/extpaths-1012/pathep-[2/7]" + - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_port_channel - interface created" + +- name: Add fex_vpc interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *aci_info + tenant: anstest + ap: anstest + epg: anstest + interface_configs: [ + { + "encap_id": 223, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": fex_vpc, + "pod": 3, + "leafs": [ + 103, + 104 + ], + "interface": '3/7', + "extpaths": [ + 103, + 104 + ], + "descr": "fex_vpc - interface created" + } + ] + register: fex_vpc_present + +- name: Assertions check for add fex_vpc interfaces to anstest epg + assert: + that: + - fex_vpc_present is changed + - fex_vpc_present.current.0.fvAEPg.children | length == 2 + - fex_vpc_present.previous.0.fvAEPg.children | length == 1 + - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-223" + - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" + - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-3/protpaths-103-104/extprotpaths-103-104/pathep-[3/7]" + - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_vpc - interface created" + +- name: Add vpc interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + interface_configs: [ + { + "encap_id": 224, + "deploy_immediacy": lazy, + "interface_mode": trunk, + "interface_type": vpc, + "pod": 4, + "leafs": [ + 105, + 106 + ], + "interface": '4/7', + "extpaths": [ + 1015 + ], + "descr": "vpc - interface created" + } + ] + register: vpc_present + +- name: Assertions check for add vpc interfaces to anstest epg + assert: + that: + - vpc_present is changed + - vpc_present.current.0.fvAEPg.children | length == 3 + - vpc_present.previous.0.fvAEPg.children | length == 2 + - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-224" + - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" + - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-4/protpaths-105-106/pathep-[4/7]" + - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "vpc - interface created" + +# Cleanup part +- name: Remove anstest epg + cisco.aci.aci_epg: + <<: *epg_present + state: absent + +- name: Remove anstest ap + cisco.aci.aci_ap: + <<: *ap_present + state: absent + +- name: Remove anstest tenant + cisco.aci.aci_tenant: + <<: *tenant_present + state: absent From 3ba174be83147c2380d3642c17979f6d8480d968 Mon Sep 17 00:00:00 2001 From: Sabari Jaganathan Date: Fri, 28 Jan 2022 18:22:11 +0530 Subject: [PATCH 2/4] Added generic attributes on module and path level. --- .../modules/aci_bulk_static_binding_to_epg.py | 282 +++--- .../tasks/main.yml | 822 ++++++++++-------- 2 files changed, 579 insertions(+), 525 deletions(-) diff --git a/plugins/modules/aci_bulk_static_binding_to_epg.py b/plugins/modules/aci_bulk_static_binding_to_epg.py index a64ebf357..bb7a7ce0b 100644 --- a/plugins/modules/aci_bulk_static_binding_to_epg.py +++ b/plugins/modules/aci_bulk_static_binding_to_epg.py @@ -33,19 +33,55 @@ - The name of the end point group. type: str aliases: [ epg_name ] + description: + description: + - Description for the static path to EPG binding. + type: str + aliases: [ descr ] + encap_id: + description: + - The encapsulation ID associating the C(epg) with the interface path. + - This acts as the secondary C(encap_id) when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096). + type: int + aliases: [ vlan, vlan_id ] + primary_encap_id: + description: + - Determines the primary encapsulation ID associating the C(epg) + with the interface path when using micro-segmentation. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown. + - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation. + type: str + aliases: [ primary_vlan, primary_vlan_id ] + deploy_immediacy: + description: + - The Deployment Immediacy of Static EPG on PC, VPC or Interface. + - The APIC defaults to C(lazy) when unset during creation. + type: str + choices: [ immediate, lazy ] + interface_mode: + description: + - Determines how layer 2 tags will be read from and added to frames. + - Values C(802.1p) and C(native) are identical. + - Values C(access) and C(untagged) are identical. + - Values C(regular), C(tagged) and C(trunk) are identical. + - The APIC defaults to C(trunk) when unset during creation. + type: str + choices: [ 802.1p, access, native, regular, tagged, trunk, untagged ] + aliases: [ interface_mode_name, mode ] + interface_type: + description: + - The type of interface for the static EPG deployment. + type: str + choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ] + default: switch_port interface_configs: description: - - List of interface configurations, elements in the form dictionary. + - List of interface configurations, elements in the form of a dictionary. + - Module level attributes will be overridden by the path level attributes. type: list elements: dict suboptions: - status: - description: - - Status of the individual interface object, This status can be overridden by interfaces_status. - - C(created) is used to bind an interface, C(deleted) is used to delete an interface and C(modified) is used to update an interface. - type: str - choices: [ created, deleted, modified ] - default: created description: description: - Description for the static path to EPG binding. @@ -87,7 +123,6 @@ - The type of interface for the static EPG deployment. type: str choices: [ fex, port_channel, switch_port, vpc, fex_port_channel, fex_vpc ] - default: switch_port pod_id: description: - The pod number part of the tDn. @@ -116,12 +151,6 @@ - Usually something like C(1011). type: list elements: str - interfaces_status: - description: - - Interfaces status is used to override the entire interface_configs objects status. - - C(created) is used to bind interfaces, C(deleted) is used to delete interfaces and C(modified) is used to update interfaces. - type: str - choices: [ created, deleted, modified ] state: description: - Use C(present) or C(absent) for adding or removing. @@ -150,7 +179,7 @@ """ EXAMPLES = r""" -- name: Add list of interfaces to an EPG +- name: Create list of interfaces using module level attributes cisco.aci.aci_bulk_static_binding_to_epg: host: apic username: admin @@ -158,32 +187,25 @@ tenant: accessport-code-cert ap: accessport_code_app epg: accessport_epg1 - interface_configs: [ - { - "encap_id": 221, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": switch_port, - "pod": 1, - "leafs": 101, - "interface": '1/7', - }, - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7' - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8' - }, - ] + encap_id: 221 + interface_mode: trunk + deploy_immediacy: lazy + description: "Module level attributes used to create interfaces" + interface_configs: + - interface: 1/7 + leafs: 101 + pod: 1 + - interface: 1/7 + leafs: 107 + pod: 7 + - interface: 1/8 + leafs: 108 + pod: 8 + encap_id: 108 + state: present delegate_to: localhost -- name: Add, Modify and Delete list of interfaces to an EPG +- name: Create/Update list of interfaces using path level attributes cisco.aci.aci_bulk_static_binding_to_epg: host: apic username: admin @@ -191,35 +213,32 @@ tenant: accessport-code-cert ap: accessport_code_app epg: accessport_epg1 - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": deleted, - "descr": "Interface Deleted" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "status": created, - "descr": "Interface Created" - } - ] + interface_configs: + - interface: 1/7 + leafs: 101 + pod: 1 + encap_id: 221 + interface_mode: trunk + deploy_immediacy: lazy + description: "Path level attributes used to create/update interfaces" + - interface: 1/7 + leafs: 107 + pod: 7 + encap_id: 221 + interface_mode: trunk + deploy_immediacy: lazy + description: "Path level attributes used to create/update interfaces" + - interface: 1/8 + leafs: 108 + pod: 8 + encap_id: 108 + interface_mode: trunk + deploy_immediacy: lazy + description: "Path level attributes used to create/update interfaces" + state: present delegate_to: localhost -- name: Add list of interfaces without changing individual object status +- name: Query all interfaces of an EPG cisco.aci.aci_bulk_static_binding_to_epg: host: apic username: admin @@ -227,47 +246,18 @@ tenant: accessport-code-cert ap: accessport_code_app epg: accessport_epg1 - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": deleted, - "descr": "Interface Deleted" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "status": created, - "descr": "Interface Created" - } - ] - interfaces_status: created + state: query delegate_to: localhost -- name: Query all interfaces of the EPG +- name: Query all interfaces cisco.aci.aci_bulk_static_binding_to_epg: host: apic username: admin password: SomeSecretPassword - tenant: accessport-code-cert - ap: accessport_code_app - epg: accessport_epg1 - state: query + state: query delegate_to: localhost -- name: Delete list of interfaces without changing individual object status +- name: Remove list of interfaces cisco.aci.aci_bulk_static_binding_to_epg: host: apic username: admin @@ -275,33 +265,21 @@ tenant: accessport-code-cert ap: accessport_code_app epg: accessport_epg1 - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": deleted, - "descr": "Interface Deleted" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "status": created, - "descr": "Interface Created" - } - ] - interfaces_status: deleted + encap_id: 221 + interface_mode: trunk + deploy_immediacy: lazy + interface_configs: + - interface: 1/7 + leafs: 101 + pod: 1 + - interface: 1/7 + leafs: 107 + pod: 7 + - interface: 1/8 + leafs: 108 + pod: 8 + encap_id: 108 + state: absent delegate_to: localhost """ @@ -442,11 +420,18 @@ def main(): tenant=dict(type="str", aliases=["tenant_name"]), ap=dict(type="str", aliases=["app_profile", "app_profile_name"]), epg=dict(type="str", aliases=["epg_name"]), + description=dict(type="str", aliases=["descr"]), + encap_id=dict(type="int", aliases=["vlan", "vlan_id"]), + primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]), + deploy_immediacy=dict(type="str", choices=["immediate", "lazy"]), + interface_mode=dict( + type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["interface_mode_name", "mode"] + ), + interface_type=dict(type="str", default="switch_port", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), interface_configs=dict( type="list", elements="dict", options=dict( - status=dict(type="str", default="created", choices=["created", "deleted", "modified"]), description=dict(type="str", aliases=["descr"]), encap_id=dict(type="int", aliases=["vlan", "vlan_id"]), primary_encap_id=dict(type="str", aliases=["primary_vlan", "primary_vlan_id"]), @@ -454,14 +439,13 @@ def main(): interface_mode=dict( type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["interface_mode_name", "mode"] ), - interface_type=dict(type="str", default="switch_port", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), + interface_type=dict(type="str", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), pod_id=dict(type="int", aliases=["pod", "pod_number"]), leafs=dict(type="list", elements="str", aliases=["leaves", "nodes", "paths", "switches"]), interface=dict(type="str"), extpaths=dict(type="list", elements="str"), ), ), - interfaces_status=dict(type="str", choices=["created", "deleted", "modified"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) @@ -477,25 +461,33 @@ def main(): tenant = module.params.get("tenant") ap = module.params.get("ap") epg = module.params.get("epg") + module_description = module.params.get("description") + module_encap_id = module.params.get("encap_id") + module_primary_encap_id = module.params.get("primary_encap_id") + module_deploy_immediacy = module.params.get("deploy_immediacy") + module_interface_mode = module.params.get("interface_mode") + module_interface_type = module.params.get("interface_type") interface_configs = module.params.get("interface_configs") - interfaces_status = module.params.get("interfaces_status") state = module.params.get("state") aci = ACIModule(module) children = [] - if state == "present": + interface_status_mapping = {"absent": "deleted"} + + if state == "present" or state == "absent": for interface_config in interface_configs: - description = interface_config.get("description") - encap_id = interface_config.get("encap_id") - primary_encap_id = interface_config.get("primary_encap_id") - deploy_immediacy = interface_config.get("deploy_immediacy") - interface_mode = interface_config.get("interface_mode") - interface_type = interface_config.get("interface_type") pod_id = interface_config.get("pod_id") leafs = interface_config.get("leafs") interface = interface_config.get("interface") extpaths = interface_config.get("extpaths") + description = interface_config.get("description") or module_description + deploy_immediacy = interface_config.get("deploy_immediacy") or module_deploy_immediacy + interface_type = interface_config.get("interface_type") or module_interface_type + encap_id = interface_config.get("encap_id") or module_encap_id + primary_encap_id = interface_config.get("primary_encap_id") or module_primary_encap_id + interface_mode = interface_config.get("interface_mode") or module_interface_mode + if interface_type in ["fex", "fex_vpc", "fex_port_channel"] and extpaths is None: aci.fail_json(msg="extpaths is required when interface_type is: {0}".format(interface_type)) @@ -553,14 +545,9 @@ def main(): static_path = INTERFACE_TYPE_MAPPING[interface_type].format(pod_id=pod_id, leafs=leafs, extpaths=extpaths, interface=interface) - if interface_mode is not None: - interface_mode = INTERFACE_MODE_MAPPING[interface_mode] + interface_mode = INTERFACE_MODE_MAPPING.get(interface_mode) - # Override individual interface object status with interfaces_status - if interfaces_status is None: - interface_status = interface_config.get("status") - else: - interface_status = interfaces_status + interface_status = interface_status_mapping.get(state) children.append( dict( @@ -602,7 +589,7 @@ def main(): aci.get_existing() - if state == "present": + if state == "present" or state == "absent": aci.payload( aci_class="fvAEPg", class_config=dict(), @@ -613,9 +600,6 @@ def main(): aci.post_config() - elif state == "absent": - aci.fail_json(msg="This module does not support delete operation") - aci.exit_json() diff --git a/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml index 3240e2ce3..b51337d6b 100644 --- a/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml +++ b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml @@ -40,450 +40,520 @@ <<: *ap_present epg: anstest -- name: Add list of interfaces with individual objects - cisco.aci.aci_bulk_static_binding_to_epg: +- name: Add list of interfaces with check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_interfaces_present <<: *epg_present - interface_configs: [ - { - "encap_id": 221, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": switch_port, - "pod": 1, - "leafs": 101, - "interface": '1/7', - }, - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7' - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8' - }, - ] - register: created_interfaces - -- name: Assertions check for add list of interfaces with individual objects + interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface_configs: + - interface: 1/7 + leafs: 101 + pod: 1 + - interface: 1/7 + leafs: 107 + pod: 7 + - interface: 1/8 + leafs: 108 + pod: 8 + encap_id: 108 + state: present + check_mode: yes + register: cm_interfaces_present + +- name: Assertions check for add list of interfaces with check mode + assert: + that: + - cm_interfaces_present is changed + - cm_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - "'children' not in cm_interfaces_present.previous.0.fvAEPg" + - "'children' not in cm_interfaces_present.current.0.fvAEPg" + +- name: Add list of interfaces with normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_interfaces_present + <<: *cm_interfaces_present + register: nm_interfaces_present + +- name: Assertions check for add list of interfaces with normal mode assert: that: - - created_interfaces is changed - - created_interfaces.current.0.fvAEPg.children | length == 3 - - '"children" not in created_interfaces.previous.0.fvAEPg' - - created_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" - - created_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" - - created_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" - -- name: Update list of interfaces with individual object status of the anstest epg + - nm_interfaces_present is changed + - nm_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - nm_interfaces_present.current.0.fvAEPg.children | length == 3 + - "'children' not in nm_interfaces_present.previous.0.fvAEPg" + - nm_interfaces_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" + +- name: Add list of interfaces with normal mode - idempotency works cisco.aci.aci_bulk_static_binding_to_epg: - <<: *epg_present - interface_configs: [ - { - "encap_id": 221, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": switch_port, - "pod": 1, - "leafs": 101, - "interface": '1/7', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": modified, - "descr": "Interface Modified" - }, - ] - register: modified_interfaces - -- name: Assertions check for update list of interfaces with individual object status + <<: *nm_interfaces_present + register: idempotency_interfaces_present + +- name: Idempotency assertions check for add list of interfaces with normal mode + assert: + that: + - idempotency_interfaces_present is not changed + - idempotency_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - idempotency_interfaces_present.current.0.fvAEPg.children | length == 3 + - idempotency_interfaces_present.previous.0.fvAEPg.children | length == 3 + +- name: Update list of interfaces - description with check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_update_interfaces_present + <<: *nm_interfaces_present + description: "Description set from module level attributes" + check_mode: yes + register: cm_update_interfaces_present + +- name: Assertions check for update list of interfaces - description with normal mode assert: that: - - modified_interfaces is changed - - modified_interfaces.current.0.fvAEPg.children | length == 3 - - modified_interfaces.previous.0.fvAEPg.children | length == 3 - - modified_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Interface Modified" - - modified_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Interface Modified" - - modified_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Interface Modified" - - modified_interfaces.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" - - modified_interfaces.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" - - modified_interfaces.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" - -- name: Use different statues to perform create, delete and modify the interface objects + - cm_update_interfaces_present is changed + - cm_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - cm_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - cm_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - cm_update_interfaces_present.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + - cm_update_interfaces_present.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + +- name: Update list of interfaces - description with normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_update_interfaces_present + <<: *cm_update_interfaces_present + register: nm_update_interfaces_present + +- name: Assertions check for update list of interfaces - description with normal mode + assert: + that: + - nm_update_interfaces_present is changed + - nm_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - nm_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - nm_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - nm_update_interfaces_present.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" + - nm_update_interfaces_present.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + +- name: Update list of interfaces - description with normal mode - idempotency works cisco.aci.aci_bulk_static_binding_to_epg: + <<: *nm_update_interfaces_present + register: idempotency_nm_update_interfaces_present + +- name: Idempotency assertions check for update list of interfaces - description with normal mode + assert: + that: + - idempotency_nm_update_interfaces_present is not changed + - idempotency_nm_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - idempotency_nm_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - idempotency_nm_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - idempotency_nm_update_interfaces_present.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + - idempotency_nm_update_interfaces_present.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + +- name: Update list of interfaces description using path level attributes with check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_path_update_interfaces_present <<: *epg_present - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": deleted, - "descr": "Interface Deleted" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": modified, - "descr": "Interface Modified" - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "status": created, - "descr": "Interface Created" - } - ] - register: multi_operation_interfaces - -- name: Assertions check for multi status interface objects validation + interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + interface_configs: + - interface: 1/7 + leafs: 101 + pod: 1 + description: "Description set from path level attributes" + - interface: 1/7 + leafs: 107 + pod: 7 + description: "Description set from path level attributes" + - interface: 1/8 + leafs: 108 + pod: 8 + encap_id: 108 + description: "Description set from path level attributes" + state: present + check_mode: yes + register: cm_path_update_interfaces_present + +- name: Assertions check for update list of interfaces description using path level attributes with check mode + assert: + that: + - cm_path_update_interfaces_present is changed + - cm_path_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - cm_path_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - cm_path_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - cm_path_update_interfaces_present.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + - cm_path_update_interfaces_present.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + - cm_path_update_interfaces_present.sent.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + +- name: Update list of interfaces description using path level attributes with normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_path_update_interfaces_present + <<: *cm_path_update_interfaces_present + register: nm_path_update_interfaces_present + +- name: Assertions check for update list of interfaces description using path level attributes with normal mode + assert: + that: + - nm_path_update_interfaces_present is changed + - nm_path_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - nm_path_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - nm_path_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - nm_path_update_interfaces_present.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - nm_path_update_interfaces_present.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from module level attributes" + +- name: Update list of interfaces description using path level attributes with normal mode - idempotency works + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *nm_path_update_interfaces_present + register: idempotency_path_update_interfaces_present + +- name: Idempotency assertions check for update list of interfaces description using path level attributes with normal mode assert: that: - - multi_operation_interfaces is changed - - multi_operation_interfaces.current.0.fvAEPg.children | length == 3 - - multi_operation_interfaces.previous.0.fvAEPg.children | length == 3 + - idempotency_path_update_interfaces_present is not changed + - idempotency_path_update_interfaces_present.current.0.fvAEPg.attributes.name == "anstest" + - idempotency_path_update_interfaces_present.current.0.fvAEPg.children | length == 3 + - idempotency_path_update_interfaces_present.previous.0.fvAEPg.children | length == 3 + - idempotency_path_update_interfaces_present.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from path level attributes" -- name: Query all interfaces with EPG name to check individual interface properties +- name: Query all interfaces of an EPG cisco.aci.aci_bulk_static_binding_to_epg: - <<: *epg_present + <<: *aci_info + tenant: anstest + ap: anstest + epg: anstest state: query - register: all_interfaces_result_1 + register: query_result_of_anstest_epg -- name: Assertions check for query all interfaces with EPG name to check individual interface properties +- name: Assertions check for query all interfaces of an EPG assert: that: - - all_interfaces_result_1 is not changed - - all_interfaces_result_1.current.0.fvAEPg.children | length == 3 + - query_result_of_anstest_epg is not changed + - query_result_of_anstest_epg.current.0.fvAEPg.children | length == 3 + - query_result_of_anstest_epg.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - query_result_of_anstest_epg.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - query_result_of_anstest_epg.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from path level attributes" -- name: Remove list of interfaces with individual object status of the anstest epg +- name: Query all interfaces cisco.aci.aci_bulk_static_binding_to_epg: - <<: *epg_present - interface_configs: [ - { - "encap_id": 221, - "pod": 1, - "leafs": 101, - "interface": '1/7', - "status": deleted, - }, - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "status": deleted, - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "status": deleted, - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "status": deleted, - } - ] - register: deleted_interfaces_with_status - -- name: Assertions check for remove list of interfaces with individual object status of the anstest epg + <<: *aci_info + state: query + register: query_all_interfaces + +- name: Assertions check for query all interfaces # Check covers only EPG level assert: that: - - deleted_interfaces_with_status is changed - - '"children" not in deleted_interfaces_with_status.current.0.fvAEPg' - - deleted_interfaces_with_status.previous.0.fvAEPg.children | length == 3 - - deleted_interfaces_with_status.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "deleted" - - deleted_interfaces_with_status.sent.fvAEPg.children.1.fvRsPathAtt.attributes.status == "deleted" - - deleted_interfaces_with_status.sent.fvAEPg.children.2.fvRsPathAtt.attributes.status == "deleted" - - deleted_interfaces_with_status.sent.fvAEPg.children.3.fvRsPathAtt.attributes.status == "deleted" - -- name: Add list of interfaces with interfaces_status to anstest epg - cisco.aci.aci_bulk_static_binding_to_epg: &cm_created_interfaces - <<: *epg_present - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - } - ] - interfaces_status: created + - query_all_interfaces is not changed + - query_all_interfaces.current | length >= 1 + +- name: Remove list of interfaces with check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_interfaces_absent + <<: *cm_interfaces_present + state: absent check_mode: yes - register: cm_created_interfaces + register: cm_interfaces_absent -- name: Assertions check for add list of interfaces with interfaces_status - check mode +- name: Assertions check for remove list of interfaces with check mode assert: that: - - cm_created_interfaces is changed - - '"children" not in cm_created_interfaces.current.0.fvAEPg' - - '"children" not in cm_created_interfaces.previous.0.fvAEPg' - - cm_created_interfaces.sent.fvAEPg.children | length == 3 + - cm_interfaces_absent is changed + - cm_interfaces_absent.current.0.fvAEPg.children | length == 3 + - cm_interfaces_absent.current.0.fvAEPg.attributes.name == "anstest" + - cm_interfaces_absent.previous.0.fvAEPg.children | length == 3 + - cm_interfaces_absent.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - cm_interfaces_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + +- name: Remove list of interfaces with normal mode + cisco.aci.aci_bulk_static_binding_to_epg: &nm_interfaces_absent + <<: *cm_interfaces_absent + register: nm_interfaces_absent + +- name: Assertions check for remove list of interfaces with normal mode + assert: + that: + - nm_interfaces_absent is changed + - "'children' not in nm_interfaces_absent.current.0.fvAEPg" + - nm_interfaces_absent.current.0.fvAEPg.attributes.name == "anstest" + - nm_interfaces_absent.previous.0.fvAEPg.children | length == 3 + - nm_interfaces_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - nm_interfaces_absent.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + - nm_interfaces_absent.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Description set from path level attributes" + +- name: Remove list of interfaces with normal mode - idempotency works + cisco.aci.aci_bulk_static_binding_to_epg: &idempotency_interfaces_absent + <<: *nm_interfaces_absent + register: idempotency_interfaces_absent + +- name: Idempotency assertions check for remove list of interfaces with normal mode + assert: + that: + - idempotency_interfaces_absent is changed + - "'children' not in idempotency_interfaces_absent.current.0.fvAEPg" + - "'children' not in idempotency_interfaces_absent.previous.0.fvAEPg" + - idempotency_interfaces_absent.current.0.fvAEPg.attributes.name == "anstest" -- name: Add list of interfaces with interfaces_status to anstest epg - normal mode - cisco.aci.aci_bulk_static_binding_to_epg: &nm_created_interfaces - <<: *cm_created_interfaces - register: nm_created_interfaces +- name: Add fex_port_channel interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: &fex_port_channel_present + <<: *epg_present + interface_mode: trunk + interface_type: fex_port_channel + deploy_immediacy: lazy + descr: "fex_port_channel - interface created" + encap_id: 222 + interface_configs: + - extpaths: + - 1012 + interface: 2/7 + leafs: 102 + pod: 2 + register: fex_port_channel_present -- name: Assertions check for add list of interfaces with interfaces_status - normal mode +- name: Assertions check for add fex_port_channel interfaces to anstest epg assert: that: - - nm_created_interfaces is changed - - nm_created_interfaces.current.0.fvAEPg.children | length == 3 - - '"children" not in nm_created_interfaces.previous.0.fvAEPg' - - nm_created_interfaces.sent.fvAEPg.children | length == 3 + - fex_port_channel_present is changed + - fex_port_channel_present.current.0.fvAEPg.children | length == 1 + - '"children" not in fex_port_channel_present.previous.0.fvAEPg' + - fex_port_channel_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-222" + - fex_port_channel_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-2/paths-102/extpaths-1012/pathep-[2/7]" + - fex_port_channel_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_port_channel - interface created" -- name: Add list of interfaces with interfaces_status to anstest epg - normal mode idempotency works +- name: Remove fex_port_channel interfaces from anstest epg cisco.aci.aci_bulk_static_binding_to_epg: - <<: *nm_created_interfaces - register: idempotency_created_interfaces - ignore_errors: yes + <<: *fex_port_channel_present + state: absent + register: fex_port_channel_absent -- name: Idempotency assertions check for add list of interfaces with interfaces_status - normal mode +- name: Assertions check for remove fex_port_channel interfaces from anstest epg assert: that: - - idempotency_created_interfaces is not changed - - idempotency_created_interfaces.response == "HTTP Error 400: Bad Request" - - '"children" in idempotency_created_interfaces.previous.0.fvAEPg' - - idempotency_created_interfaces.previous.0.fvAEPg.children | length == 3 - - idempotency_created_interfaces.sent.fvAEPg.children | length == 3 - -- name: Update list of interfaces with interfaces_status - cisco.aci.aci_bulk_static_binding_to_epg: &updated_interfaces - <<: *epg_present - interface_configs: [ - { - "vlan_id": 107, - "pod": 7, - "leafs": 107, - "interface": '1/7', - "descr": "Interface Modified" - }, - { - "vlan_id": 108, - "pod": 8, - "leafs": 108, - "interface": '1/8', - "descr": "Interface Modified" - }, - { - "vlan_id": 109, - "pod": 9, - "leafs": 109, - "interface": '1/9', - "descr": "Interface Modified" - } - ] - interfaces_status: modified - register: updated_interfaces - -- name: Assertions check for update list of interfaces with interfaces_status - assert: - that: - - updated_interfaces is changed - - updated_interfaces.current.0.fvAEPg.children | length == 3 - - updated_interfaces.previous.0.fvAEPg.children | length == 3 - - updated_interfaces.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Interface Modified" - - updated_interfaces.current.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "Interface Modified" - - updated_interfaces.current.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "Interface Modified" - - updated_interfaces.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "" - - updated_interfaces.previous.0.fvAEPg.children.1.fvRsPathAtt.attributes.descr == "" - - updated_interfaces.previous.0.fvAEPg.children.2.fvRsPathAtt.attributes.descr == "" - -- name: Query all interfaces with EPG name - which is added by interfaces_status - cisco.aci.aci_bulk_static_binding_to_epg: + - fex_port_channel_absent is changed + - fex_port_channel_absent.previous.0.fvAEPg.children | length == 1 + - "'children' not in fex_port_channel_absent.current.0.fvAEPg" + - fex_port_channel_absent.previous.0.fvAEPg.attributes.name == "anstest" + - fex_port_channel_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-222" + - fex_port_channel_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-2/paths-102/extpaths-1012/pathep-[2/7]" + +- name: Add fex_vpc interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: &fex_vpc_present <<: *epg_present - state: query - register: all_interfaces_result_2 + interface_mode: trunk + interface_type: fex_vpc + deploy_immediacy: lazy + descr: fex_vpc - interface created + encap_id: 223 + interface_configs: + - extpaths: + - 103 + - 104 + interface: 3/7 + leafs: + - 103 + - 104 + pod: 3 + register: fex_vpc_present -- name: Assertions check for query all interfaces with EPG name - which is added by interfaces_status +- name: Assertions check for add fex_vpc interfaces to anstest epg assert: that: - - all_interfaces_result_2 is not changed - - all_interfaces_result_2.current.0.fvAEPg.children | length == 3 - - all_interfaces_result_2.filter_string == "?rsp-subtree=full&rsp-subtree-class=fvRsPathAtt" - -- name: Remove list of interfaces with interfaces_status from anstest epg - check mode - cisco.aci.aci_bulk_static_binding_to_epg: &cm_deleted_list_of_interfaces - <<: *updated_interfaces - interfaces_status: deleted - check_mode: yes - register: cm_deleted_list_of_interfaces + - fex_vpc_present is changed + - fex_vpc_present.current.0.fvAEPg.children | length == 1 + - "'children' not in fex_vpc_present.previous.0.fvAEPg" + - fex_vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_vpc - interface created" + - fex_vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-223" + - fex_vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-3/protpaths-103-104/extprotpaths-103-104/pathep-[3/7]" -- name: Assertions check for remove list of interfaces with interfaces_status from anstest epg - check mode +- name: Remove fex_vpc interfaces from anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_present + state: absent + register: fex_vpc_absent + +- name: Assertions check for remove fex_vpc interfaces from anstest epg assert: that: - - cm_deleted_list_of_interfaces is changed - - cm_deleted_list_of_interfaces.current.0.fvAEPg.children | length == 3 - - cm_deleted_list_of_interfaces.previous.0.fvAEPg.children | length == 3 - - cm_deleted_list_of_interfaces.sent.fvAEPg.children | length == 3 + - fex_vpc_absent is changed + - fex_vpc_absent.previous.0.fvAEPg.children | length == 1 + - "'children' not in fex_vpc_absent.current.0.fvAEPg" + - fex_vpc_absent.previous.0.fvAEPg.attributes.name == "anstest" + - fex_vpc_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-223" + - fex_vpc_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-3/protpaths-103-104/extprotpaths-103-104/pathep-[3/7]" -- name: Remove list of interfaces with interfaces_status from anstest epg - normal mode - cisco.aci.aci_bulk_static_binding_to_epg: &nm_deleted_list_of_interfaces - <<: *cm_deleted_list_of_interfaces - register: nm_deleted_list_of_interfaces +- name: Add vpc interfaces to anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: &vpc_present + <<: *epg_present + deploy_immediacy: lazy + descr: vpc - interface created + interface_mode: trunk + interface_type: vpc + encap_id: 224 + interface_configs: + - interface: 4/7 + leafs: + - 105 + - 106 + pod: 4 + extpaths: + - 1015 + register: vpc_present -- name: Assertions check for remove list of interfaces with interfaces_status from anstest epg - normal mode +- name: Assertions check for add vpc interfaces to anstest epg assert: that: - - nm_deleted_list_of_interfaces is changed - - '"children" not in nm_deleted_list_of_interfaces.current.0.fvAEPg' - - nm_deleted_list_of_interfaces.previous.0.fvAEPg.children | length == 3 - - nm_deleted_list_of_interfaces.sent.fvAEPg.children | length == 3 + - vpc_present is changed + - vpc_present.current.0.fvAEPg.children | length == 1 + - "'children' not in vpc_present.previous.0.fvAEPg" + - vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "vpc - interface created" + - vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-224" + - vpc_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-4/protpaths-105-106/pathep-[4/7]" -- name: Remove list of interfaces with interfaces_status from anstest epg - normal mode idempotency works +- name: Remove vpc interfaces from anstest epg cisco.aci.aci_bulk_static_binding_to_epg: - <<: *nm_deleted_list_of_interfaces - register: idempotency_deleted_list_of_interfaces + <<: *vpc_present + state: absent + register: vpc_absent -- name: Idempotency assertions check for remove list of interfaces with interfaces_status from anstest epg - normal mode +- name: Assertions check for remove vpc interfaces from anstest epg assert: that: - - idempotency_deleted_list_of_interfaces is changed - - '"children" not in idempotency_deleted_list_of_interfaces.current.0.fvAEPg' - - '"children" not in idempotency_deleted_list_of_interfaces.previous.0.fvAEPg' - -- name: Add fex_port_channel interfaces to anstest epg + - vpc_absent is changed + - vpc_absent.previous.0.fvAEPg.children | length == 1 + - "'children' not in vpc_absent.current.0.fvAEPg" + - vpc_absent.previous.0.fvAEPg.attributes.name == "anstest" + - vpc_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-224" + - vpc_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-4/protpaths-105-106/pathep-[4/7]" + +- name: Query all interfaces before start module and path level check cisco.aci.aci_bulk_static_binding_to_epg: <<: *aci_info tenant: anstest ap: anstest epg: anstest - interface_configs: [ - { - "encap_id": 222, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": fex_port_channel, - "pod": 2, - "leafs": 102, - "interface": '2/7', - "extpaths": [ - 1012 - ], - "descr": "fex_port_channel - interface created" - } - ] - register: fex_port_channel_present + state: query + register: query_result -- name: Assertions check for add fex_port_channel interfaces to anstest epg +- name: Assertions check for query all interfaces before start module and path level check assert: that: - - fex_port_channel_present is changed - - fex_port_channel_present.current.0.fvAEPg.children | length == 1 - - '"children" not in fex_port_channel_present.previous.0.fvAEPg' - - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-222" - - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" - - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-2/paths-102/extpaths-1012/pathep-[2/7]" - - fex_port_channel_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_port_channel - interface created" + - query_result is not changed + - "'children' not in query_result.current.0.fvAEPg" + - query_result.current.0.fvAEPg.attributes.name == "anstest" -- name: Add fex_vpc interfaces to anstest epg +- name: Add an interface with module level attributes + cisco.aci.aci_bulk_static_binding_to_epg: &module_level_check_present + <<: *epg_present + interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 108 + description: "Module level test" + interface_configs: + - interface: 1/8 + leafs: 108 + pod: 8 + state: present + register: module_level_check + +- name: Assertions check for add an interface with module level attributes + assert: + that: + - module_level_check is changed + - module_level_check.current.0.fvAEPg.children | length == 1 + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-108" + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-8/paths-108/pathep-[eth1/8]" + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Module level test" + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.instrImedcy == "lazy" + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.mode == "regular" + +- name: Remove an interface with module level attributes cisco.aci.aci_bulk_static_binding_to_epg: - <<: *aci_info - tenant: anstest - ap: anstest - epg: anstest - interface_configs: [ - { - "encap_id": 223, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": fex_vpc, - "pod": 3, - "leafs": [ - 103, - 104 - ], - "interface": '3/7', - "extpaths": [ - 103, - 104 - ], - "descr": "fex_vpc - interface created" - } - ] - register: fex_vpc_present + <<: *module_level_check_present + state: absent + register: module_level_check_absent -- name: Assertions check for add fex_vpc interfaces to anstest epg +- name: Assertions check for remove an interface with module level attributes assert: that: - - fex_vpc_present is changed - - fex_vpc_present.current.0.fvAEPg.children | length == 2 - - fex_vpc_present.previous.0.fvAEPg.children | length == 1 - - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-223" - - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" - - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-3/protpaths-103-104/extprotpaths-103-104/pathep-[3/7]" - - fex_vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "fex_vpc - interface created" + - module_level_check_absent is changed + - "'children' not in module_level_check_absent.current.0.fvAEPg" + - module_level_check_absent.current.0.fvAEPg.attributes.name == "anstest" + - module_level_check_absent.previous.0.fvAEPg.children | length == 1 + - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-108" + - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Module level test" + - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-8/paths-108/pathep-[eth1/8]" + +- name: Add an interface with path level attributes + cisco.aci.aci_bulk_static_binding_to_epg: &path_level_check_present + <<: *epg_present + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 109 + description: "Path level test" + interface: 1/9 + leafs: 109 + pod: 9 + state: present + register: path_level_check -- name: Add vpc interfaces to anstest epg +- name: Assertions check for add an interface with path level attributes + assert: + that: + - path_level_check is changed + - path_level_check.current.0.fvAEPg.children | length == 1 + - path_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-109" + - path_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-9/paths-109/pathep-[eth1/9]" + +- name: Remove an interface with path level attributes cisco.aci.aci_bulk_static_binding_to_epg: + <<: *path_level_check_present + state: absent + register: path_level_check_absent + +- name: Assertions check for remove an interface with path level attributes + assert: + that: + - path_level_check_absent is changed + - "'children' not in path_level_check_absent.current.0.fvAEPg" + - path_level_check_absent.current.0.fvAEPg.attributes.name == "anstest" + - path_level_check_absent.previous.0.fvAEPg.children | length == 1 + - path_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-109" + - path_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Path level test" + - path_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-9/paths-109/pathep-[eth1/9]" + +- name: Add an interface encap_id with path and module level attributes + cisco.aci.aci_bulk_static_binding_to_epg: &path_and_module_encap_id_present <<: *epg_present - interface_configs: [ - { - "encap_id": 224, - "deploy_immediacy": lazy, - "interface_mode": trunk, - "interface_type": vpc, - "pod": 4, - "leafs": [ - 105, - 106 - ], - "interface": '4/7', - "extpaths": [ - 1015 - ], - "descr": "vpc - interface created" - } - ] - register: vpc_present + encap_id: 108 + interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + description: "Path and Module level test" + interface_configs: + - interface: 1/7 + leafs: 107 + pod: 7 + encap_id: 107 + state: present + register: path_and_module_encap_id_present -- name: Assertions check for add vpc interfaces to anstest epg +- name: Assertions check for add an interface encap_id with path and module level attributes assert: that: - - vpc_present is changed - - vpc_present.current.0.fvAEPg.children | length == 3 - - vpc_present.previous.0.fvAEPg.children | length == 2 - - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-224" - - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.status == "created" - - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-4/protpaths-105-106/pathep-[4/7]" - - vpc_present.sent.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "vpc - interface created" + - path_and_module_encap_id_present is changed + - path_and_module_encap_id_present.current.0.fvAEPg.children | length == 1 + - path_and_module_encap_id_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-107" + - path_and_module_encap_id_present.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-7/paths-107/pathep-[eth1/7]" + +- name: Remove an interface encap_id with path and module level attributes + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *path_and_module_encap_id_present + state: absent + register: path_and_module_encap_id_absent + +- name: Assertions check for remove an interface encap_id with path and module level attributes + assert: + that: + - path_and_module_encap_id_absent is changed + - "'children' not in path_and_module_encap_id_absent.current.0.fvAEPg" + - path_and_module_encap_id_absent.current.0.fvAEPg.attributes.name == "anstest" + - path_and_module_encap_id_absent.previous.0.fvAEPg.children | length == 1 + - path_and_module_encap_id_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-107" + - path_and_module_encap_id_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Path and Module level test" + - path_and_module_encap_id_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-7/paths-107/pathep-[eth1/7]" # Cleanup part - name: Remove anstest epg From b1f4ae6a10b096c5764e11bbb4dada9d3a66d75e Mon Sep 17 00:00:00 2001 From: Sabari Jaganathan Date: Fri, 11 Feb 2022 15:03:43 +0530 Subject: [PATCH 3/4] Moved aci.construct_url part above to the state value check condition and fixed documentation sentence. --- .../modules/aci_bulk_static_binding_to_epg.py | 53 +++++++++---------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/plugins/modules/aci_bulk_static_binding_to_epg.py b/plugins/modules/aci_bulk_static_binding_to_epg.py index bb7a7ce0b..833e4f842 100644 --- a/plugins/modules/aci_bulk_static_binding_to_epg.py +++ b/plugins/modules/aci_bulk_static_binding_to_epg.py @@ -49,7 +49,7 @@ description: - Determines the primary encapsulation ID associating the C(epg) with the interface path when using micro-segmentation. - - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown). - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation. type: str aliases: [ primary_vlan, primary_vlan_id ] @@ -98,7 +98,7 @@ description: - Determines the primary encapsulation ID associating the C(epg) with the interface path when using micro-segmentation. - - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown. + - Accepted values are any valid encap ID for specified encap, currently ranges between C(1) and C(4096) and C(unknown). - C(unknown) is the default value and using C(unknown) disables the Micro-Segmentation. type: str aliases: [ primary_vlan, primary_vlan_id ] @@ -474,6 +474,30 @@ def main(): children = [] interface_status_mapping = {"absent": "deleted"} + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter=dict(name=tenant), + ), + subclass_1=dict( + aci_class="fvAp", + aci_rn="ap-{0}".format(ap), + module_object=ap, + target_filter=dict(name=ap), + ), + subclass_2=dict( + aci_class="fvAEPg", + aci_rn="epg-{0}".format(epg), + module_object=epg, + target_filter=dict(name=epg), + ), + child_classes=["fvRsPathAtt"], + ) + + aci.get_existing() + if state == "present" or state == "absent": for interface_config in interface_configs: pod_id = interface_config.get("pod_id") @@ -565,31 +589,6 @@ def main(): ) ) - aci.construct_url( - root_class=dict( - aci_class="fvTenant", - aci_rn="tn-{0}".format(tenant), - module_object=tenant, - target_filter=dict(name=tenant), - ), - subclass_1=dict( - aci_class="fvAp", - aci_rn="ap-{0}".format(ap), - module_object=ap, - target_filter=dict(name=ap), - ), - subclass_2=dict( - aci_class="fvAEPg", - aci_rn="epg-{0}".format(epg), - module_object=epg, - target_filter=dict(name=epg), - ), - child_classes=["fvRsPathAtt"], - ) - - aci.get_existing() - - if state == "present" or state == "absent": aci.payload( aci_class="fvAEPg", class_config=dict(), From e672a6a7d60b65f62a81e324022d498cc46d44db Mon Sep 17 00:00:00 2001 From: Lionel Hercot Date: Tue, 31 May 2022 21:42:59 -0700 Subject: [PATCH 4/4] [ignore] Fix aci_bulk_static_binding_to_epg tests for better coverage --- .../modules/aci_bulk_static_binding_to_epg.py | 30 +-- .../tasks/main.yml | 210 +++++++++++++++++- 2 files changed, 218 insertions(+), 22 deletions(-) diff --git a/plugins/modules/aci_bulk_static_binding_to_epg.py b/plugins/modules/aci_bulk_static_binding_to_epg.py index 833e4f842..2f8e8af40 100644 --- a/plugins/modules/aci_bulk_static_binding_to_epg.py +++ b/plugins/modules/aci_bulk_static_binding_to_epg.py @@ -128,6 +128,7 @@ - The pod number part of the tDn. - C(pod_id) is usually an integer below C(10). type: int + required: yes aliases: [ pod, pod_number ] leafs: description: @@ -137,12 +138,14 @@ - The C(leafs) value is usually something like '101' or '101-102' depending on C(connection_type). type: list elements: str + required: yes aliases: [ leaves, nodes, paths, switches ] interface: description: - The C(interface) string value part of the tDn. - Usually a policy group like C(test-IntPolGrp) or an interface of the following format C(1/7) depending on C(interface_type). type: str + required: yes extpaths: description: - The C(extpaths) integer value part of the tDn. @@ -401,16 +404,16 @@ "untagged": "untagged", } -INTERFACE_TYPE_MAPPING = dict( - fex="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]", - fex_port_channel="topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[{interface}]", - fex_vpc="topology/pod-{pod_id}/protpaths-{leafs}/extprotpaths-{extpaths}/pathep-[{interface}]", - port_channel="topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]", - switch_port="topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]", - vpc="topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]", -) +INTERFACE_TYPE_MAPPING = { + "fex": "topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[eth{interface}]", + "fex_port_channel": "topology/pod-{pod_id}/paths-{leafs}/extpaths-{extpaths}/pathep-[{interface}]", + "fex_vpc": "topology/pod-{pod_id}/protpaths-{leafs}/extprotpaths-{extpaths}/pathep-[{interface}]", + "port_channel": "topology/pod-{pod_id}/paths-{leafs}/pathep-[{interface}]", + "switch_port": "topology/pod-{pod_id}/paths-{leafs}/pathep-[eth{interface}]", + "vpc": "topology/pod-{pod_id}/protpaths-{leafs}/pathep-[{interface}]", +} -# TODO: change 'deploy_immediacy' to 'resolution_immediacy' (as seen in aci_epg_to_domain)? +INTERFACE_STATUS_MAPPING = {"absent": "deleted"} def main(): @@ -440,9 +443,9 @@ def main(): type="str", choices=["802.1p", "access", "native", "regular", "tagged", "trunk", "untagged"], aliases=["interface_mode_name", "mode"] ), interface_type=dict(type="str", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), - pod_id=dict(type="int", aliases=["pod", "pod_number"]), - leafs=dict(type="list", elements="str", aliases=["leaves", "nodes", "paths", "switches"]), - interface=dict(type="str"), + pod_id=dict(type="int", required=True, aliases=["pod", "pod_number"]), + leafs=dict(type="list", elements="str", required=True, aliases=["leaves", "nodes", "paths", "switches"]), + interface=dict(type="str", required=True), extpaths=dict(type="list", elements="str"), ), ), @@ -472,7 +475,6 @@ def main(): aci = ACIModule(module) children = [] - interface_status_mapping = {"absent": "deleted"} aci.construct_url( root_class=dict( @@ -571,7 +573,7 @@ def main(): interface_mode = INTERFACE_MODE_MAPPING.get(interface_mode) - interface_status = interface_status_mapping.get(state) + interface_status = INTERFACE_STATUS_MAPPING.get(state) children.append( dict( diff --git a/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml index b51337d6b..7bd6ffffc 100644 --- a/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml +++ b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml @@ -58,6 +58,7 @@ leafs: 108 pod: 8 encap_id: 108 + primary_encap_id: 1008 state: present check_mode: yes register: cm_interfaces_present @@ -163,6 +164,7 @@ leafs: 108 pod: 8 encap_id: 108 + primary_encap_id: 1008 description: "Description set from path level attributes" state: present check_mode: yes @@ -438,6 +440,7 @@ interface_mode: trunk deploy_immediacy: lazy encap_id: 108 + primary_encap_id: unknown description: "Module level test" interface_configs: - interface: 1/8 @@ -452,6 +455,7 @@ - module_level_check is changed - module_level_check.current.0.fvAEPg.children | length == 1 - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-108" + - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.primaryEncap == "unknown" - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-8/paths-108/pathep-[eth1/8]" - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Module level test" - module_level_check.current.0.fvAEPg.children.0.fvRsPathAtt.attributes.instrImedcy == "lazy" @@ -471,6 +475,7 @@ - module_level_check_absent.current.0.fvAEPg.attributes.name == "anstest" - module_level_check_absent.previous.0.fvAEPg.children | length == 1 - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.encap == "vlan-108" + - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.primaryEncap == "unknown" - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Module level test" - module_level_check_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-8/paths-108/pathep-[eth1/8]" @@ -555,17 +560,206 @@ - path_and_module_encap_id_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.descr == "Path and Module level test" - path_and_module_encap_id_absent.previous.0.fvAEPg.children.0.fvRsPathAtt.attributes.tDn == "topology/pod-7/paths-107/pathep-[eth1/7]" -# Cleanup part -- name: Remove anstest epg - cisco.aci.aci_epg: +- name: Bind static-binding to epg - interface type switch_port - no leafs (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: <<: *epg_present - state: absent + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + state: present + ignore_errors: yes + register: switch_port_no_leafs -- name: Remove anstest ap - cisco.aci.aci_ap: - <<: *ap_present - state: absent +- name: Bind static-binding to epg - interface type fex_vpc - no extpaths (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: &fex_vpc_no_extpaths + <<: *epg_present + interface_configs: + - interface_type: fex_vpc + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + leafs: 101 + pod: 1 + state: present + ignore_errors: yes + register: fex_vpc_no_extpaths + +- name: Bind static-binding to epg - interface type fex_vpc - incorrect extpaths (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: fex_vpc + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + leafs: 101 + pod: 1 + extpaths: + - 1012 + ignore_errors: yes + register: fex_vpc_one_node + +- name: Bind static-binding to epg - fex_vpc with one extpaths (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: fex_vpc + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + - 102 + extpaths: + - 103 + ignore_errors: yes + register: fex_vpc_one_extpaths + +- name: Bind static-binding to epg - switch_port with two leafs (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + - 102 + ignore_errors: yes + register: switch_port_two_leafs + +- name: Bind static-binding to epg - switch_port with two extpaths (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + extpaths: + - 102 + - 103 + ignore_errors: yes + register: switch_port_two_extpaths + +- name: Bind static-binding to epg - fex_vpc with 3 nodes (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: fex_vpc + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + - 102 + - 103 + extpaths: + - 101 + - 102 + ignore_errors: yes + register: fex_vpc_three_leafs + +- name: Bind static-binding to epg - fex_vpc with 3 expaths (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: fex_vpc + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + - 102 + extpaths: + - 101 + - 102 + - 103 + ignore_errors: yes + register: fex_vpc_three_expaths + +- name: Bind static-binding to epg - switch_port with encap_id 5000 (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 5000 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + ignore_errors: yes + register: switch_port_encap_id_too_high + +- name: Bind static-binding to epg - switch_port with primary_encap_id 5000 (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + primary_encap_id: 5000 + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + ignore_errors: yes + register: switch_port_primary_encap_id_too_high + +- name: Bind static-binding to epg - switch_port with primary_encap_id 5000 (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_vpc_no_extpaths + interface_configs: + - interface_type: switch_port + interface_mode: trunk + deploy_immediacy: lazy + encap_id: 107 + primary_encap_id: not_unknown + interface: 'ansible_test' + pod: 1 + leafs: + - 101 + ignore_errors: yes + register: switch_port_primary_encap_id_not_unknown + +- name: Negative assertions to check error messages + assert: + that: + - switch_port_no_leafs.msg == "missing required arguments{{':'}} leafs found in interface_configs" + - fex_vpc_no_extpaths.msg == "extpaths is required when interface_type is{{':'}} fex_vpc" + - fex_vpc_one_node.msg == "A interface_type of \"vpc\" requires 2 leafs" + - fex_vpc_one_extpaths.msg == "A interface_type of \"fex_vpc\" requires 2 extpaths" + - switch_port_two_leafs.msg == "The interface_types \"switch_port\", \"port_channel\", and \"fex\" do not support using multiple leafs for a single binding" + - switch_port_two_extpaths.msg == "The interface_types \"fex\" and \"fex_port_channel\" do not support using multiple extpaths for a single binding" + - fex_vpc_three_leafs.msg == "The \"leafs\" parameter must not have more than 2 entries" + - fex_vpc_three_expaths.msg == "The \"extpaths\" parameter must not have more than 2 entries" + - switch_port_encap_id_too_high.msg == "Valid VLAN assignments are from 1 to 4096" + - switch_port_primary_encap_id_too_high.msg == "Valid VLAN assignments are from 1 to 4096 or unknown." + - switch_port_primary_encap_id_not_unknown.msg.startswith("Valid VLAN assignments are from 1 to 4096 or unknown. ") +# Cleanup part - name: Remove anstest tenant cisco.aci.aci_tenant: <<: *tenant_present