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..2f8e8af40 --- /dev/null +++ b/plugins/modules/aci_bulk_static_binding_to_epg.py @@ -0,0 +1,608 @@ +#!/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 ] + 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 of a dictionary. + - Module level attributes will be overridden by the path level attributes. + type: list + elements: dict + suboptions: + 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 ] + pod_id: + description: + - 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: + - 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 + 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. + - 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 + 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: Create list of interfaces using module level attributes + 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 + 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: Create/Update list of interfaces using path level attributes + 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: + - 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: Query all interfaces of 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 + state: query + delegate_to: localhost + +- name: Query all interfaces + cisco.aci.aci_bulk_static_binding_to_epg: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + +- name: Remove list of 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 + 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 +""" + +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 = { + "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_STATUS_MAPPING = {"absent": "deleted"} + + +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"]), + 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( + 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", choices=["fex", "port_channel", "switch_port", "vpc", "fex_port_channel", "fex_vpc"]), + 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"), + ), + ), + 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") + 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") + state = module.params.get("state") + + aci = ACIModule(module) + children = [] + + 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") + 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)) + + 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) + + interface_mode = INTERFACE_MODE_MAPPING.get(interface_mode) + + interface_status = INTERFACE_STATUS_MAPPING.get(state) + + 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.payload( + aci_class="fvAEPg", + class_config=dict(), + child_configs=children, + ) + + aci.get_diff(aci_class="fvAEPg") + + aci.post_config() + + 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..7bd6ffffc --- /dev/null +++ b/tests/integration/targets/aci_bulk_static_binding_to_epg/tasks/main.yml @@ -0,0 +1,766 @@ +# 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 check mode + cisco.aci.aci_bulk_static_binding_to_epg: &cm_interfaces_present + <<: *epg_present + 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 + primary_encap_id: 1008 + 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: + - 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: + <<: *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: + - 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_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 + primary_encap_id: 1008 + 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: + - 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 of an EPG + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *aci_info + tenant: anstest + ap: anstest + epg: anstest + state: query + register: query_result_of_anstest_epg + +- name: Assertions check for query all interfaces of an EPG + assert: + that: + - 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: Query all interfaces + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *aci_info + state: query + register: query_all_interfaces + +- name: Assertions check for query all interfaces # Check covers only EPG level + assert: + that: + - 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_interfaces_absent + +- name: Assertions check for remove list of interfaces with check mode + assert: + that: + - 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 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 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.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: Remove fex_port_channel interfaces from anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *fex_port_channel_present + state: absent + register: fex_port_channel_absent + +- name: Assertions check for remove fex_port_channel interfaces from anstest epg + assert: + that: + - 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 + 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 add fex_vpc interfaces to anstest epg + assert: + that: + - 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: 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: + - 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: 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 add vpc interfaces to anstest epg + assert: + that: + - 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 vpc interfaces from anstest epg + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *vpc_present + state: absent + register: vpc_absent + +- name: Assertions check for remove vpc interfaces from anstest epg + assert: + that: + - 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 + state: query + register: query_result + +- name: Assertions check for query all interfaces before start module and path level check + assert: + that: + - query_result is not changed + - "'children' not in query_result.current.0.fvAEPg" + - query_result.current.0.fvAEPg.attributes.name == "anstest" + +- 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 + primary_encap_id: unknown + 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.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" + - 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: + <<: *module_level_check_present + state: absent + register: module_level_check_absent + +- name: Assertions check for remove an interface with module level attributes + assert: + that: + - 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.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]" + +- 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: 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 + 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 an interface encap_id with path and module level attributes + assert: + that: + - 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]" + +- name: Bind static-binding to epg - interface type switch_port - no leafs (normal mode) + cisco.aci.aci_bulk_static_binding_to_epg: + <<: *epg_present + 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: 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 + state: absent