diff --git a/.gitignore b/.gitignore index 2ee73b459..28885b49c 100644 --- a/.gitignore +++ b/.gitignore @@ -390,4 +390,7 @@ $RECYCLE.BIN/ .vscode # Ansible Collection tarball -cisco-aci-*.tar.gz \ No newline at end of file +cisco-aci-*.tar.gz + +# Virtualenv +.venv/ \ No newline at end of file diff --git a/plugins/modules/aci_contract_subject.py b/plugins/modules/aci_contract_subject.py index 0b63bb66c..45114922a 100644 --- a/plugins/modules/aci_contract_subject.py +++ b/plugins/modules/aci_contract_subject.py @@ -79,6 +79,15 @@ description: - The alias for the current object. This relates to the nameAlias field in ACI. type: str + subject_label: + description: + - The label applid to this contract subject + type: str + subject_label_direction: + description: + - Use C(consumer) to add a consumed label and C(provider) to add a provided label to this contract + type: str + choices: [ consumer, provider ] extends_documentation_fragment: - cisco.aci.aci - cisco.aci.annotation @@ -94,6 +103,7 @@ link: https://developer.cisco.com/docs/apic-mim-ref/ author: - Swetha Chunduri (@schunduri) +- Mark Ciecior (@markciecior) """ EXAMPLES = r""" @@ -112,6 +122,23 @@ state: present register: query_result +- name: Add a label to a contract subject + cisco.aci.aci_contract_subject: + host: apic + username: admin + password: SomeSecretPassword + tenant: production + contract: web_to_db + subject: default + description: test + reverse_filter: yes + priority: level1 + dscp: unspecified + subject_label: MyLabel + subject_label_direction: consumer + state: present + register: query_result + - name: Remove a contract subject cisco.aci.aci_contract_subject: host: apic @@ -260,6 +287,10 @@ none="None", ) +SUBJ_LABEL_MAPPING = dict( + consumer="vzConsSubjLbl", + provider="vzProvSubjLbl", +) def main(): argument_spec = aci_argument_spec() @@ -304,6 +335,8 @@ def main(): provider_match=dict(type="str", choices=["all", "at_least_one", "at_most_one", "none"]), state=dict(type="str", default="present", choices=["absent", "present", "query"]), name_alias=dict(type="str"), + subject_label=dict(type="str"), + subject_label_direction=dict(type="str", choices=["consumer", "provider"]), ) module = AnsibleModule( @@ -332,6 +365,12 @@ def main(): state = module.params.get("state") tenant = module.params.get("tenant") name_alias = module.params.get("name_alias") + subject_label = module.params.get("subject_label") + subject_label_direction = module.params.get("subject_label_direction") + + subject_label_class = SUBJ_LABEL_MAPPING[subject_label_direction] + + child_classes = [subject_label_class] aci.construct_url( root_class=dict( @@ -352,11 +391,17 @@ def main(): module_object=subject, target_filter={"name": subject}, ), + child_classes=child_classes, ) aci.get_existing() if state == "present": + child_configs = [] + if subject_label: + child_configs.append( + {subject_label_class: {"attributes": {"name": subject_label}}} + ) aci.payload( aci_class="vzSubj", class_config=dict( @@ -369,6 +414,7 @@ def main(): descr=description, nameAlias=name_alias, ), + child_configs=child_configs ) aci.get_diff(aci_class="vzSubj") diff --git a/plugins/modules/aci_epg.py b/plugins/modules/aci_epg.py index 183a4f064..9d6d1460a 100644 --- a/plugins/modules/aci_epg.py +++ b/plugins/modules/aci_epg.py @@ -323,6 +323,10 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec +SUBJ_LABEL_MAPPING = dict( + consumer="vzConsSubjLbl", + provider="vzProvSubjLbl", +) def main(): argument_spec = aci_argument_spec() @@ -341,6 +345,8 @@ def main(): name_alias=dict(type="str"), monitoring_policy=dict(type="str"), custom_qos_policy=dict(type="str"), + subject_label=dict(type="str"), + subject_label_direction=dict(type="str", choices=["consumer", "provider"]), useg=dict(type="str", choices=['yes', 'no']), ) @@ -369,9 +375,19 @@ def main(): monitoring_policy = module.params.get("monitoring_policy") custom_qos_policy = module.params.get("custom_qos_policy") useg = module.params.get("useg") + subject_label = module.params.get("subject_label") + subject_label_direction = module.params.get("subject_label_direction") + + subject_label_class = SUBJ_LABEL_MAPPING[subject_label_direction] + + child_classes = ["fvRsBd", "fvRsAEPgMonPol", "fvRsCustQosPol"] child_configs = [dict(fvRsBd=dict(attributes=dict(tnFvBDName=bd))), dict(fvRsAEPgMonPol=dict(attributes=dict(tnMonEPGPolName=monitoring_policy)))] + if subject_label_direction is not None: + child_classes.append(subject_label_class) + child_configs.append({subject_label_class: {"attributes": {"name": subject_label}}}) + if custom_qos_policy is not None: child_configs.append(dict(fvRsCustQosPol=dict(attributes=dict(tnQosCustomPolName=custom_qos_policy)))) @@ -394,7 +410,7 @@ def main(): module_object=epg, target_filter={"name": epg}, ), - child_classes=["fvRsBd", "fvRsAEPgMonPol", "fvRsCustQosPol"], + child_classes=child_classes, ) aci.get_existing() diff --git a/plugins/modules/aci_l3out_extepg_to_contract.py b/plugins/modules/aci_l3out_extepg_to_contract.py index 5d516f2ad..7df0d61b3 100644 --- a/plugins/modules/aci_l3out_extepg_to_contract.py +++ b/plugins/modules/aci_l3out_extepg_to_contract.py @@ -52,6 +52,10 @@ - This is configurable for provided contracts. type: str choices: ['all', 'at_least_one', 'at_most_one', 'none'] + subject_label: + description: + - The label to be applied to this contract relationship + type: str state: description: - Use C(present) or C(absent) for adding or removing. @@ -89,6 +93,20 @@ state: present delegate_to: localhost +- name: Add a subject label to an external EPG + cisco.aci.aci_l3out_extepg_to_contract: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l3out: l3out + extepg : testEpg + contract: contract1 + contract_type: provider + subject_label: my_test_label + state: present + delegate_to: localhost + - name: Remove existing contract from an external EPG cisco.aci.aci_l3out_extepg_to_contract: host: apic @@ -253,6 +271,11 @@ none="None", ) +SUBJ_LABEL_MAPPING = dict( + consumer="vzConsSubjLbl", + provider="vzProvSubjLbl", +) + def main(): argument_spec = aci_argument_spec() @@ -266,6 +289,7 @@ def main(): state=dict(type="str", default="present", choices=["absent", "present", "query"]), tenant=dict(type="str"), extepg=dict(type="str", aliases=["extepg_name", "external_epg"]), + subject_label=dict(type="str") ) module = AnsibleModule( argument_spec=argument_spec, @@ -286,13 +310,17 @@ def main(): provider_match = PROVIDER_MATCH_MAPPING.get(provider_match) state = module.params.get("state") tenant = module.params.get("tenant") + subject_label = module.params.get("subject_label") aci_class = ACI_CLASS_MAPPING.get(contract_type)["class"] aci_rn = ACI_CLASS_MAPPING.get(contract_type)["rn"] + subject_label_class = SUBJ_LABEL_MAPPING[contract_type] if contract_type == "consumer" and provider_match is not None: module.fail_json(msg="the 'provider_match' is only configurable for Provided Contracts") + child_classes = [subject_label_class] + aci = ACIModule(module) aci.construct_url( root_class=dict( @@ -319,11 +347,17 @@ def main(): module_object=contract, target_filter={"tnVzBrCPName": contract}, ), + child_classes=child_classes, ) aci.get_existing() if state == "present": + child_configs = [] + if subject_label: + child_configs.append( + {subject_label_class: {"attributes": {"name": subject_label}}} + ) aci.payload( aci_class=aci_class, class_config=dict( @@ -331,6 +365,7 @@ def main(): prio=priority, tnVzBrCPName=contract, ), + child_configs=child_configs, ) aci.get_diff(aci_class=aci_class) diff --git a/plugins/modules/aci_l3out_extepg_to_subject_label.py b/plugins/modules/aci_l3out_extepg_to_subject_label.py new file mode 100644 index 000000000..c1f1ca2c7 --- /dev/null +++ b/plugins/modules/aci_l3out_extepg_to_subject_label.py @@ -0,0 +1,319 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2020, Sudhakar Shet Kudtarkar (@kudtarkar1) +# Copyright: (c) 2020, Shreyas Srish +# Copyright: (c) 2022, Mark Ciecior (@markciecior) +# 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": "community"} + +DOCUMENTATION = r""" +--- +module: aci_l3out_extepg_to_subject_label +short_description: Bind Subject Labels to External End Point Groups (EPGs) +description: +- Bind Subject Labels to External End Point Groups (EPGs) on ACI fabrics. +options: + tenant: + description: + - Name of existing tenant. + type: str + l3out: + description: + - Name of the l3out. + type: str + aliases: ['l3out_name'] + extepg: + description: + - Name of the external end point group. + type: str + aliases: ['extepg_name', 'external_epg'] + subject_label: + description: + - Name of the subject label. + type: str + contract_type: + description: + - The type of contract. + type: str + required: yes + choices: ['consumer', 'provider'] + 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(l3out) and C(extepg) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l3out) and M(cisco.aci.aci_l3out_extepg) modules can be used for this. +seealso: +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(fvtenant), B(l3extInstP) and B(l3extOut). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Sudhakar Shet Kudtarkar (@kudtarkar1) +- Shreyas Srish (@shrsr) +""" + +EXAMPLES = r""" +- name: Bind a subject label to an external EPG + cisco.aci.aci_l3out_extepg_to_subject_label: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l3out: l3out + extepg : testEpg + subject_label: my_test_label + contract_type: provider + state: present + delegate_to: localhost + +- name: Remove existing subject label from an external EPG + cisco.aci.aci_l3out_extepg_to_subject_label: + host: apic + username: admin + password: SomeSecretePassword + tenant: Auto-Demo + l3out: l3out + extepg : testEpg + subject_label: my_test_label + contract_type: provider + state: absent + delegate_to: localhost + +- name: Query a subject label bound to an external EPG + cisco.aci.aci_l3out_extepg_to_subject_label: + host: apic + username: admin + password: SomeSecretePassword + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subject_label: my_test_label + contract_type: provider + state: query + delegate_to: localhost + register: query_result + +- name: Query all contracts relationships + cisco.aci.aci_l3out_extepg_to_subject_label: + host: apic + username: admin + password: SomeSecretePassword + contract_type: provider + state: query + delegate_to: localhost + register: query_result +""" + +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 + +ACI_CLASS_MAPPING = dict( + consumer={ + "class": "vzConsSubjLbl", + "rn": "conssubjlbl-", + }, + provider={ + "class": "vzProvSubjLbl", + "rn": "provsubjlbl-", + }, +) + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update( + contract_type=dict(type="str", required=True, choices=["consumer", "provider"]), + l3out=dict(type="str", aliases=["l3out_name"]), + subject_label=dict(type="str"), + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + tenant=dict(type="str"), + extepg=dict(type="str", aliases=["extepg_name", "external_epg"]), + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["extepg", "subject_label", "l3out", "tenant"]], + ["state", "present", ["extepg", "subject_label", "l3out", "tenant"]], + ], + ) + + l3out = module.params.get("l3out") + subject_label = module.params.get("subject_label") + contract_type = module.params.get("contract_type") + extepg = module.params.get("extepg") + state = module.params.get("state") + tenant = module.params.get("tenant") + + aci_class = ACI_CLASS_MAPPING.get(contract_type)["class"] + aci_rn = ACI_CLASS_MAPPING.get(contract_type)["rn"] + + aci = ACIModule(module) + aci.construct_url( + root_class=dict( + aci_class="fvTenant", + aci_rn="tn-{0}".format(tenant), + module_object=tenant, + target_filter={"name": tenant}, + ), + subclass_1=dict( + aci_class="l3extOut", + aci_rn="out-{0}".format(l3out), + module_object=l3out, + target_filter={"name": l3out}, + ), + subclass_2=dict( + aci_class="l3extInstP", + aci_rn="instP-{0}".format(extepg), + module_object=extepg, + target_filter={"name": extepg}, + ), + subclass_3=dict( + aci_class=aci_class, + aci_rn="{0}{1}".format(aci_rn, subject_label), + module_object=subject_label, + target_filter={"name": subject_label}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class=aci_class, + class_config=dict( + name=subject_label, + ), + ) + + aci.get_diff(aci_class=aci_class) + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/inventory.networking b/tests/integration/inventory.networking index 124630f84..39046a000 100644 --- a/tests/integration/inventory.networking +++ b/tests/integration/inventory.networking @@ -4,6 +4,7 @@ lh-dmz1-pod3-sim-v42 ansible_host=173.36.219.68 aci_hostname=173.36.219.68 lh-dmz1-pod3-sim-v51 ansible_host=173.36.219.129 aci_hostname=173.36.219.129 aws_cloud ansible_host=52.52.20.121 aci_hostname=52.52.20.121 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=aws region=us-east-1 region_2=us-west-1 availability_zone=us-west-1a azure_cloud ansible_host=104.42.26.226 aci_hostname=104.42.26.226 aci_password="sJ94G92#8dq2hx*K4qh" cloud_type=azure region=westus region_2=westus2 vnet_gateway=true +sandbox ansible_host=sandboxapicdc.cisco.com aci_hostname=sandboxapicdc.cisco.com aci_username=admin aci_password="!v3G@!4@Y" [aci:vars] aci_username=ansible_github_ci diff --git a/tests/integration/targets/aci_contract_subject/tasks/main.yml b/tests/integration/targets/aci_contract_subject/tasks/main.yml index 9441f410f..1d25d3448 100644 --- a/tests/integration/targets/aci_contract_subject/tasks/main.yml +++ b/tests/integration/targets/aci_contract_subject/tasks/main.yml @@ -1,5 +1,5 @@ # Test code for the ACI modules -# Copyright: (c) 2017, Jacob McGill (@jmcgill298) +# Copyright: (c) 2017, Jacob McGill (@jmcgill298), Mark Ciecior (@markciecior) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -63,6 +63,14 @@ priority: level3 register: subject_present_2 +- name: create subject - add subject labels + cisco.aci.aci_contract_subject: &aci_subject_present_label + <<: *aci_contract_present + subject: anstest3 + subject_label: my_test_label + subject_label_direction: consumer + register: subject_present_label + - name: missing param - failure message works cisco.aci.aci_contract_subject: <<: *aci_tenant_present @@ -90,6 +98,8 @@ - subject_present_2.sent.vzSubj.attributes.name == 'anstest2' - subject_present_2.sent.vzSubj.attributes.prio == 'level3' - subject_present_2.sent.vzSubj.attributes.revFltPorts == 'no' + - subject_present_label is changed + - subject_present_label.sent.vzConsSubjLbl.attributes.name == 'my_test_label' - present_missing_param is failed - 'present_missing_param.msg == "state is present but all of the following are missing: contract, subject"' @@ -137,6 +147,13 @@ contract: "{{ fakevar | default(omit) }}" register: query_subject +- name: query labeled subject + cisco.aci.aci_contract_subject: + <<: *aci_query_subject_label + tenant: "{{ fakevar | default(omit) }}" + contract: "{{ fakevar | default(omit) }}" + register: query_subject_label + - name: query all cisco.aci.aci_contract_subject: <<: *aci_tenant_present @@ -187,6 +204,10 @@ - query_subject.current.0.vzSubj.attributes.name == "anstest" - '"query-target-filter=eq(vzSubj.name,\"anstest\")" in query_subject.filter_string' - '"class/vzSubj.json" in query_subject.url' + - query_subject_label is not changed + - query_subject_label.current.0.vzSubj.attributes.name == "anstest3" + - '"query-target-filter=eq(vzConsSubjLbl.name,\"my_test_label\")" in query_subject_label.filter_string' + - '"class/vzConsSubjLbl.json" in query_subject_label.url' - query_all is not changed - query_all.current|length > 1 - query_all.current.0.vzSubj is defined @@ -214,6 +235,11 @@ <<: *aci_subject_present_2 state: absent +- name: delete subject with label - cleanup + cisco.aci.aci_contract_subject: + <<: *aci_subject_present_label + state: absent + - name: missing params - failure message works cisco.aci.aci_contract_subject: <<: *aci_subject_absent diff --git a/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml b/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml index 794910175..b8ddd49a6 100644 --- a/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml +++ b/tests/integration/targets/aci_l3out_extepg_to_contract/tasks/main.yml @@ -1,5 +1,5 @@ # Test code for the ACI modules -# Copyright: (c) 2020, Shreyas Srish (@shrsr) +# Copyright: (c) 2020, Shreyas Srish (@shrsr), Mark Ciecior (@markciecior) # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -62,6 +62,7 @@ extepg: ansible_extEpg contract: ansible_contract contract_type: provider + subject_label: my_test_label state: present register: bind_extepg_provider_contract @@ -70,6 +71,8 @@ that: - bind_extepg_provider_contract.current.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract" - bind_extepg_provider_contract.current.0.fvRsProv.attributes.annotation == 'orchestrator:ansible' + - bind_extepg_provider_contract.current.0.vzConsSubjLbl is defined + - bind_extepg_provider_contract.current.0.vzConsSubjLbl.attributes.name == 'my_test_label' - name: Bind second External End Point Groups to Contracts aci_l3out_extepg_to_contract: diff --git a/tests/integration/targets/aci_l3out_extepg_to_subject_label/aliases b/tests/integration/targets/aci_l3out_extepg_to_subject_label/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_l3out_extepg_to_subject_label/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_l3out_extepg_to_subject_label/tasks/main.yml b/tests/integration/targets/aci_l3out_extepg_to_subject_label/tasks/main.yml new file mode 100644 index 000000000..017e308b6 --- /dev/null +++ b/tests/integration/targets/aci_l3out_extepg_to_subject_label/tasks/main.yml @@ -0,0 +1,129 @@ +# Test code for the ACI modules +# Copyright: (c) 2020, Shreyas Srish (@shrsr), Mark Ciecior (@markciecior) + +# 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 + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + aci_tenant: + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + +- name: Add a new l3out + aci_l3out: + <<: *aci_info + tenant: ansible_tenant + name: ansible_l3out + description: l3out for Ansible tenant + domain: ansible_dom + route_control: export + vrf: ansible_vrf + l3protocol: ospf + state: present + +- name: Add a new ExtEpg + aci_l3out_extepg: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + name: ansible_extEpg + description: ExtEpg for Ansible l3out + state: present + +- name: Bind External End Point Groups to Subject Labels + aci_l3out_extepg_to_subject_label: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract_type: provider + subject_label: my_test_label + state: present + register: bind_extepg_provider_contract + +- name: Verify bind_extepg_provider_contract + assert: + that: + - bind_extepg_provider_contract.current.0.vzConsSubjLbl is defined + - bind_extepg_provider_contract.current.0.vzConsSubjLbl.attributes.name == 'my_test_label' + +- name: Query the External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subject_label: my_test_label + contract_type: provider + state: query + register: query_extepg + +- name: Verify query_extepg + assert: + that: + - query_extepg is not changed + - query_extepg.current.0.vzConsSubjLbl.attributes.name == 'my_test_label' + +- name: Query all the External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + contract_type: provider + state: query + register: query_all + +- name: Remove existing subject label to External End Point Groups + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + contract: ansible_contract + contract_type: provider + state: absent + register: remove_contract_extepg + +- name: Verify remove_contract_extepg + assert: + that: + - remove_contract_extepg.previous.0.fvRsProv.attributes.dn == "uni/tn-ansible_tenant/out-ansible_l3out/instP-ansible_extEpg/rsprov-ansible_contract" + +- name: Bind External End Point Groups to Contracts + aci_l3out_extepg_to_contract: + <<: *aci_info + tenant: ansible_tenant + l3out: ansible_l3out + extepg: ansible_extEpg + subject_label: my_test_label + provider_match: all + state: present + ignore_errors: yes + register: bind_extepg_consumer_contract + +- name: Verify bind_extepg_consumer_contract + assert: + that: + - bind_extepg_consumer_contract.msg == "state is present but all of the following are missing: contract_type" \ No newline at end of file