diff --git a/plugins/modules/aci_tenant_span_src_group_src.py b/plugins/modules/aci_tenant_span_src_group_src.py new file mode 100644 index 000000000..ff490f08c --- /dev/null +++ b/plugins/modules/aci_tenant_span_src_group_src.py @@ -0,0 +1,319 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright: (c) 2022, Akini Ross (@akinross) + +# 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_tenant_span_src_group_src +short_description: Manage SPAN source group sources (span:Src) +description: +- Manage SPAN source group sources on Cisco ACI fabrics. +options: + name: + description: + - The name of the Span source. + type: str + description: + description: + - The description for Span source. + type: str + aliases: [ descr ] + src_group: + description: + - The name of the Span source group. + type: str + tenant: + description: + - The name of the Tenant. + type: str + aliases: [ tenant_name ] + direction: + description: + - The direction of the SPAN source. + type: str + choices: [ incoming, outgoing, both ] + src_epg: + description: + - The name of the Span source epg. + type: str + aliases: [ epg ] + src_ap: + description: + - The name of the Span source ap. + type: str + aliases: [ ap ] + 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 +- cisco.aci.owner + +notes: +- The C(tenant) used must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) module can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(span:SrcGrp). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Akini Ross (@akinross) +""" + +EXAMPLES = r""" +- name: Create a SPAN source + cisco.aci.aci_tenant_span_src_group_src: + host: apic + username: admin + password: SomeSecretPassword + src_group: my_span_source_group + tenant: prod + name: test + direction: incoming + src_epg: epg1 + state: present + delegate_to: localhost + +- name: Delete a SPAN source + cisco.aci.aci_tenant_span_src_group_src: + host: apic + username: admin + password: SomeSecretPassword + src_group: my_span_source_group + tenant: prod + name: test + state: absent + delegate_to: localhost + +- name: Query all SPAN sources + cisco.aci.aci_tenant_span_src_group_src: + host: apic + username: admin + password: SomeSecretPassword + state: query + delegate_to: localhost + register: query_result + +- name: Query a specific SPAN source + cisco.aci.aci_tenant_span_src_group_src: + host: apic + username: admin + password: SomeSecretPassword + name: test + 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_owner_spec + +DIRECTION_MAP = { + "incoming": "in", "outgoing": "out", "both": "both" +} + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + description=dict(type="str", aliases=["descr"]), + direction=dict(type="str", choices=["incoming", "outgoing", "both"]), + name=dict(type="str"), # Not required for querying all objects + src_ap=dict(type="str", aliases=["ap"]), + src_epg=dict(type="str", aliases=["epg"]), + src_group=dict(type="str"), # Not required for querying all objects + state=dict(type="str", default="present", choices=["absent", "present", "query"]), + tenant=dict(type="str", aliases=["tenant_name"]), # Not required for querying all objects + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[ + ["state", "absent", ["name", "src_group", "tenant"]], + ["state", "present", ["name", "direction", "src_group", "tenant"]], + ], + required_together=[('src_ap', 'src_epg')], + ) + + aci = ACIModule(module) + + description = module.params.get("description") + direction = module.params.get("direction") + name = module.params.get("name") + src_ap = module.params.get("src_ap") + src_epg = module.params.get("src_epg") + src_group = module.params.get("src_group") + state = module.params.get("state") + tenant = module.params.get("tenant") + + 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="spanSrcGrp", + aci_rn="srcgrp-{0}".format(src_group), + module_object=src_group, + target_filter={"name": src_group}, + ), + subclass_2=dict( + aci_class="spanSrc", + aci_rn="src-{0}".format(name), + module_object=name, + target_filter={"name": name}, + ), + child_classes=["spanRsSrcToEpg"], + ) + + aci.get_existing() + + if state == "present": + + tdn = None + if src_epg: + tdn = "uni/tn-{0}/ap-{1}/epg-{2}".format(tenant, src_ap, src_epg) + + aci.payload( + aci_class="spanSrc", + class_config=dict(descr=description, name=name, dir=DIRECTION_MAP.get(direction)), + child_configs=[{"spanRsSrcToEpg": {"attributes": {"tDn": tdn}}}] + ) + + aci.get_diff(aci_class="spanSrc") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_tenant_span_src_group/aliases b/tests/integration/targets/aci_tenant_span_src_group/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_tenant_span_src_group/tasks/main.yml b/tests/integration/targets/aci_tenant_span_src_group/tasks/main.yml new file mode 100644 index 000000000..1af587cd0 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group/tasks/main.yml @@ -0,0 +1,178 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Akini Ross (@akinross) + +# 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: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant + <<: *aci_info + tenant: ansible_tenant + state: present + +- name: Add new bd + cisco.aci.aci_bd: &aci_bd + <<: *aci_tenant + bd: anstest + register: bd_present + +- name: Add a new source ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_source_ap + +- name: Add a new dest ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_dest_ap + +- name: Add a new dest epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_dest_epg + +- name: Add a new source epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_source_epg + +- name: Add span dest group + cisco.aci.aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_dest_group + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + destination_epg: + tenant: ansible_tenant + ap: ansible_dest_ap + epg: ansible_dest_epg + state: present + +- name: Create a source group (check mode) + cisco.aci.aci_tenant_span_src_group: &aci_span + <<: *aci_tenant + name: ansible_span + description: ansible test description + dst_group: ansible_dest_group + check_mode: yes + register: cm_create_with_dest + +- name: Create a source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_span + register: nm_create_with_dest + +- name: Create a source group again + cisco.aci.aci_tenant_span_src_group: + <<: *aci_span + register: nm_create_with_dest_again + +- name: Create second source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_tenant + name: ansible_span_2 + description: ansible test description + register: create_without_dest + +- name: Change second source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_tenant + name: ansible_span_2 + description: ansible test description 2 + register: change_without_dest + +- name: Verify create of source groups + ansible.builtin.assert: + that: + - cm_create_with_dest is changed + - nm_create_with_dest is changed + - nm_create_with_dest.current.0.spanSrcGrp.attributes.name == "ansible_span" + - nm_create_with_dest.current.0.spanSrcGrp.attributes.descr == "ansible test description" + - nm_create_with_dest.current.0.spanSrcGrp.children.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - nm_create_with_dest_again is not changed + - nm_create_with_dest_again.current.0.spanSrcGrp.attributes.name == "ansible_span" + - nm_create_with_dest_again.current.0.spanSrcGrp.attributes.descr == "ansible test description" + - nm_create_with_dest_again.current.0.spanSrcGrp.children.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - create_without_dest is changed + - create_without_dest.current.0.spanSrcGrp.attributes.name == "ansible_span_2" + - create_without_dest.current.0.spanSrcGrp.attributes.descr == "ansible test description" + - '"children" not in create_without_dest.current.0.spanSrcGrp' + - change_without_dest is changed + - change_without_dest.current.0.spanSrcGrp.attributes.name == "ansible_span_2" + - change_without_dest.current.0.spanSrcGrp.attributes.descr == "ansible test description 2" + - '"children" not in change_without_dest.current.0.spanSrcGrp' + +- name: Query a source groups + cisco.aci.aci_tenant_span_src_group: + <<: *aci_span + state: query + register: query_one + +- name: Query all source groups + cisco.aci.aci_tenant_span_src_group: + <<: *aci_info + state: query + register: query_all + +- name: Verify queries to source groups + ansible.builtin.assert: + that: + - query_one is not changed + - query_one.current.0.spanSrcGrp.attributes.name == "ansible_span" + - query_one.current.0.spanSrcGrp.attributes.descr == "ansible test description" + - query_one.current.0.spanSrcGrp.children.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - query_all is not changed + - query_all.current | length == 2 + +- name: Delete a source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_span + state: absent + register: delete + +- name: Delete a source group again + cisco.aci.aci_tenant_span_src_group: + <<: *aci_span + state: absent + register: delete_again + +- name: Verify delete of source group + ansible.builtin.assert: + that: + - delete is changed + - delete_again is not changed + - delete.current == [] + +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_tenant + state: absent \ No newline at end of file diff --git a/tests/integration/targets/aci_tenant_span_src_group_src/aliases b/tests/integration/targets/aci_tenant_span_src_group_src/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group_src/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_tenant_span_src_group_src/tasks/main.yml b/tests/integration/targets/aci_tenant_span_src_group_src/tasks/main.yml new file mode 100644 index 000000000..35268e814 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group_src/tasks/main.yml @@ -0,0 +1,198 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Akini Ross (@akinross) + +# 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: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant + <<: *aci_info + tenant: ansible_tenant + state: present + +- name: Add new bd + cisco.aci.aci_bd: &aci_bd + <<: *aci_tenant + bd: anstest + register: bd_present + +- name: Add a new source ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_source_ap + +- name: Add a new dest ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_dest_ap + +- name: Add a new dest epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_dest_epg + +- name: Add a new source epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_source_epg + +- name: Add span dest group + cisco.aci.aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_dest_group + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + destination_epg: + tenant: ansible_tenant + ap: ansible_dest_ap + epg: ansible_dest_epg + state: present + +- name: Create a source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_tenant + name: ansible_span + description: ansible test description + dst_group: ansible_dest_group + register: nm_create_with_dest + +- name: Create a ansible_source SPAN Source (check mode) + cisco.aci.aci_tenant_span_src_group_src: &aci_src + <<: *aci_tenant + name: ansible_source + description: ansible test description + direction: incoming + src_ap: ansible_source_ap + src_epg: ansible_source_epg + src_group: ansible_span + check_mode: yes + register: cm_create_with_epg + +- name: Create a ansible_source SPAN Source + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_src + register: nm_create_with_epg + +- name: Create a ansible_source SPAN Source again + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_src + register: nm_create_with_epg_again + +- name: Create second ansible_source SPAN Source + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_tenant + name: ansible_source_2 + description: ansible test description 2 + direction: outgoing + src_group: ansible_span + register: create_without_epg_direction_outgoing + +- name: Change second ansible_source SPAN Source + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_tenant + name: ansible_source_2 + description: ansible test description 3 + direction: both + src_group: ansible_span + register: change_without_epg_direction_both + +- name: Verify create and changes of sources + ansible.builtin.assert: + that: + - cm_create_with_epg is changed + - nm_create_with_epg is changed + - nm_create_with_epg.current.0.spanSrc.attributes.name == "ansible_source" + - nm_create_with_epg.current.0.spanSrc.attributes.descr == "ansible test description" + - nm_create_with_epg.current.0.spanSrc.attributes.dir == "in" + - nm_create_with_epg.current.0.spanSrc.children.0.spanRsSrcToEpg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_source_ap/epg-ansible_source_epg" + - nm_create_with_epg_again is not changed + - nm_create_with_epg_again.current.0.spanSrc.attributes.name == "ansible_source" + - nm_create_with_epg_again.current.0.spanSrc.attributes.descr == "ansible test description" + - nm_create_with_epg_again.current.0.spanSrc.attributes.dir == "in" + - nm_create_with_epg_again.current.0.spanSrc.children.0.spanRsSrcToEpg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_source_ap/epg-ansible_source_epg" + - create_without_epg_direction_outgoing is changed + - create_without_epg_direction_outgoing.current.0.spanSrc.attributes.name == "ansible_source_2" + - create_without_epg_direction_outgoing.current.0.spanSrc.attributes.descr == "ansible test description 2" + - create_without_epg_direction_outgoing.current.0.spanSrc.attributes.dir == "out" + - '"children" not in create_without_epg_direction_outgoing.current.0.spanSrc' + - change_without_epg_direction_both is changed + - change_without_epg_direction_both.current.0.spanSrc.attributes.name == "ansible_source_2" + - change_without_epg_direction_both.current.0.spanSrc.attributes.descr == "ansible test description 3" + - change_without_epg_direction_both.current.0.spanSrc.attributes.dir == "both" + - '"children" not in change_without_epg_direction_both.current.0.spanSrc' + +- name: Query a ansible_source SPAN Source + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_src + state: query + register: query_one + +- name: Query all ansible_source SPAN Sources + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_info + state: query + register: query_all + +- name: Verify queries of ansible_source SPAN Sources + ansible.builtin.assert: + that: + - query_one is not changed + - query_one.current.0.spanSrc.attributes.name == "ansible_source" + - query_one.current.0.spanSrc.attributes.descr == "ansible test description" + - query_one.current.0.spanSrc.attributes.dir == "in" + - query_one.current.0.spanSrc.children.0.spanRsSrcToEpg.attributes.tDn == "uni/tn-ansible_tenant/ap-ansible_source_ap/epg-ansible_source_epg" + - query_all is not changed + - query_all.current | length == 2 + +- name: Delete a ansible_source SPAN Source + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_src + state: absent + register: delete + +- name: Delete a ansible_source SPAN Source again + cisco.aci.aci_tenant_span_src_group_src: + <<: *aci_src + state: absent + register: delete_again + +- name: Verify delete of ansible_source SPAN Source + ansible.builtin.assert: + that: + - delete is changed + - delete_again is not changed + - delete.current == [] + +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_tenant + state: absent \ No newline at end of file diff --git a/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/aliases b/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/tasks/main.yml b/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/tasks/main.yml new file mode 100644 index 000000000..3390b86c1 --- /dev/null +++ b/tests/integration/targets/aci_tenant_span_src_group_to_dst_group/tasks/main.yml @@ -0,0 +1,196 @@ +# Test code for the ACI modules +# Copyright: (c) 2022, Akini Ross (@akinross) + +# 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: '{{ aci_output_level | default("info") }}' + +# CLEAN ENVIRONMENT +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant + <<: *aci_info + tenant: ansible_tenant + state: present + +- name: Add new bd + cisco.aci.aci_bd: &aci_bd + <<: *aci_tenant + bd: anstest + register: bd_present + +- name: Add a new source ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_source_ap + +- name: Add a new dest ap + cisco.aci.aci_ap: + <<: *aci_tenant + ap: ansible_dest_ap + +- name: Add a new dest epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_dest_epg + +- name: Add a new source epg + cisco.aci.aci_epg: + <<: *aci_bd + ap: ansible_dest_ap + epg: ansible_source_epg + +- name: Add span dest group + cisco.aci.aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_dest_group + destination_ip: 10.0.0.1 + source_ip: 10.0.2.1 + tenant: ansible_tenant + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + destination_epg: + tenant: ansible_tenant + ap: ansible_dest_ap + epg: ansible_dest_epg + state: present + +- name: Add span dest group + cisco.aci.aci_tenant_span_dst_group: + <<: *aci_info + destination_group: ansible_dest_group_2 + destination_ip: 20.0.0.1 + source_ip: 20.0.2.1 + tenant: ansible_tenant + ttl: 2 + mtu: 1500 + flow_id: 1 + dscp: "CS1" + destination_epg: + tenant: ansible_tenant + ap: ansible_dest_ap + epg: ansible_dest_epg + state: present + +- name: Create a source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_tenant + name: ansible_span + description: ansible test description + +- name: Create another source group + cisco.aci.aci_tenant_span_src_group: + <<: *aci_tenant + name: ansible_span_2 + description: ansible test description + +- name: Create a source group to destination (check mode) + cisco.aci.aci_tenant_span_src_group_to_dst_group: &aci_source_to_dest + <<: *aci_tenant + src_group: ansible_span + dst_group: ansible_dest_group + check_mode: yes + register: cm_create + +- name: Create a source group to destination + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + register: nm_create + +- name: Create a source group to destination again + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + register: nm_create_again + +- name: Create another source group to destination + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_tenant + src_group: ansible_span_2 + dst_group: ansible_dest_group_2 + register: nm_create_another + +- name: Change a source group to destination (error not allowed) + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + dst_group: ansible_dest_group_2 + ignore_errors: yes + register: error_change + +- name: Verify create source groups to destination + ansible.builtin.assert: + that: + - cm_create is changed + - nm_create is changed + - nm_create.current.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - nm_create_again is not changed + - nm_create_again.current.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - nm_create_another is changed + - nm_create_another.current.0.spanSpanLbl.attributes.name == "ansible_dest_group_2" + - error_change is not changed + - 'error_change.msg == "APIC Error 105: Only one span destination is supported per session"' + +- name: Query a a source group to destination + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + state: query + register: query_one + +- name: Query all a source group to destinations + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_info + state: query + register: query_all + +- name: Verify queries of source group to destination + ansible.builtin.assert: + that: + - query_one is not changed + - query_one.current.0.spanSpanLbl.attributes.name == "ansible_dest_group" + - query_all is not changed + - query_all.current | length == 2 + +- name: Delete a source group to destination + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + state: absent + register: delete + +- name: Delete a source group to destination again + cisco.aci.aci_tenant_span_src_group_to_dst_group: + <<: *aci_source_to_dest + state: absent + register: delete_again + +- name: Verify delete of source group to destinations + ansible.builtin.assert: + that: + - delete is changed + - delete_again is not changed + - delete.current == [] + +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: + <<: *aci_tenant + state: absent \ No newline at end of file