diff --git a/meta/runtime.yml b/meta/runtime.yml
index 4b462e205..345a53f66 100644
--- a/meta/runtime.yml
+++ b/meta/runtime.yml
@@ -276,3 +276,18 @@ plugin_routing:
deprecation:
removal_version: 3.0.0
warning_text: Use cisco.aci.aci_l4l7_policy_based_redirect_destination instead.
+ aci_l4l7_service_graph_template_func_conn:
+ redirect: cisco.aci.aci_l4l7_service_graph_template_functional_connection
+ deprecation:
+ removal_version: 3.0.0
+ warning_text: Use aci_l4l7_service_graph_template_functional_connection instead.
+ aci_l4l7_service_graph_template_abs_conn:
+ redirect: cisco.aci.aci_l4l7_service_graph_template_abs_connection
+ deprecation:
+ removal_version: 3.0.0
+ warning_text: Use cisco.aci.aci_l4l7_service_graph_template_abs_connection instead.
+ aci_l4l7_device_selection_if_context:
+ redirect: cisco.aci.aci_l4l7_device_selection_interface_context
+ deprecation:
+ removal_version: 3.0.0
+ warning_text: Use cisco.aci.aci_l4l7_device_selection_interface_context instead.
diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py
index 1bc3af185..73062deb2 100644
--- a/plugins/module_utils/constants.py
+++ b/plugins/module_utils/constants.py
@@ -451,3 +451,31 @@
L4L7_FUNC_TYPES_MAPPING = {"go_to": "GoTo", "go_through": "GoThrough", "l1": "L1", "l2": "L2"}
L4L7_HASH_ALGORITHMS_MAPPING = {"source_ip": "sip", "destination_ip": "dip", "ip_and_protocol": "sip-dip-prototype"}
+
+L4L7_FUNCTIONAL_TEMPLATE_TYPES_MAPPING = {
+ "adc_one_arm": "ADC_ONE_ARM",
+ "adc_two_arm": "ADC_TWO_ARM",
+ "cloud_native_fw": "CLOUD_NATIVE_FW",
+ "cloud_native_lb": "CLOUD_NATIVE_LB",
+ "cloud_vendor_fw": "CLOUD_VENDOR_FW",
+ "cloud_vendor_lb": "CLOUD_VENDOR_LB",
+ "fw_routed": "FW_ROUTED",
+ "fw_trans": "FW_TRANS",
+ "other": "OTHER",
+}
+
+L4L7_UI_TEMPLATE_TYPE = {
+ "ndo_implicit_template": "NDO_IMPLICIT_TEMPLATE",
+ "one_node_adc_one_arm": "ONE_NODE_ADC_ONE_ARM",
+ "one_node_adc_one_arm_l3ext": "ONE_NODE_ADC_ONE_ARM_L3EXT",
+ "one_node_adc_two_arm": "ONE_NODE_ADC_TWO_ARM",
+ "one_node_fw_routed": "ONE_NODE_FW_ROUTED",
+ "one_node_fw_trans": "ONE_NODE_FW_TRANS",
+ "two_node_fw_routed_adc_one_arm": "TWO_NODE_FW_ROUTED_ADC_ONE_ARM",
+ "two_node_fw_routed_adc_one_arm_l3ext": "TWO_NODE_FW_ROUTED_ADC_ONE_ARM_L3EXT",
+ "two_node_fw_routed_adc_two_arm": "TWO_NODE_FW_ROUTED_ADC_TWO_ARM",
+ "two_node_fw_trans_adc_one_arm": "TWO_NODE_FW_TRANS_ADC_ONE_ARM",
+ "two_node_fw_trans_adc_one_arm_l3ext": "TWO_NODE_FW_TRANS_ADC_ONE_ARM_L3EXT",
+ "two_node_fw_trans_adc_two_arm": "TWO_NODE_FW_TRANS_ADC_TWO_ARM",
+ "unspecified": "UNSPECIFIED",
+}
diff --git a/plugins/modules/aci_l4l7_device_selection_interface_context.py b/plugins/modules/aci_l4l7_device_selection_interface_context.py
new file mode 100644
index 000000000..f25869f08
--- /dev/null
+++ b/plugins/modules/aci_l4l7_device_selection_interface_context.py
@@ -0,0 +1,452 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_device_selection_interface_context
+short_description: Manage L4-L7 Device Selection Policy Logical Interface Contexts (vns:LIfCtx)
+description:
+- Manage L4-L7 Device Selection Policy Logical Interface Contexts
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ contract:
+ description:
+ - The name of an existing contract.
+ type: str
+ aliases: [ contract_name ]
+ graph:
+ description:
+ - The name of an existing Service Graph Template.
+ type: str
+ aliases: [ service_graph, service_graph_name ]
+ node:
+ description:
+ - The name of an existing Service Graph Node.
+ type: str
+ aliases: [ node_name ]
+ context:
+ description:
+ - The name of the logical interface context.
+ type: str
+ l3_destination:
+ description:
+ - Whether the context is a Layer3 destination.
+ - The APIC defaults to C(true) when unset during creation.
+ type: bool
+ aliases: [ l3_dest ]
+ permit_log:
+ description:
+ - Whether to log permitted traffic.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ bridge_domain:
+ description:
+ - The Bridge Domain to bind to the Context.
+ type: str
+ aliases: [ bd, bd_name ]
+ bridge_domain_tenant:
+ description:
+ - The tenant the Bridge Domain resides in.
+ - Omit this variable if both context and Bridge Domain are in the same tenant.
+ - Intended use case is for when the Bridge Domain is in the common tenant, but the context is not.
+ type: str
+ aliases: [ bd_tenant ]
+ logical_device:
+ description:
+ - The Logical Device to bind the context to.
+ type: str
+ logical_interface:
+ description:
+ - The Logical Interface to bind the context to.
+ type: str
+ redirect_policy:
+ description:
+ - The Redirect Policy to bind the context to.
+ type: str
+ permit_handoff:
+ description:
+ - Indicates whether to allow handoff of traffic to the associated logical interface.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ acl:
+ description:
+ - Specifies whether an Access Control List (ACL) is applied to the logical interface.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ description:
+ description:
+ - A brief description for the Logical Interface Context.
+ type: str
+ rule_type:
+ description:
+ - Indicates whether the context uses a specific rule type for traffic handling.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ 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 I(tenant), I(graph), I(contract) and I(node) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l4l7_service_graph_template), M(cisco.aci.aci_contract)
+ and M(cisco.aci.aci_l4l7_service_graph_template_node) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- module: cisco.aci.aci_contract
+- module: cisco.aci.aci_l4l7_service_graph_template_node
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class, B(vns:LIfCtx)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new interface context
+ cisco.aci.aci_l4l7_device_selection_interface_context:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ context: provider
+ state: present
+ delegate_to: localhost
+
+- name: Query an interface context
+ cisco.aci.aci_l4l7_device_selection_interface_context:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ context: consumer
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all interface contexts
+ cisco.aci.aci_l4l7_device_selection_interface_context:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete an interface context
+ cisco.aci.aci_l4l7_device_selection_interface_context:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ context: provider
+ 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
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ contract=dict(type="str", aliases=["contract_name"]),
+ graph=dict(type="str", aliases=["service_graph", "service_graph_name"]),
+ node=dict(type="str", aliases=["node_name"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ context=dict(type="str"),
+ l3_destination=dict(type="bool", aliases=["l3_dest"]),
+ permit_log=dict(type="bool"),
+ bridge_domain=dict(type="str", aliases=["bd", "bd_name"]),
+ bridge_domain_tenant=dict(type="str", aliases=["bd_tenant"]),
+ logical_device=dict(type="str"),
+ logical_interface=dict(type="str"),
+ redirect_policy=dict(type="str"),
+ permit_handoff=dict(type="bool"),
+ acl=dict(type="bool"),
+ description=dict(type="str"),
+ rule_type=dict(type="bool"),
+ )
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "contract", "graph", "node", "context"]],
+ ["state", "present", ["tenant", "contract", "graph", "node", "context"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ contract = module.params.get("contract")
+ graph = module.params.get("graph")
+ node = module.params.get("node")
+ context = module.params.get("context")
+ l3_destination = aci.boolean(module.params.get("l3_destination"))
+ permit_log = aci.boolean(module.params.get("permit_log"))
+ bridge_domain = module.params.get("bridge_domain")
+ bridge_domain_tenant = module.params.get("bridge_domain_tenant")
+ logical_device = module.params.get("logical_device")
+ logical_interface = module.params.get("logical_interface")
+ redirect_policy = module.params.get("redirect_policy")
+ permit_handoff = aci.boolean(module.params.get("permit_handoff"))
+ acl = aci.boolean(module.params.get("acl"))
+ description = module.params.get("description")
+ rule_type = aci.boolean(module.params.get("rule_type"))
+
+ ldev_ctx_rn = "ldevCtx-c-{0}-g-{1}-n-{2}".format(contract, graph, node) if (contract, graph, node) != (None, None, None) else None
+
+ 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="vnsLDevCtx",
+ aci_rn=ldev_ctx_rn,
+ module_object=ldev_ctx_rn,
+ target_filter={"dn": ldev_ctx_rn},
+ ),
+ subclass_2=dict(
+ aci_class="vnsLIfCtx",
+ aci_rn="lIfCtx-c-{0}".format(context),
+ module_object=context,
+ target_filter={"connNameOrLbl": context},
+ ),
+ child_classes=["vnsRsLIfCtxToBD", "vnsRsLIfCtxToLIf", "vnsRsLIfCtxToSvcRedirectPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if bridge_domain:
+ if bridge_domain_tenant is None:
+ bridge_domain_tenant = tenant
+ bd_tdn = "uni/tn-{0}/BD-{1}".format(bridge_domain_tenant, bridge_domain)
+ child_configs.append({"vnsRsLIfCtxToBD": {"attributes": {"tDn": bd_tdn}}})
+ else:
+ bd_tdn = None
+ if logical_interface:
+ log_intf_tdn = "uni/tn-{0}/lDevVip-{1}/lIf-{2}".format(tenant, logical_device, logical_interface)
+ child_configs.append({"vnsRsLIfCtxToLIf": {"attributes": {"tDn": log_intf_tdn}}})
+ else:
+ log_intf_tdn = None
+ if redirect_policy:
+ redir_pol_tdn = "uni/tn-{0}/svcCont/svcRedirectPol-{1}".format(tenant, redirect_policy)
+ child_configs.append({"vnsRsLIfCtxToSvcRedirectPol": {"attributes": {"tDn": redir_pol_tdn}}})
+ else:
+ redir_pol_tdn = None
+ # Validate if existing and remove child objects when do not match provided configuration
+ if isinstance(aci.existing, list) and len(aci.existing) > 0:
+ for child in aci.existing[0].get("vnsLIfCtx", {}).get("children", {}):
+ if child.get("vnsRsLIfCtxToBD") and child.get("vnsRsLIfCtxToBD").get("attributes").get("tDn") != bd_tdn:
+ # Appending to child_config list not possible because of APIC Error 103: child (Rn) of class vnsRsLIfCtxToBD is already attached.
+ # A seperate delete request to dn of the vnsRsLIfCtxToBD is needed to remove the object prior to adding to child_configs.
+ # child_configs.append(
+ # {
+ # "vnsRsLIfCtxToBD": {
+ # "attributes": {
+ # "dn": child.get("vnsRsLIfCtxToBD").get("attributes").get("dn"),
+ # "status": "deleted",
+ # }
+ # }
+ # }
+ # )
+ aci.api_call(
+ "DELETE",
+ "{0}/api/mo/uni/tn-{1}/ldevCtx-c-{2}-g-{3}-n-{4}/lIfCtx-c-{5}/rsLIfCtxToBD.json".format(
+ aci.base_url, tenant, contract, graph, node, context
+ ),
+ )
+ elif child.get("vnsRsLIfCtxToLIf") and child.get("vnsRsLIfCtxToLIf").get("attributes").get("tDn") != log_intf_tdn:
+ child_configs.append(
+ {
+ "vnsRsLIfCtxToLIf": {
+ "attributes": {
+ "dn": child.get("vnsRsLIfCtxToLIf").get("attributes").get("dn"),
+ "status": "deleted",
+ }
+ }
+ }
+ )
+ elif child.get("vnsRsLIfCtxToSvcRedirectPol") and child.get("vnsRsLIfCtxToSvcRedirectPol").get("attributes").get("tDn") != redir_pol_tdn:
+ child_configs.append(
+ {
+ "vnsRsLIfCtxToSvcRedirectPol": {
+ "attributes": {
+ "dn": child.get("vnsRsLIfCtxToSvcRedirectPol").get("attributes").get("dn"),
+ "status": "deleted",
+ }
+ }
+ }
+ )
+ aci.payload(
+ aci_class="vnsLIfCtx",
+ class_config=dict(
+ connNameOrLbl=context,
+ l3Dest=l3_destination,
+ permitLog=permit_log,
+ permitHandoff=permit_handoff,
+ acl=acl,
+ descr=description,
+ ruleType=rule_type,
+ ),
+ child_configs=child_configs,
+ )
+ aci.get_diff(aci_class="vnsLIfCtx")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_device_selection_policy.py b/plugins/modules/aci_l4l7_device_selection_policy.py
new file mode 100644
index 000000000..289e3196f
--- /dev/null
+++ b/plugins/modules/aci_l4l7_device_selection_policy.py
@@ -0,0 +1,339 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_device_selection_policy
+short_description: Manage L4-L7 Device Selection Policies (vns:LDevCtx)
+description:
+- Manage L4-L7 Device Selection Policies
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ contract:
+ description:
+ - The name of an existing contract.
+ - The APIC defaults to C(any) when unset during creation.
+ type: str
+ aliases: [ contract_name ]
+ graph:
+ description:
+ - The name of an existing service graph.
+ - The APIC defaults to C(any) when unset during creation.
+ type: str
+ aliases: [ service_graph, service_graph_name ]
+ node:
+ description:
+ - The name of an existing L4-L7 node.
+ - The APIC defaults to C(any) when unset during creation.
+ type: str
+ aliases: [ node_name ]
+ device:
+ description:
+ - The name of the L4-L7 Device to bind to the policy.
+ type: str
+ context:
+ description:
+ - The context name.
+ type: str
+ description:
+ description:
+ - A brief description for the Device Selection Policy.
+ type: 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 I(tenant), I(contract), I(graph), I(device) and I(node) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_contract), M(cisco.aci.aci_l4l7_service_graph), M(cisco.aci.aci_l4l7_device) and
+ M(cisco.aci.aci_l4l7_service_graph_template_node) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_contract
+- module: cisco.aci.aci_l4l7_service_graph
+- module: cisco.aci.aci_l4l7_device
+- module: cisco.aci.aci_l4l7_service_graph_template_node
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:LDevCtx)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new device selection policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ state: present
+ delegate_to: localhost
+
+- name: Query a device selection policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all device selection policies
+ cisco.aci.aci_l4l7_device_selection_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a device selection policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ contract: my_contract
+ graph: my_graph
+ node: my_node
+ 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
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ contract=dict(type="str", aliases=["contract_name"]),
+ graph=dict(type="str", aliases=["service_graph", "service_graph_name"]),
+ node=dict(type="str", aliases=["node_name"]),
+ device=dict(type="str"),
+ context=dict(type="str"),
+ description=dict(type="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", ["tenant", "contract", "graph", "node"]],
+ ["state", "present", ["tenant", "contract", "graph", "node"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ contract = module.params.get("contract")
+ graph = module.params.get("graph")
+ node = module.params.get("node")
+ device = module.params.get("device")
+ context = module.params.get("context")
+ description = module.params.get("description")
+
+ ldev_ctx_rn = "ldevCtx-c-{0}-g-{1}-n-{2}".format(contract, graph, node) if (contract, graph, node) != (None, None, None) else None
+
+ 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="vnsLDevCtx",
+ aci_rn=ldev_ctx_rn,
+ module_object=ldev_ctx_rn,
+ target_filter={"dn": ldev_ctx_rn},
+ ),
+ child_classes=["vnsRsLDevCtxToLDev"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if device:
+ device_tdn = "uni/tn-{0}/lDevVip-{1}".format(tenant, device)
+ child_configs.append({"vnsRsLDevCtxToLDev": {"attributes": {"tDn": device_tdn}}})
+ else:
+ device_tdn = None
+ # Validate if existing and remove child objects when do not match provided configuration
+ if isinstance(aci.existing, list) and len(aci.existing) > 0:
+ for child in aci.existing[0].get("vnsLDevCtx", {}).get("children", {}):
+ if child.get("vnsRsLDevCtxToLDev") and child.get("vnsRsLDevCtxToLDev").get("attributes").get("tDn") != device_tdn:
+ child_configs.append(
+ {
+ "vnsRsLDevCtxToLDev": {
+ "attributes": {
+ "dn": child.get("vnsRsLDevCtxToLDev").get("attributes").get("dn"),
+ "status": "deleted",
+ }
+ }
+ }
+ )
+ aci.payload(
+ aci_class="vnsLDevCtx",
+ class_config=dict(
+ ctrctNameOrLbl=contract,
+ graphNameOrLbl=graph,
+ nodeNameOrLbl=node,
+ context=context,
+ descr=description,
+ ),
+ child_configs=child_configs,
+ )
+ aci.get_diff(aci_class="vnsLDevCtx")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template.py b/plugins/modules/aci_l4l7_service_graph_template.py
new file mode 100644
index 000000000..78b8dae09
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template.py
@@ -0,0 +1,325 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_service_graph_template
+short_description: Manage L4-L7 Service Graph Templates (vns:AbsGraph)
+description:
+- Manage Layer 4 to Layer 7 (L4-L7) Service Graph Templates on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of Service Graph Template.
+ type: str
+ ui_template_type:
+ description:
+ - The UI Template Type.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [
+ ndo_implicit_template,
+ one_node_adc_one_arm,
+ one_node_adc_one_arm_l3ext,
+ one_node_adc_two_arm,
+ one_node_fw_routed,
+ one_node_fw_trans,
+ two_node_fw_routed_adc_one_arm,
+ two_node_fw_routed_adc_one_arm_l3ext,
+ two_node_fw_routed_adc_two_arm,
+ two_node_fw_trans_adc_one_arm,
+ two_node_fw_trans_adc_one_arm_l3ext,
+ two_node_fw_trans_adc_two_arm,
+ unspecified
+ ]
+ type:
+ description:
+ - Specifies the type of Service Graph Template.
+ - The APIC defaults to C(legacy) when unset during creation.
+ type: str
+ choices: [ cloud, legacy ]
+ service_rule_type:
+ description:
+ - Defines the type of service rule applied within the Service Graph Template.
+ - The APIC defaults to C(vrf) when unset during creation.
+ type: str
+ choices: [ epg, subnet, vrf ]
+ filter_between_nodes:
+ description:
+ - Determines how traffic is filtered between nodes in the Service Graph Template.
+ - The APIC defaults to C(allow-all) when unset during creation.
+ type: str
+ choices: [ allow-all, filters-from-contract ]
+ description:
+ description:
+ - A description of the Service Graph Template.
+ type: 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
+- cisco.aci.owner
+notes:
+- The I(tenant) 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(vns:AbsGraph)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ state: present
+ delegate_to: localhost
+
+- name: Query a Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ state: query
+ register: query_result
+ delegate_to: localhost
+
+- name: Query all Service Graph Templates
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ 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, aci_owner_spec
+from ansible_collections.cisco.aci.plugins.module_utils.constants import L4L7_UI_TEMPLATE_TYPE
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ ui_template_type=dict(type="str", choices=list(L4L7_UI_TEMPLATE_TYPE)),
+ type=dict(type="str", choices=["cloud", "legacy"]),
+ service_rule_type=dict(type="str", choices=["epg", "subnet", "vrf"]),
+ filter_between_nodes=dict(type="str", choices=["allow-all", "filters-from-contract"]),
+ description=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph"]],
+ ["state", "present", ["tenant", "service_graph"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ state = module.params.get("state")
+ ui_template_type = L4L7_UI_TEMPLATE_TYPE.get(module.params.get("ui_template_type"))
+ type = module.params.get("type")
+ service_rule_type = module.params.get("service_rule_type")
+ filter_between_nodes = module.params.get("filter_between_nodes")
+ description = (module.params.get("description"),)
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ child_classes=["vnsAbsTermNodeProv", "vnsAbsTermNodeCon"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsAbsGraph",
+ class_config=dict(
+ name=service_graph,
+ uiTemplateType=ui_template_type,
+ type=type,
+ svcRuleType=service_rule_type,
+ filterBetweenNodes=filter_between_nodes,
+ descr=description,
+ ),
+ )
+ aci.get_diff(aci_class="vnsAbsGraph")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template_abs_connection.py b/plugins/modules/aci_l4l7_service_graph_template_abs_connection.py
new file mode 100644
index 000000000..d931083a6
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_abs_connection.py
@@ -0,0 +1,332 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_service_graph_template_abs_connection
+short_description: Manage L4-L7 Service Graph Template Abs Connections (vns:AbsConnection)
+description:
+- Manage Layer 4 to Layer 7 (L4-L7) Service Graph Template Connections.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of an existing Service Graph.
+ type: str
+ connection_name:
+ description:
+ - The name of the connection.
+ type: str
+ direct_connect:
+ description:
+ - Whether to enable direct connections.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ unicast_route:
+ description:
+ - Whether to enable unicast routing.
+ - The APIC defaults to C(true) when unset during creation.
+ type: bool
+ adjacency_type:
+ description:
+ - Whether the adjacency is Layer2 or Layer3.
+ - The APIC defaults to C(l2) when unset during creation.
+ type: str
+ choices: [ l2, l3 ]
+ connector_direction:
+ description:
+ - The connector direction.
+ - The APIC defaults to C(unknown) when unset during creation.
+ type: str
+ choices: [ provider, consumer, unknown ]
+ connection_type:
+ description:
+ - The connection type.
+ - The APIC defaults to C(external) when unset during creation.
+ type: str
+ choices: [ internal, external ]
+ description:
+ description:
+ - The description for the Service Graph Template connection.
+ type: 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
+- cisco.aci.owner
+notes:
+- The I(tenant) and I(service_graph) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_service_graph_template) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class, B(vns:AbsConnection)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_graph
+ connection_name: C1
+ state: present
+ delegate_to: localhost
+
+- name: Query a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_graph
+ connection_name: C1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all connections
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_graph
+ connection_name: C1
+ 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, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ connection_name=dict(type="str"),
+ direct_connect=dict(type="bool"),
+ unicast_route=dict(type="bool"),
+ adjacency_type=dict(type="str", choices=["l2", "l3"]),
+ connector_direction=dict(type="str", choices=["provider", "consumer", "unknown"]),
+ connection_type=dict(type="str", choices=["internal", "external"]),
+ description=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph", "connection_name"]],
+ ["state", "present", ["tenant", "service_graph", "connection_name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ state = module.params.get("state")
+ connection_name = module.params.get("connection_name")
+ unicast_route = aci.boolean(module.params.get("unicast_route"))
+ adjacency_type = module.params.get("adjacency_type").upper() if module.params.get("adjacency_type") is not None else None
+ direct_connect = aci.boolean(module.params.get("direct_connect"))
+ connector_direction = module.params.get("connector_direction")
+ connection_type = module.params.get("connection_type")
+ description = module.params.get("description")
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ subclass_2=dict(
+ aci_class="vnsAbsConnection",
+ aci_rn="AbsConnection-{0}".format(connection_name),
+ module_object=connection_name,
+ target_filter={"name": connection_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsAbsConnection",
+ class_config=dict(
+ name=connection_name,
+ adjType=adjacency_type,
+ directConnect=direct_connect,
+ unicastRoute=unicast_route,
+ connType=connection_type,
+ connDir=connector_direction,
+ descr=description,
+ ),
+ )
+ aci.get_diff(aci_class="vnsAbsConnection")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template_abs_connection_conns.py b/plugins/modules/aci_l4l7_service_graph_template_abs_connection_conns.py
new file mode 100644
index 000000000..498756c05
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_abs_connection_conns.py
@@ -0,0 +1,301 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# 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_l4l7_service_graph_template_abs_connection_conns
+short_description: Manage L4-L7 Service Graph Template Connections (vns:RsAbsConnectionConns)
+description:
+- Manage Manage L4-L7 Service Graph Template Connections.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of an existing Service Graph.
+ type: str
+ connection_name:
+ description:
+ - The name of an existing vns:AbsConnection object.
+ type: str
+ direction:
+ description:
+ - The direction of the connection.
+ - If this links to a terminal node, both vns:RsAbsConnectionConns will use the same direction.
+ - Otherwise one vns:RsAbsConnectionConns will be consumer, and the other will be provider.
+ type: str
+ choices: [ consumer, provider ]
+ connected_node:
+ description:
+ - The name of an existing node.
+ - Omit this variable for connections to terminal nodes.
+ type: 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 I(tenant), I(service_graph) and I(connection_name) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l4l7_service_graph_template)
+ and M(cisco.aci.aci_l4l7_service_graph_template_abs_conn) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- module: cisco.aci.aci_l4l7_service_graph_template_abs_connection
+- module: cisco.aci.aci_l4l7_service_graph_template_node
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class, B(vns:RsAbsConnectionConns)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ direction: provider
+ connection_name: C1
+ state: present
+ delegate_to: localhost
+
+- name: Query a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ direction: provider
+ connection_name: C1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ direction: provider
+ connection_name: C1
+ 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
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ connection_name=dict(type="str"),
+ direction=dict(type="str", choices=["consumer", "provider"]),
+ connected_node=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph", "connection_name", "direction"]],
+ ["state", "present", ["tenant", "service_graph", "connection_name", "direction"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ state = module.params.get("state")
+ connection_name = module.params.get("connection_name")
+ direction = module.params.get("direction")
+ connected_node = module.params.get("connected_node")
+
+ aci = ACIModule(module)
+ if connected_node:
+ tdn = "uni/tn-{0}/AbsGraph-{1}/AbsNode-{2}/AbsFConn-{3}".format(tenant, service_graph, connected_node, direction)
+ elif direction == "consumer":
+ tdn = "uni/tn-{0}/AbsGraph-{1}/AbsTermNodeCon-T1/AbsTConn".format(tenant, service_graph)
+ elif direction == "provider":
+ tdn = "uni/tn-{0}/AbsGraph-{1}/AbsTermNodeProv-T2/AbsTConn".format(tenant, service_graph)
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ subclass_2=dict(
+ aci_class="vnsAbsConnection",
+ aci_rn="AbsConnection-{0}".format(connection_name),
+ module_object=connection_name,
+ target_filter={"name": connection_name},
+ ),
+ subclass_3=dict(
+ aci_class="vnsRsAbsConnectionConns",
+ aci_rn="rsabsConnectionConns-[{0}]".format(tdn),
+ module_object=tdn,
+ target_filter={"tDn": tdn},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsRsAbsConnectionConns",
+ class_config=dict(tDn=tdn),
+ )
+ aci.get_diff(aci_class="vnsRsAbsConnectionConns")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template_functional_connection.py b/plugins/modules/aci_l4l7_service_graph_template_functional_connection.py
new file mode 100644
index 000000000..39c4841b0
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_functional_connection.py
@@ -0,0 +1,324 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# 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_l4l7_service_graph_template_functional_connection
+short_description: Manage L4-L7 Service Graph Templates Functional Connections (vns:AbsFuncConn)
+description:
+- Manage Manage L4-L7 Service Graph Templates Functional Connections.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of an existing Service Graph.
+ type: str
+ node:
+ description:
+ - The name an existing Service Graph Node.
+ type: str
+ connection_name:
+ description:
+ - Whether this Functional Connection is the consumer or provider.
+ type: str
+ choices: [ consumer, provider ]
+ attachment_notify:
+ description:
+ - Indicates whether attachment notifications are enabled for this connection.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ description:
+ description:
+ - The description for the Service Graph connection.
+ type: str
+ connection_type:
+ description:
+ - Specifies the type of connection for the node.
+ - The APIC defaults to C(none) when unset during creation.
+ type: str
+ choices: [ dnat, none, redir, snat, snat_dnat ]
+ device_interface_name:
+ description:
+ - The name of the device interface associated with this connection.
+ type: 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
+- cisco.aci.owner
+notes:
+- The I(tenant), I(service_graph) and I(node) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l4l7_service_graph_template) and M(cisco.aci.aci_l4l7_service_graph_template_node) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- module: cisco.aci.aci_l4l7_service_graph_template_node
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes, B(vns:AbsFuncConn)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Consumer Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_functional_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ connection_name: consumer
+ state: present
+ delegate_to: localhost
+
+- name: Query a Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_functional_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ connection_name: consumer
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Functional Connections
+ cisco.aci.aci_l4l7_service_graph_template_functional_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_functional_connection:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ connection_name: consumer
+ 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, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ node=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ connection_name=dict(type="str", choices=["consumer", "provider"]),
+ attachment_notify=dict(type="bool"),
+ connection_type=dict(type="str", choices=["dnat", "none", "redir", "snat", "snat_dnat"]),
+ description=dict(type="str"),
+ device_interface_name=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph", "node", "connection_name"]],
+ ["state", "present", ["tenant", "service_graph", "node", "connection_name"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ node = module.params.get("node")
+ state = module.params.get("state")
+ connection_name = module.params.get("connection_name")
+ attachment_notify = aci.boolean(module.params.get("attachment_notification"))
+ description = module.params.get("description")
+ connection_type = module.params.get("connection_type")
+ device_interface_name = module.params.get("device_interface_name")
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ subclass_2=dict(
+ aci_class="vnsAbsNode",
+ aci_rn="AbsNode-{0}".format(node),
+ module_object=node,
+ target_filter={"name": node},
+ ),
+ subclass_3=dict(
+ aci_class="vnsAbsFuncConn",
+ aci_rn="AbsFConn-{0}".format(connection_name),
+ module_object=connection_name,
+ target_filter={"name": connection_name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsAbsFuncConn",
+ class_config=dict(
+ name=connection_name, attNotify=attachment_notify, connType=connection_type, descr=description, deviceLIfName=device_interface_name
+ ),
+ )
+ aci.get_diff(aci_class="vnsAbsFuncConn")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template_node.py b/plugins/modules/aci_l4l7_service_graph_template_node.py
new file mode 100644
index 000000000..5cf87ee4e
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_node.py
@@ -0,0 +1,384 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_service_graph_template_node
+short_description: Manage L4-L7 Service Graph Templates Nodes (vns:AbsNode)
+description:
+- Manage Manage L4-L7 Service Graph Templates Nodes.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of an existing Service Graph.
+ type: str
+ description:
+ description:
+ - The description of the Service Graph Template Node.
+ type: str
+ node:
+ description:
+ - The name of the Service Graph Template Node.
+ type: str
+ functional_template_type:
+ description:
+ - The functional template type for the node.
+ - The APIC defaults to C(other) when unset during creation.
+ type: str
+ choices: [
+ adc_one_arm,
+ adc_two_arm,
+ cloud_native_fw,
+ cloud_native_lb,
+ cloud_vendor_fw,
+ cloud_vendor_lb,
+ fw_routed,
+ fw_trans,
+ other
+ ]
+ aliases: [ func_template_type ]
+ function_type:
+ description:
+ - The type of function.
+ - The APIC defaults to C(go_to) when unset during creation.
+ type: str
+ choices: [ go_to, go_through, l1, l2 ]
+ aliases: [ func_type ]
+ device:
+ description:
+ - The name of an existing logical device.
+ type: str
+ device_tenant:
+ description:
+ - The tenant the logical device exists under.
+ - This variable is only used if logical device and node exist within different tenants.
+ - Intended use case is when the device is in the C(common) tenant but the node is not.
+ type: str
+ managed:
+ description:
+ - Whether this device managed by the apic.
+ - The APIC defaults to C(true) when unset during creation.
+ type: bool
+ routing_mode:
+ description:
+ - The routing mode for the node.
+ - The APIC defaults to C(unspecified) when unset during creation.
+ type: str
+ choices: [ redirect, unspecified ]
+ is_copy:
+ description:
+ - Whether the device is a copy device.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ share_encap:
+ description:
+ - Whether to share encapsulation across the service graph.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ 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 I(tenant), I(service_graph) and I(device) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant), M(cisco.aci.aci_l4l7_service_graph_template) and M(cisco.aci.aci_l4l7_device) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- module: cisco.aci.aci_l4l7_device
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes B(vnsAbsNode)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Service Graph Template Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ functional_template_type: adc_one_arm
+ function_type: GoTo
+ device: test-device
+ managed: false
+ routing_mode: redirect
+ state: present
+ delegate_to: localhost
+
+- name: Query a Service Graph Template Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Service Graph Template Nodes
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Service Graph Template Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ 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, aci_owner_spec
+from ansible_collections.cisco.aci.plugins.module_utils.constants import L4L7_FUNC_TYPES_MAPPING, L4L7_FUNCTIONAL_TEMPLATE_TYPES_MAPPING
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ node=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ functional_template_type=dict(type="str", aliases=["func_template_type"], choices=list(L4L7_FUNCTIONAL_TEMPLATE_TYPES_MAPPING)),
+ function_type=dict(type="str", aliases=["func_type"], choices=list(L4L7_FUNC_TYPES_MAPPING)),
+ device=dict(type="str"),
+ device_tenant=dict(type="str"),
+ managed=dict(type="bool"),
+ routing_mode=dict(type="str", choices=["redirect", "unspecified"]),
+ is_copy=dict(type="bool"),
+ description=dict(type="str"),
+ share_encap=dict(type="bool"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph", "node"]],
+ ["state", "present", ["tenant", "service_graph", "node", "device"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ node = module.params.get("node")
+ state = module.params.get("state")
+ functional_template_type = L4L7_FUNCTIONAL_TEMPLATE_TYPES_MAPPING.get(module.params.get("functional_template_type"))
+ function_type = L4L7_FUNC_TYPES_MAPPING.get(module.params.get("function_type"))
+ device = module.params.get("device")
+ device_tenant = module.params.get("device_tenant")
+ managed = aci.boolean(module.params.get("managed"))
+ routing_mode = "Redirect" if module.params.get("routing_mode") == "redirect" else module.params.get("routing_mode")
+ is_copy = aci.boolean(module.params.get("is_copy"))
+ description = module.params.get("description")
+ share_encap = aci.boolean(module.params.get("share_encap"))
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ subclass_2=dict(
+ aci_class="vnsAbsNode",
+ aci_rn="AbsNode-{0}".format(node),
+ module_object=node,
+ target_filter={"name": node},
+ ),
+ child_classes=["vnsRsNodeToLDev"],
+ )
+
+ aci.get_existing()
+ if not device_tenant:
+ device_tenant = tenant
+ dev_tdn = "uni/tn-{0}/lDevVip-{1}".format(device_tenant, device)
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsAbsNode",
+ class_config=dict(
+ name=node,
+ funcTemplateType=functional_template_type,
+ funcType=function_type,
+ managed=managed,
+ routingMode=routing_mode,
+ isCopy=is_copy,
+ descr=description,
+ shareEncap=share_encap,
+ ),
+ child_configs=[
+ dict(
+ vnsRsNodeToLDev=dict(
+ attributes=dict(tDn=dev_tdn),
+ ),
+ ),
+ ],
+ )
+ aci.get_diff(aci_class="vnsAbsNode")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_service_graph_template_term_node.py b/plugins/modules/aci_l4l7_service_graph_template_term_node.py
new file mode 100644
index 000000000..9c8b21f08
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_term_node.py
@@ -0,0 +1,293 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+# 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_l4l7_service_graph_template_term_node
+short_description: Manage L4-L7 SGT Term Nodes (vns:AbsTermNodeCon, vns:AbsTermNodeProv and vns:AbsTermConn)
+description:
+- Manage L4-L7 Service Graph Template Term Nodes on Cisco ACI fabrics.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ service_graph:
+ description:
+ - The name of an existing Service graph.
+ type: str
+ node_name:
+ description:
+ - The name of the Term Node.
+ type: str
+ choices: [ T1, T2 ]
+ 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 I(tenant) and I(service_graph) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_service_graph_template) modules can be used for this.
+seealso:
+- module: cisco.aci.aci_tenant
+- module: cisco.aci.aci_l4l7_service_graph_template
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC classes, B(vns:AbsTermNodeCon), B(vns:AbsTermNodeProv), B(vns:AbsTermConn)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+- Shreyas Srish (@shrsr)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Term Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ node_name: T1
+ state: present
+ delegate_to: localhost
+
+- name: Query a Term Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ node_name: T1
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Delete a Term Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_service_graph
+ node_name: T1
+ 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, aci_owner_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(aci_owner_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ service_graph=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ node_name=dict(type="str", choices=["T1", "T2"]),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "service_graph", "node_name"]],
+ ["state", "present", ["tenant", "service_graph", "node_name"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ service_graph = module.params.get("service_graph")
+ state = module.params.get("state")
+ node_name = module.params.get("node_name")
+
+ aci = ACIModule(module)
+
+ if node_name == "T1":
+ term_class = "vnsAbsTermNodeCon"
+ term_rn = "AbsTermNodeCon-T1"
+ term_module_object = "T1"
+ term_target_filter = {"name": "T1"}
+ name = "T1"
+ elif node_name == "T2":
+ term_class = "vnsAbsTermNodeProv"
+ term_rn = "AbsTermNodeProv-T2"
+ term_module_object = "T2"
+ term_target_filter = {"name": "T2"}
+ name = "T2"
+
+ 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="vnsAbsGraph",
+ aci_rn="AbsGraph-{0}".format(service_graph),
+ module_object=service_graph,
+ target_filter={"name": service_graph},
+ ),
+ subclass_2=dict(
+ aci_class=term_class,
+ aci_rn=term_rn,
+ module_object=term_module_object,
+ target_filter=term_target_filter,
+ ),
+ child_classes=["vnsAbsTermConn"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class=term_class,
+ class_config=dict(name=name),
+ child_configs=[
+ dict(
+ vnsAbsTermConn=dict(
+ attributes=dict(name="1"),
+ ),
+ ),
+ ],
+ )
+ aci.get_diff(aci_class=term_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/integration/targets/aci_l4l7_device_selection_interface_context/tasks/main.yml b/tests/integration/targets/aci_l4l7_device_selection_interface_context/tasks/main.yml
new file mode 100644
index 000000000..5e6e0b0fe
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_interface_context/tasks/main.yml
@@ -0,0 +1,388 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+ # GET Credentials from the inventory
+- 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") }}'
+
+- name: Query system information
+ cisco.aci.aci_system:
+ <<: *aci_info
+ id: 1
+ state: query
+ register: version
+
+# CLEAN ENVIRONMENT
+- name: Remove ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain if it already exists
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAIN
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+# CREATE CONTRACT
+- name: Create contract
+ cisco.aci.aci_contract:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ scope: application-profile
+ state: present
+
+# CREATE L4-L7 DEVICE
+- name: Create L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+
+# CREATE L4-L7 SERVICE GRAPH
+- name: Create L4-L7 Service Graph
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# CREATE L4-L7 SERVICE GRAPH NODE
+- name: Add a new Service Graph Template Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device
+ managed: false
+ routing_mode: redirect
+ state: present
+
+# CREATE L4-L7 DEVICE SELECTION POLICY
+- name: Add a new device selection policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: present
+
+# CREATE BRIDGE DOMAINS
+- name: Add a Bridge Domain in ansible_tenant
+ cisco.aci.aci_bd:
+ <<: *aci_info
+ tenant: ansible_tenant
+ bd: ansible_bd
+ state: present
+
+- name: Add a Bridge Domain in the common tenant
+ cisco.aci.aci_bd:
+ <<: *aci_info
+ tenant: common
+ bd: ansible_if_context_bd
+ state: present
+
+# CREATE LOGICAL INTERFACE
+- name: Add a new logical interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ encap: vlan-987
+ state: present
+
+# CREATE L4-L7 REDIRECT POLICY
+- name: Add a new Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ dest_type: l3
+ hash_algorithm: destination_ip
+ resilient_hash: yes
+ state: present
+
+# CREATE L4-L7 DEVICE SELECTION LOGICAL INTERFACE CONTEXT
+- name: Create L4-L7 Device Selection Logical Interface Context in check mode
+ cisco.aci.aci_l4l7_device_selection_if_context: &add_context
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ bridge_domain: ansible_bd
+ logical_device: ansible_device
+ logical_interface: ansible_log_intf
+ redirect_policy: ansible_pbr_policy
+ state: present
+ check_mode: true
+ register: add_context_cm
+
+- name: Create L4-L7 Device Selection Logical Interface Context
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *add_context
+ register: add_context
+
+- name: Sort children by tDn attribute
+ set_fact:
+ sorted_children: "{{ add_context.current[0].vnsLIfCtx.children | map('dict2items') | sort(attribute='0.key') | map('items2dict') | list }}"
+
+- name: Verify context creation
+ ansible.builtin.assert:
+ that:
+ - add_context_cm is changed
+ - add_context is changed
+ - add_context_cm.proposed.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - add_context_cm.proposed.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - add_context.current.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - add_context.current.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - sorted_children.0.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-ansible_tenant/BD-ansible_bd"
+ - sorted_children.1.vnsRsLIfCtxToLIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - sorted_children.2.vnsRsLIfCtxToSvcRedirectPol.attributes.tDn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+
+
+# CREATE L4-L7 DEVICE SELECTION LOGICAL INTERFACE CONTEXT AGAIN TO TEST IDEMPOTENCE
+- name: Create L4-L7 Device Selection Logical Interface Context again
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *add_context
+ register: add_context_again
+
+- name: Sort children by tDn attribute again
+ set_fact:
+ sorted_children_again: "{{ add_context_again.current[0].vnsLIfCtx.children | map('dict2items') | sort(attribute='0.key') | map('items2dict') | list }}"
+
+- name: Verify context creation idempotence
+ ansible.builtin.assert:
+ that:
+ - add_context_again is not changed
+ - add_context_again.current.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - add_context_again.current.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - sorted_children_again.0.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-ansible_tenant/BD-ansible_bd"
+ - sorted_children_again.1.vnsRsLIfCtxToLIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - sorted_children_again.2.vnsRsLIfCtxToSvcRedirectPol.attributes.tDn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+
+- name: Execute tasks only for ACI v6+
+ when:
+ - version.current.0.topSystem.attributes.version is version('6', '>=')
+ block:
+ - name: Create L4-L7 Device Selection Logical Interface Context with new attributes
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ bridge_domain: ansible_bd
+ logical_device: ansible_device
+ logical_interface: ansible_log_intf
+ redirect_policy: ansible_pbr_policy
+ acl: true
+ permit_handoff: true
+ rule_type: true
+ state: present
+ register: add_context_new
+
+ - name: Verify L4-L7 Device Selection Logical Interface Context with new attributes
+ ansible.builtin.assert:
+ that:
+ - add_context_new is changed
+ - add_context_new.current.0.vnsLIfCtx.attributes.acl == "yes"
+ - add_context_new.current.0.vnsLIfCtx.attributes.ruleType == "yes"
+ - add_context_new.current.0.vnsLIfCtx.attributes.permitHandoff == "yes"
+
+# UPDATE L4-L7 DEVICE SELECTION LOGICAL INTERFACE CONTEXT
+- name: Modify L4-L7 Device Selection Logical Interface Context
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ bridge_domain: ansible_if_context_bd
+ bridge_domain_tenant: common
+ state: present
+ register: update_context
+
+- name: Verify context update
+ ansible.builtin.assert:
+ that:
+ - update_context is changed
+ - update_context.current.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - update_context.current.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - update_context.current.0.vnsLIfCtx.children.0.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-common/BD-ansible_if_context_bd"
+ - update_context.current.0.vnsLIfCtx.children | length == 1
+
+- name: Remove L4-L7 Device Selection Logical Interface Context BD
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ state: present
+ register: update_context
+
+- name: Verify BD binding removal
+ ansible.builtin.assert:
+ that:
+ - update_context is changed
+ - update_context.current.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - update_context.current.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - update_context.current.0.vnsLIfCtx.children is not defined
+
+- name: Re-add L4-L7 Device Selection Logical Interface Context BD binding
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ bridge_domain: ansible_if_context_bd
+ bridge_domain_tenant: common
+ state: present
+
+# QUERY L4-L7 DEVICE SELECTION INTERFACE CONTEXT
+- name: Query L4-L7 Device Selection Logical Interface Context
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ state: query
+ register: query_context
+
+- name: Verify context query
+ ansible.builtin.assert:
+ that:
+ - query_context is not changed
+ - query_context.current.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - query_context.current.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+ - query_context.current.0.vnsLIfCtx.children.0.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-common/BD-ansible_if_context_bd"
+
+# QUERY ALL L4-L7 DEVICE SELECTION INTERFACE CONTEXTS
+- name: Query all L4-L7 Device Selection Logical Interface Contexts
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *aci_info
+ state: query
+ register: query_all_contexts
+
+- name: Verify Query All
+ ansible.builtin.assert:
+ that:
+ - query_all_contexts is not changed
+
+# DELETE L4-L7 DEVICE SELECTION INTERFACE CONTEXT
+- name: Delete L4-L7 Device Selection Logical Interface Context in check mode
+ cisco.aci.aci_l4l7_device_selection_if_context: &delete_context
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ state: absent
+ check_mode: true
+ register: delete_context_cm
+
+- name: Delete L4-L7 Device Selection Logical Interface Context
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *delete_context
+ register: delete_context
+
+- name: Verify context deletion
+ ansible.builtin.assert:
+ that:
+ - delete_context_cm is changed
+ - delete_context_cm.proposed == {}
+ - delete_context_cm.previous == delete_context.previous
+ - delete_context is changed
+ - delete_context.current == []
+ - delete_context.previous.0.vnsLIfCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node/lIfCtx-c-provider"
+ - delete_context.previous.0.vnsLIfCtx.attributes.connNameOrLbl == "provider"
+
+# DELETE L4-L7 DEVICE SELECTION INTERFACE CONTEXT AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Device Selection Logical Interface Context again
+ cisco.aci.aci_l4l7_device_selection_if_context:
+ <<: *delete_context
+ register: delete_context_again
+
+- name: Verify context deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_context_again is not changed
+ - delete_context_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
+
+- name: Remove ansible_if_context_bd
+ cisco.aci.aci_bd:
+ <<: *aci_info
+ tenant: common
+ bd: ansible_if_context_bd
+ state: absent
\ No newline at end of file
diff --git a/tests/integration/targets/aci_l4l7_device_selection_policy/tasks/main.yml b/tests/integration/targets/aci_l4l7_device_selection_policy/tasks/main.yml
new file mode 100644
index 000000000..a799b76ce
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_policy/tasks/main.yml
@@ -0,0 +1,244 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+# GET Credentials from the inventory
+- 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 ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain if it already exists
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAIN
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+# CREATE L4-L7 LOGICAL DEVICE
+- name: Create L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+
+# CREATE CONTRACT
+- name: Create contract
+ cisco.aci.aci_contract:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ scope: application-profile
+ state: present
+
+# CREATE L4-L7 SERVICE GRAPH
+- name: Create L4-L7 Service Graph
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: present
+
+# CREATE L4-L7 SERVICE GRAPH NODE
+- name: Create Service Graph Template Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ node: ansible_node
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device
+ managed: false
+ routing_mode: redirect
+ state: present
+
+# CREATE L4-L7 DEVICE SELECTION POLICY
+- name: Create L4-L7 Device Selection Policy in check mode
+ cisco.aci.aci_l4l7_device_selection_policy: &add_l4l7_device_selection_policy
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ device: ansible_device
+ state: present
+ check_mode: true
+ register: add_l4l7_device_selection_policy_cm
+
+- name: Create L4-L7 Device Selection Policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *add_l4l7_device_selection_policy
+ register: add_l4l7_device_selection_policy
+
+- name: Verify L4-L7 Device Selection Policy attributes
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_device_selection_policy_cm is changed
+ - add_l4l7_device_selection_policy is changed
+ - add_l4l7_device_selection_policy_cm.proposed.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - add_l4l7_device_selection_policy_cm.proposed.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - add_l4l7_device_selection_policy_cm.proposed.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - add_l4l7_device_selection_policy_cm.proposed.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+ - add_l4l7_device_selection_policy_cm.proposed.vnsLDevCtx.children.0.vnsRsLDevCtxToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - add_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - add_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - add_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - add_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+ - add_l4l7_device_selection_policy.current.0.vnsLDevCtx.children.0.vnsRsLDevCtxToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# ADD L4-L7 DEVICE SELECTION POLICY AGAIN TO CHECK IDEMPOTENCE
+- name: Add L4-L7 Device Selection Policy again
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *add_l4l7_device_selection_policy
+ register: add_l4l7_device_selection_policy_again
+
+- name: Verify L4-L7 Device Selection Policy attributes are unchanged
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_device_selection_policy_again is not changed
+ - add_l4l7_device_selection_policy_again.current.0.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - add_l4l7_device_selection_policy_again.current.0.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - add_l4l7_device_selection_policy_again.current.0.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - add_l4l7_device_selection_policy_again.current.0.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+ - add_l4l7_device_selection_policy_again.current.0.vnsLDevCtx.children.0.vnsRsLDevCtxToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# REMOVE LOGICAL DEVICE BINDING
+- name: Remove L4-L7 Device Selection Policy Device Binding
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: present
+ register: remove_l4l7_device_selection_policy_device_binding
+
+- name: Verify L4-L7 Device Selection Policy Device Binding removal
+ ansible.builtin.assert:
+ that:
+ - remove_l4l7_device_selection_policy_device_binding is changed
+ - remove_l4l7_device_selection_policy_device_binding.current.0.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - remove_l4l7_device_selection_policy_device_binding.current.0.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - remove_l4l7_device_selection_policy_device_binding.current.0.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - remove_l4l7_device_selection_policy_device_binding.current.0.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+ - remove_l4l7_device_selection_policy_device_binding.current.0.vnsLDevCtx.children is not defined
+
+# QUERY L4-L7 DEVICE SELECTION POLICY
+- name: Query L4-L7 Device Selection Policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: query
+ register: query_l4l7_device_selection_policy
+
+- name: Verify L4-L7 Device Selection Policy attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_device_selection_policy is not changed
+ - query_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - query_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - query_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - query_l4l7_device_selection_policy.current.0.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+
+# DELETE L4-L7 DEVICE SELECTION POLICY
+- name: Delete L4-L7 Device Selection Policy in check mode
+ cisco.aci.aci_l4l7_device_selection_policy: &delete_l4l7_device_selection_policy
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: absent
+ check_mode: true
+ register: delete_l4l7_device_selection_policy_cm
+
+- name: Delete L4-L7 Device Selection Policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *delete_l4l7_device_selection_policy
+ register: delete_l4l7_device_selection_policy
+
+- name: Verify L4-L7 Device Selection Policy removal
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_device_selection_policy_cm is changed
+ - delete_l4l7_device_selection_policy_cm.proposed == {}
+ - delete_l4l7_device_selection_policy_cm.previous == delete_l4l7_device_selection_policy.previous
+ - delete_l4l7_device_selection_policy is changed
+ - delete_l4l7_device_selection_policy.current == []
+ - delete_l4l7_device_selection_policy.previous.0.vnsLDevCtx.attributes.dn == "uni/tn-ansible_tenant/ldevCtx-c-ansible_contract-g-ansible_graph-n-ansible_node"
+ - delete_l4l7_device_selection_policy.previous.0.vnsLDevCtx.attributes.ctrctNameOrLbl == "ansible_contract"
+ - delete_l4l7_device_selection_policy.previous.0.vnsLDevCtx.attributes.graphNameOrLbl == "ansible_graph"
+ - delete_l4l7_device_selection_policy.previous.0.vnsLDevCtx.attributes.nodeNameOrLbl == "ansible_node"
+
+# DELETE L4-L7 DEVICE SELECTION POLICY AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Device Selection Policy
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *delete_l4l7_device_selection_policy
+ register: delete_l4l7_device_selection_policy_again
+
+- name: Verify L4-L7 Device Selection Policy removal
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_device_selection_policy_again is not changed
+ - delete_l4l7_device_selection_policy_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template/tasks/main.yml
new file mode 100644
index 000000000..827e21022
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template/tasks/main.yml
@@ -0,0 +1,189 @@
+# Test code for the ACI modules
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+
+# 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
+
+# GET Credentials from the inventory
+- 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") }}'
+
+- name: Query system information
+ cisco.aci.aci_system:
+ <<: *aci_info
+ id: 1
+ state: query
+ register: version
+
+# CLEAN ENVIRONMENT
+- name: Remove ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# ADD service graph template
+- name: Create L4-L7 Service Graph Template in check mode
+ cisco.aci.aci_l4l7_service_graph_template: &sgt_add
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ ui_template_type: one_node_fw_routed
+ state: present
+ check_mode: true
+ register: create_sgt_cm
+
+- name: Create L4-L7 Service Graph Template again
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *sgt_add
+ register: create_sgt
+
+- name: Verify L4-L7 Service Graph Template has been created
+ ansible.builtin.assert:
+ that:
+ - create_sgt_cm is changed
+ - create_sgt is changed
+ - create_sgt_cm.proposed.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph"
+ - create_sgt_cm.proposed.vnsAbsGraph.attributes.name == "ansible_service_graph"
+ - create_sgt_cm.proposed.vnsAbsGraph.attributes.uiTemplateType == "ONE_NODE_FW_ROUTED"
+ - create_sgt.previous == []
+ - create_sgt.current.0.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph"
+ - create_sgt.current.0.vnsAbsGraph.attributes.name == "ansible_service_graph"
+ - create_sgt.current.0.vnsAbsGraph.attributes.uiTemplateType == "ONE_NODE_FW_ROUTED"
+
+# ADD service graph template again to check idempotence
+- name: Create L4-L7 Service Graph Template again
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *sgt_add
+ register: create_sgt_again
+
+- name: Verify L4-L7 Service Graph Template attributes
+ ansible.builtin.assert:
+ that:
+ - create_sgt_again is not changed
+ - create_sgt.current == create_sgt_again.previous
+ - create_sgt_again.current.0.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph"
+ - create_sgt_again.current.0.vnsAbsGraph.attributes.name == "ansible_service_graph"
+
+- name: Execute tasks only for ACI v5+
+ when:
+ - version.current.0.topSystem.attributes.version is version('5', '>=')
+ block:
+ - name: Create L4-L7 Service Graph Template with new attributes
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph_new
+ service_rule_type: epg
+ state: present
+ register: create_sgt_new
+
+ - name: Verify L4-L7 Service Graph Template attributes
+ ansible.builtin.assert:
+ that:
+ - create_sgt_new is changed
+ - create_sgt_new.current.0.vnsAbsGraph.attributes.svcRuleType == "epg"
+ - create_sgt_new.current.0.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph_new"
+ - create_sgt_new.current.0.vnsAbsGraph.attributes.name == "ansible_service_graph_new"
+
+# QUERY service graph template
+- name: Create another L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph_another
+ state: present
+
+- name: Query L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: query
+ register: query_sgt
+
+- name: Verify L4-L7 Service Graph Template attributes
+ ansible.builtin.assert:
+ that:
+ - query_sgt is not changed
+ - query_sgt.current.0.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph"
+ - query_sgt.current.0.vnsAbsGraph.attributes.name == "ansible_service_graph"
+
+- name: Query all L4-L7 Service Graph Templates
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ state: query
+ register: query_sgt_all
+
+- name: Verify L4-L7 Service Graph Template attributes
+ ansible.builtin.assert:
+ that:
+ - query_sgt_all is not changed
+ - query_sgt_all.current | length >= 2
+ - "'uni/tn-ansible_tenant/AbsGraph-ansible_service_graph' in query_sgt_all.current | map(attribute='vnsAbsGraph.attributes.dn') | list"
+ - "'uni/tn-ansible_tenant/AbsGraph-ansible_service_graph_another' in query_sgt_all.current | map(attribute='vnsAbsGraph.attributes.dn') | list"
+
+# DELETE service graph template
+- name: Remove L4-L7 Service Graph Template in check mode
+ cisco.aci.aci_l4l7_service_graph_template: &remove_sgt
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: absent
+ check_mode: true
+ register: remove_sgt_cm
+
+- name: Remove L4-L7 Service Graph Template in check mode
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *remove_sgt
+ register: remove_sgt
+
+- name: Verify L4-L7 Service Graph Template deletion
+ ansible.builtin.assert:
+ that:
+ - remove_sgt_cm is changed
+ - remove_sgt_cm.proposed == {}
+ - remove_sgt_cm.previous == remove_sgt.previous
+ - remove_sgt is changed
+ - remove_sgt.current == []
+ - remove_sgt.previous.0.vnsAbsGraph.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_service_graph"
+ - remove_sgt.previous.0.vnsAbsGraph.attributes.name == "ansible_service_graph"
+
+# DELETE SERVICE GRAPH TEMPLATE AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *remove_sgt
+ register: remove_sgt_again
+
+- name: Verify L4-L7 Service Graph Template deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - remove_sgt_again is not changed
+ - remove_sgt_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection/tasks/main.yml
new file mode 100644
index 000000000..3d0c88b82
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection/tasks/main.yml
@@ -0,0 +1,218 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+# GET Credentials from the inventory
+- 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 ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# ADD SERVICE GRAPH TEMPLATE
+- name: Create L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# ADD SERVICE GRAPH ABS CONNECTION
+- name: Create L4-L7 Service Graph Abs Connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn: &add_l4l7_abs_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ connector_direction: provider
+ connection_type: external
+ direct_connect: true
+ unicast_route: true
+ adjacency_type: l3
+ state: present
+ check_mode: true
+ register: add_l4l7_abs_conn_cm
+
+- name: Create L4-L7 Service Graph Abs Connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *add_l4l7_abs_conn
+ register: add_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION
+- name: Verify Connection Attributes
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_abs_conn_cm is changed
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.name == "C1"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.adjType == "L3"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.connDir == "provider"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.connType == "external"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.directConnect == "yes"
+ - add_l4l7_abs_conn_cm.proposed.vnsAbsConnection.attributes.unicastRoute == "yes"
+ - add_l4l7_abs_conn is changed
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.name == "C1"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.adjType == "L3"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connDir == "provider"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connType == "external"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.directConnect == "yes"
+ - add_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.unicastRoute == "yes"
+
+# ADD SERVICE GRAPH ABS CONNECTION AGAIN TO TEST IDEMPOTENCE
+- name: Create L4-L7 Service Graph Abs Connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *add_l4l7_abs_conn
+ register: add_l4l7_abs_conn_again
+
+# VERIFY SERVICE GRAPH ABS CONNECTION ARE UNCHANGED
+- name: Verify Connection Attributes are unchanged
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_abs_conn_again is not changed
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.name == "C1"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.adjType == "L3"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.connDir == "provider"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.connType == "external"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.directConnect == "yes"
+ - add_l4l7_abs_conn_again.current.0.vnsAbsConnection.attributes.unicastRoute == "yes"
+
+# MODIFY SERVICE GRAPH ABS CONNECTION
+- name: Modify L4-L7 Service Graph Abs Connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ direct_connect: false
+ unicast_route: false
+ adjacency_type: l2
+ state: present
+ register: update_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION
+- name: Verify Connection Attributes
+ ansible.builtin.assert:
+ that:
+ - update_l4l7_abs_conn is changed
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.name == "C1"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.adjType == "L2"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connDir == "provider"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connType == "external"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.directConnect == "no"
+ - update_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.unicastRoute == "no"
+
+# QUERY SERVICE GRAPH ABS CONNECTION
+- name: Query L4-L7 Service Graph Abs Connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ state: query
+ register: query_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION
+- name: Verify Connection Attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_abs_conn is not changed
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.name == "C1"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.adjType == "L2"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connDir == "provider"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.connType == "external"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.directConnect == "no"
+ - query_l4l7_abs_conn.current.0.vnsAbsConnection.attributes.unicastRoute == "no"
+
+# QUERY ALL SERVICE GRAPH ABS CONNECTIONS
+- name: Query all L4-L7 Service Graph Abs Connections
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ state: query
+ register: query_l4l7_abs_conn_all
+
+# VERIFY SERVICE GRAPH ABS CONNECTION
+- name: Verify Connection Attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_abs_conn_all is not changed
+
+# DELETE SERVICE GRAPH ABS CONNECTION
+- name: Delete L4-L7 Service Graph Abs Connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn: &delete_l4l7_abs_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ state: absent
+ check_mode: true
+ register: delete_l4l7_abs_conn_cm
+
+- name: Delete L4-L7 Service Graph Abs Connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *delete_l4l7_abs_conn
+ register: delete_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION DELETION
+- name: Verify Connection Deletion
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_abs_conn_cm is changed
+ - delete_l4l7_abs_conn_cm.proposed == {}
+ - delete_l4l7_abs_conn_cm.previous == delete_l4l7_abs_conn.previous
+ - delete_l4l7_abs_conn is changed
+ - delete_l4l7_abs_conn.current == []
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.name == "C1"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.adjType == "L2"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.connDir == "provider"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.connType == "external"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.directConnect == "no"
+ - delete_l4l7_abs_conn.previous.0.vnsAbsConnection.attributes.unicastRoute == "no"
+
+# DELETE ABS CONN AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Service Graph Abs Connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *delete_l4l7_abs_conn
+ register: delete_l4l7_abs_conn_again
+
+- name: Verify Connection Deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_abs_conn_again is not changed
+ - delete_l4l7_abs_conn_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/tasks/main.yml
new file mode 100644
index 000000000..f38d9aa64
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/tasks/main.yml
@@ -0,0 +1,747 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+# GET Credentials from the inventory
+- 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 ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain if it already exists
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAIN
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+# ADD SERVICE GRAPH TEMPLATE
+- name: Create L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# ADD L4-L7 LOGICAL DEVICES
+- name: Create PBR Device 1
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device1
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: false
+ state: present
+
+- name: Create PBR Device 2
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device2
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: false
+ state: present
+
+# ADD SERVICE GRAPH NODES
+- name: Add Service Graph Template Node 1
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node1
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device1
+ managed: false
+ routing_mode: redirect
+ state: present
+
+- name: Add Service Graph Template Node 2
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node2
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device2
+ managed: false
+ routing_mode: redirect
+ state: present
+
+# ADD SERVICE GRAPH ABS CONNECTIONS
+- name: Create L4-L7 Service Graph Abs Connection C1
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ direct_connect: true
+ unicast_route: true
+ adjacency_type: l3
+ state: present
+
+- name: Create L4-L7 Service Graph Abs Connection C2
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C2
+ direct_connect: true
+ unicast_route: true
+ adjacency_type: l3
+ state: present
+
+- name: Create L4-L7 Service Graph Abs Connection C3
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C3
+ direct_connect: true
+ unicast_route: true
+ adjacency_type: l3
+ state: present
+
+# CREATE CONNECTION CONNS
+- name: Add C1 node connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c1_node_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ connected_node: ansible_node1
+ state: present
+ check_mode: true
+ register: add_c1_node_conn_cm
+
+- name: Add C1 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c1_node_conn
+ register: add_c1_node_conn
+
+- name: Add C1 term connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c1_term_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ state: present
+ check_mode: true
+ register: add_c1_term_conn_cm
+
+- name: Add C1 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c1_term_conn
+ register: add_c1_term_conn
+
+- name: Add C2 provider connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c2_prov_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C2
+ connected_node: ansible_node1
+ state: present
+ check_mode: true
+ register: add_c2_prov_conn_cm
+
+- name: Add C2 provider connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c2_prov_conn
+ register: add_c2_prov_conn
+
+- name: Add C2 consumer connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c2_cons_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C2
+ connected_node: ansible_node2
+ state: present
+ check_mode: true
+ register: add_c2_cons_conn_cm
+
+- name: Add C2 consumer connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c2_cons_conn
+ register: add_c2_cons_conn
+
+- name: Add C3 node connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c3_node_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ connected_node: ansible_node2
+ state: present
+ check_mode: true
+ register: add_c3_node_conn_cm
+
+- name: Add C3 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c3_node_conn
+ register: add_c3_node_conn
+
+- name: Add C3 term connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &add_c3_term_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ state: present
+ check_mode: true
+ register: add_c3_term_conn_cm
+
+- name: Add C3 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c3_term_conn
+ register: add_c3_term_conn
+
+# VERIFY CONNECTION ATTRIBUTES
+- name: Verify C1 Node Connection
+ ansible.builtin.assert:
+ that:
+ - add_c1_node_conn_cm is changed
+ - add_c1_node_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer]"
+ - add_c1_node_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer"
+ - add_c1_node_conn is changed
+ - add_c1_node_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer]"
+ - add_c1_node_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer"
+
+- name: Verify C1 Term Node Connection
+ ansible.builtin.assert:
+ that:
+ - add_c1_term_conn_cm is changed
+ - add_c1_term_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn]"
+ - add_c1_term_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn"
+ - add_c1_term_conn is changed
+ - add_c1_term_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn]"
+ - add_c1_term_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn"
+
+- name: Verify C2 Provider Connection
+ ansible.builtin.assert:
+ that:
+ - add_c2_prov_conn_cm is changed
+ - add_c2_prov_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider]"
+ - add_c2_prov_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider"
+ - add_c2_prov_conn is changed
+ - add_c2_prov_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider]"
+ - add_c2_prov_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider"
+
+- name: Verify C2 Consumer Connection
+ ansible.builtin.assert:
+ that:
+ - add_c2_cons_conn_cm is changed
+ - add_c2_cons_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer]"
+ - add_c2_cons_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer"
+ - add_c2_cons_conn is changed
+ - add_c2_cons_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer]"
+ - add_c2_cons_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer"
+
+- name: Verify C3 Node Connection
+ ansible.builtin.assert:
+ that:
+ - add_c3_node_conn_cm is changed
+ - add_c3_node_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider]"
+ - add_c3_node_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider"
+ - add_c3_node_conn is changed
+ - add_c3_node_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider]"
+ - add_c3_node_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider"
+
+- name: Verify C3 Term Node Connection
+ ansible.builtin.assert:
+ that:
+ - add_c3_term_conn_cm is changed
+ - add_c3_term_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn]"
+ - add_c3_term_conn_cm.proposed.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn"
+ - add_c3_term_conn is changed
+ - add_c3_term_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn]"
+ - add_c3_term_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn"
+
+# ADD CONNECTIONS AGAIN TO TEST IDEMPOTENCE
+- name: Add C1 node connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c1_node_conn
+ register: add_c1_node_conn_again
+
+- name: Add C1 term connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c1_term_conn
+ register: add_c1_term_conn_again
+
+- name: Add C2 provider connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c2_prov_conn
+ register: add_c2_prov_conn_again
+
+- name: Add C2 consumer connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c2_cons_conn
+ register: add_c2_cons_conn_again
+
+- name: Add C3 node connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c3_node_conn
+ register: add_c3_node_conn_again
+
+- name: Add C3 term connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *add_c3_term_conn
+ register: add_c3_term_conn_again
+
+# VERIFY CONNECTION ATTRIBUTES
+- name: Verify C1 Node Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c1_node_conn_again is not changed
+ - add_c1_node_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer]"
+ - add_c1_node_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer"
+
+- name: Verify C1 Term Node Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c1_term_conn_again is not changed
+ - add_c1_term_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn]"
+ - add_c1_term_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn"
+
+- name: Verify C2 Provider Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c2_prov_conn_again is not changed
+ - add_c2_prov_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider]"
+ - add_c2_prov_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider"
+
+- name: Verify C2 Consumer Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c2_cons_conn_again is not changed
+ - add_c2_cons_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer]"
+ - add_c2_cons_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer"
+
+- name: Verify C3 Node Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c3_node_conn_again is not changed
+ - add_c3_node_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider]"
+ - add_c3_node_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider"
+
+- name: Verify C3 Term Node Connection is unchanged
+ ansible.builtin.assert:
+ that:
+ - add_c3_term_conn_again is not changed
+ - add_c3_term_conn_again.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn]"
+ - add_c3_term_conn_again.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn"
+
+# QUERY CONNECTION ATTRIBUTES
+- name: Query C1 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ connected_node: ansible_node1
+ state: query
+ register: query_c1_node_conn
+
+- name: Query C1 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ state: query
+ register: query_c1_term_conn
+
+- name: Query C2 provider connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C2
+ connected_node: ansible_node1
+ state: query
+ register: query_c2_prov_conn
+
+- name: Query C2 consumer connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C2
+ connected_node: ansible_node2
+ state: query
+ register: query_c2_cons_conn
+
+- name: Query C3 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ connected_node: ansible_node2
+ state: query
+ register: query_c3_node_conn
+
+- name: Query C3 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ state: query
+ register: query_c3_term_conn
+
+- name: Verify C1 Node Connection
+ ansible.builtin.assert:
+ that:
+ - query_c1_node_conn is not changed
+ - query_c1_node_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer]"
+ - query_c1_node_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer"
+
+- name: Verify C1 Term Node Connection
+ ansible.builtin.assert:
+ that:
+ - query_c1_term_conn is not changed
+ - query_c1_term_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn]"
+ - query_c1_term_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn"
+
+- name: Verify C2 Provider Connection
+ ansible.builtin.assert:
+ that:
+ - query_c2_prov_conn is not changed
+ - query_c2_prov_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider]"
+ - query_c2_prov_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider"
+
+- name: Verify C2 Consumer Connection
+ ansible.builtin.assert:
+ that:
+ - query_c2_cons_conn is not changed
+ - query_c2_cons_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer]"
+ - query_c2_cons_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer"
+
+- name: Verify C3 Node Connection
+ ansible.builtin.assert:
+ that:
+ - query_c3_node_conn is not changed
+ - query_c3_node_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider]"
+ - query_c3_node_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider"
+
+- name: Verify C3 Term Node Connection
+ ansible.builtin.assert:
+ that:
+ - query_c3_term_conn is not changed
+ - query_c3_term_conn.current.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn]"
+ - query_c3_term_conn.current.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn"
+
+# DELETE CONNECTIONS
+# Delete C1 Node Connection
+- name: Delete C1 node connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c1_node_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ connected_node: ansible_node1
+ state: absent
+ check_mode: true
+ register: delete_c1_node_conn_cm
+
+- name: Delete C1 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c1_node_conn
+ register: delete_c1_node_conn
+
+# Delete C1 Term Connection
+- name: Delete C1 term connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c1_term_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ state: absent
+ check_mode: true
+ register: delete_c1_term_conn_cm
+
+- name: Delete C1 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c1_term_conn
+ register: delete_c1_term_conn
+
+# Delete C2 Provider Connection
+- name: Delete C2 provider connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c2_prov_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C2
+ connected_node: ansible_node1
+ state: absent
+ check_mode: true
+ register: delete_c2_prov_conn_cm
+
+- name: Delete C2 provider connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c2_prov_conn
+ register: delete_c2_prov_conn
+
+# Delete C2 Consumer Connection
+- name: Delete C2 consumer connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c2_cons_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C2
+ connected_node: ansible_node2
+ state: absent
+ check_mode: true
+ register: delete_c2_cons_conn_cm
+
+- name: Delete C2 consumer connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c2_cons_conn
+ register: delete_c2_cons_conn
+
+# Delete C3 Node Connection
+- name: Delete C3 node connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c3_node_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ connected_node: ansible_node2
+ state: absent
+ check_mode: true
+ register: delete_c3_node_conn_cm
+
+- name: Delete C3 node connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c3_node_conn
+ register: delete_c3_node_conn
+
+# Delete C3 Term Connection
+- name: Delete C3 term connection in check mode
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns: &delete_c3_term_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: provider
+ connection_name: C3
+ state: absent
+ check_mode: true
+ register: delete_c3_term_conn_cm
+
+- name: Delete C3 term connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c3_term_conn
+ register: delete_c3_term_conn
+
+- name: Verify C1 Node Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c1_node_conn_cm is changed
+ - delete_c1_node_conn_cm.proposed == {}
+ - delete_c1_node_conn_cm.previous == delete_c1_node_conn.previous
+ - delete_c1_node_conn is changed
+ - delete_c1_node_conn.current == []
+ - delete_c1_node_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer]"
+ - delete_c1_node_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-consumer"
+
+- name: Verify C1 Term Node Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c1_term_conn_cm is changed
+ - delete_c1_term_conn_cm.proposed == {}
+ - delete_c1_term_conn_cm.previous == delete_c1_term_conn.previous
+ - delete_c1_term_conn is changed
+ - delete_c1_term_conn.current == []
+ - delete_c1_term_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C1/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn]"
+ - delete_c1_term_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1/AbsTConn"
+
+- name: Verify C2 Provider Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c2_prov_conn_cm is changed
+ - delete_c2_prov_conn_cm.proposed == {}
+ - delete_c2_prov_conn_cm.previous == delete_c2_prov_conn.previous
+ - delete_c2_prov_conn is changed
+ - delete_c2_prov_conn.current == []
+ - delete_c2_prov_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider]"
+ - delete_c2_prov_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node1/AbsFConn-provider"
+
+- name: Verify C2 Consumer Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c2_cons_conn_cm is changed
+ - delete_c2_cons_conn_cm.proposed == {}
+ - delete_c2_cons_conn_cm.previous == delete_c2_cons_conn.previous
+ - delete_c2_cons_conn is changed
+ - delete_c2_cons_conn.current == []
+ - delete_c2_cons_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C2/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer]"
+ - delete_c2_cons_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-consumer"
+
+- name: Verify C3 Node Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c3_node_conn_cm is changed
+ - delete_c3_node_conn_cm.proposed == {}
+ - delete_c3_node_conn_cm.previous == delete_c3_node_conn.previous
+ - delete_c3_node_conn is changed
+ - delete_c3_node_conn.current == []
+ - delete_c3_node_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider]"
+ - delete_c3_node_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node2/AbsFConn-provider"
+
+- name: Verify C3 Term Node Connection Removal
+ ansible.builtin.assert:
+ that:
+ - delete_c3_term_conn_cm is changed
+ - delete_c3_term_conn_cm.proposed == {}
+ - delete_c3_term_conn_cm.previous == delete_c3_term_conn.previous
+ - delete_c3_term_conn is changed
+ - delete_c3_term_conn.current == []
+ - delete_c3_term_conn.previous.0.vnsRsAbsConnectionConns.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsConnection-C3/rsabsConnectionConns-[uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn]"
+ - delete_c3_term_conn.previous.0.vnsRsAbsConnectionConns.attributes.tDn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2/AbsTConn"
+
+# REMOVE CONNECTIONS AGAIN TO TEST IDEMPOTENCE
+- name: Delete C1 node connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c1_node_conn
+ register: delete_c1_node_conn_again
+
+- name: Delete C1 term connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c1_term_conn
+ register: delete_c1_term_conn_again
+
+- name: Delete C2 provider connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c2_prov_conn
+ register: delete_c2_prov_conn_again
+
+- name: Delete C2 consumer connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c2_cons_conn
+ register: delete_c2_cons_conn_again
+
+- name: Delete C3 node connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c3_node_conn
+ register: delete_c3_node_conn_again
+
+- name: Delete C3 term connection again
+ cisco.aci.aci_l4l7_service_graph_template_abs_connection_conns:
+ <<: *delete_c3_term_conn
+ register: delete_c3_term_conn_again
+
+- name: Verify C1 Node Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c1_node_conn_again is not changed
+ - delete_c1_node_conn_again.current == []
+
+- name: Verify C1 Term Node Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c1_term_conn_again is not changed
+ - delete_c1_term_conn_again.current == []
+
+- name: Verify C2 Provider Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c2_prov_conn_again is not changed
+ - delete_c2_prov_conn_again.current == []
+
+- name: Verify C2 Consumer Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c2_cons_conn_again is not changed
+ - delete_c2_cons_conn_again.current == []
+
+- name: Verify C3 Node Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c3_node_conn_again is not changed
+ - delete_c3_node_conn_again.current == []
+
+- name: Verify C3 Term Node Connection Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_c3_term_conn_again is not changed
+ - delete_c3_term_conn_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_functional_connection/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_functional_connection/tasks/main.yml
new file mode 100644
index 000000000..be4d046d3
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_functional_connection/tasks/main.yml
@@ -0,0 +1,319 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+# GET Credentials from the inventory
+- 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") }}'
+
+- name: Query system information
+ cisco.aci.aci_system:
+ <<: *aci_info
+ id: 1
+ state: query
+ register: version
+
+# CLEAN ENVIRONMENT
+- name: Remove ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAIN
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+# ADD DEVICE
+- name: Create L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+
+# ADD SERVICE GRAPH TEMPLATE
+- name: Create L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# ADD SERVICE GRAPH NODE
+- name: Create L4-L7 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device
+ managed: false
+ routing_mode: redirect
+ state: present
+
+# ADD FUNCTIONAL CONNECTIONS
+- name: Create Consumer Func Conn in check mode
+ cisco.aci.aci_l4l7_service_graph_template_func_conn: &add_cons_func_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: consumer
+ state: present
+ check_mode: true
+ register: add_cons_func_conn_cm
+
+- name: Create Provider Func Conn in check mode
+ cisco.aci.aci_l4l7_service_graph_template_func_conn: &add_prov_func_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: provider
+ state: present
+ check_mode: true
+ register: add_prov_func_conn_cm
+
+- name: Create Consumer Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *add_cons_func_conn
+ register: add_cons_func_conn
+
+- name: Create Provider Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *add_prov_func_conn
+ register: add_prov_func_conn
+
+# VERIFY ATTRIBUTES
+- name: Verify Consumer Func Conn Creation
+ ansible.builtin.assert:
+ that:
+ - add_cons_func_conn_cm is changed
+ - add_cons_func_conn is changed
+ - add_cons_func_conn_cm.proposed.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-consumer"
+ - add_cons_func_conn_cm.proposed.vnsAbsFuncConn.attributes.name == "consumer"
+ - add_cons_func_conn.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-consumer"
+ - add_cons_func_conn.current.0.vnsAbsFuncConn.attributes.name == "consumer"
+
+- name: Verify Provider Func Conn Creation
+ ansible.builtin.assert:
+ that:
+ - add_prov_func_conn_cm is changed
+ - add_prov_func_conn_cm.proposed.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-provider"
+ - add_prov_func_conn_cm.proposed.vnsAbsFuncConn.attributes.name == "provider"
+ - add_prov_func_conn is changed
+ - add_prov_func_conn.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-provider"
+ - add_prov_func_conn.current.0.vnsAbsFuncConn.attributes.name == "provider"
+
+# ADD FUNC CONNS AGAIN TO TEST IDEMPOTENCE
+- name: Add Consumer Func Conn again
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *add_cons_func_conn
+ register: add_cons_func_conn_again
+
+- name: Add Provider Func Conn again
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *add_prov_func_conn
+ register: add_prov_func_conn_again
+
+- name: Verify Consumer Func Conn is not changed
+ ansible.builtin.assert:
+ that:
+ - add_cons_func_conn_again is not changed
+ - add_cons_func_conn_again.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-consumer"
+ - add_cons_func_conn_again.current.0.vnsAbsFuncConn.attributes.name == "consumer"
+
+- name: Verify Provider Func Conn is not changed
+ ansible.builtin.assert:
+ that:
+ - add_prov_func_conn_again is not changed
+ - add_prov_func_conn_again.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-provider"
+ - add_prov_func_conn_again.current.0.vnsAbsFuncConn.attributes.name == "provider"
+
+- name: Execute tasks only for ACI v5+
+ when:
+ - version.current.0.topSystem.attributes.version is version('5', '>=')
+ block:
+ - name: Modify Provider Func Conn with new attributes
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: provider
+ connection_type: redir
+ state: present
+ register: add_prov_func_conn_new
+
+ - name: Verify L4-L7 Device Selection Logical Interface Context with new attributes
+ ansible.builtin.assert:
+ that:
+ - add_prov_func_conn_new is changed
+ - add_prov_func_conn_new.current.0.vnsAbsFuncConn.attributes.connType == "redir"
+
+# QUERY FUNC CONNS
+- name: Query Consumer Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: consumer
+ state: query
+ register: query_cons_func_conn
+
+- name: Query Provider Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: provider
+ state: query
+ register: query_prov_func_conn
+
+# VERIFY ATTRIBUTES
+- name: Verify Consumer Func Conn Attributes
+ ansible.builtin.assert:
+ that:
+ - query_cons_func_conn is not changed
+ - query_cons_func_conn.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-consumer"
+ - query_cons_func_conn.current.0.vnsAbsFuncConn.attributes.name == "consumer"
+
+- name: Verify Provider Func Conn Attributes
+ ansible.builtin.assert:
+ that:
+ - query_prov_func_conn is not changed
+ - query_prov_func_conn.current.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-provider"
+ - query_prov_func_conn.current.0.vnsAbsFuncConn.attributes.name == "provider"
+
+# QUERY ALL FUNC CONNS
+- name: Query All Func Conns
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *aci_info
+ state: query
+ register: query_func_conn_all
+
+- name: Verify query all
+ ansible.builtin.assert:
+ that:
+ - query_func_conn_all is not changed
+ - query_func_conn_all | length >=2
+
+# DELETE FUNC CONNS
+- name: Delete Consumer Func Conn in check mode
+ cisco.aci.aci_l4l7_service_graph_template_func_conn: &delete_cons_func_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: consumer
+ state: absent
+ check_mode: true
+ register: delete_cons_func_conn_cm
+
+- name: Delete Provider Func Conn in check mode
+ cisco.aci.aci_l4l7_service_graph_template_func_conn: &delete_prov_func_conn
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: provider
+ state: absent
+ check_mode: true
+ register: delete_prov_func_conn_cm
+
+- name: Delete Consumer Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *delete_cons_func_conn
+ register: delete_cons_func_conn
+
+- name: Delete Provider Func Conn
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *delete_prov_func_conn
+ register: delete_prov_func_conn
+
+# VERIFY ATTRIBUTES
+- name: Verify Consumer Func Conn Removal
+ ansible.builtin.assert:
+ that:
+ - delete_cons_func_conn_cm is changed
+ - delete_cons_func_conn_cm.proposed == {}
+ - delete_cons_func_conn_cm.previous == delete_cons_func_conn.previous
+ - delete_cons_func_conn is changed
+ - delete_cons_func_conn.current == []
+ - delete_cons_func_conn.previous.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-consumer"
+ - delete_cons_func_conn.previous.0.vnsAbsFuncConn.attributes.name == "consumer"
+
+- name: Verify Provider Func Conn Removal
+ ansible.builtin.assert:
+ that:
+ - delete_prov_func_conn_cm is changed
+ - delete_prov_func_conn_cm.proposed == {}
+ - delete_prov_func_conn_cm.previous == delete_prov_func_conn.previous
+ - delete_prov_func_conn is changed
+ - delete_prov_func_conn.current == []
+ - delete_prov_func_conn.previous.0.vnsAbsFuncConn.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node/AbsFConn-provider"
+ - delete_prov_func_conn.previous.0.vnsAbsFuncConn.attributes.name == "provider"
+
+# DELETE FUNC CONN AGAIN TO TEST IDEMPOTENCE
+- name: Delete Consumer Func Conn again
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ <<: *delete_cons_func_conn
+ register: delete_cons_func_conn_again
+
+- name: Verify Consumer Func Conn Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_cons_func_conn_again is not changed
+ - delete_cons_func_conn_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_dom
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_node/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_node/tasks/main.yml
new file mode 100644
index 000000000..f93ac77b7
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_node/tasks/main.yml
@@ -0,0 +1,267 @@
+# Test code for the ACI modules
+# Copyright: (c) 2021, Tim Cragg (@timcragg)
+
+# 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
+
+ # GET Credentials from the inventory
+- 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 ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAIN
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+# ADD DEVICE
+- name: Create L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ domain: ansible_phys_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: physical
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+
+# ADD SERVICE GRAPH TEMPLATE
+- name: Create L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# ADD SERVICE GRAPH NODE
+- name: Create L4-L7 Service Graph Node in check mode
+ cisco.aci.aci_l4l7_service_graph_template_node: &l4l7node
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ func_template_type: adc_one_arm
+ func_type: go_to
+ device: ansible_device
+ managed: false
+ routing_mode: redirect
+ share_encap: true
+ state: present
+ check_mode: true
+ register: add_l4l7_node_cm
+
+- name: Create L4-L7 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *l4l7node
+ register: add_l4l7_node
+
+# VERIFY NODE CREATION
+- name: Verify Node has been created correctly
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_node_cm is changed
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.name == "ansible_node"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.funcType == "GoTo"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.funcTemplateType == "ADC_ONE_ARM"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.managed == "no"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.routingMode == "Redirect"
+ - add_l4l7_node_cm.proposed.vnsAbsNode.attributes.shareEncap == "yes"
+ - add_l4l7_node is changed
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.name == "ansible_node"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.funcType == "GoTo"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.funcTemplateType == "ADC_ONE_ARM"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.managed == "no"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.routingMode == "Redirect"
+ - add_l4l7_node.current.0.vnsAbsNode.attributes.shareEncap == "yes"
+
+# VERIFY NODE BINDING TO LOGICAL DEVICE
+- name: Verify Node Binding to Logical Device
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_node.current.0.vnsAbsNode.children.0.vnsRsNodeToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# ADD NODE AGAIN TO TEST IDEMPOTENCE
+- name: Create L4-L7 Service Graph Node again
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *l4l7node
+ register: add_l4l7_node_again
+
+# VERIFY NODE IS NOT MODIFIED
+- name: Verify Node has not changed
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_node_again is not changed
+ - add_l4l7_node_again.previous == add_l4l7_node_again.current
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.name == "ansible_node"
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.funcType == "GoTo"
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.funcTemplateType == "ADC_ONE_ARM"
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.managed == "no"
+ - add_l4l7_node_again.current.0.vnsAbsNode.attributes.routingMode == "Redirect"
+
+# VERIFY NODE BINDING TO LOGICAL DEVICE
+- name: Verify Node Binding to Logical Device is not changed
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_node_again.current.0.vnsAbsNode.children.0.vnsRsNodeToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# MODIFY L4-L7 NODE
+- name: Update L4-L7 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ func_template_type: adc_two_arm
+ func_type: go_through
+ device: ansible_device
+ managed: true
+ routing_mode: redirect
+ state: present
+ register: update_l4l7_node
+
+# VERIFY NODE ATTRIBUTES
+- name: Verify Node has not changed
+ ansible.builtin.assert:
+ that:
+ - update_l4l7_node is changed
+ - update_l4l7_node.previous == add_l4l7_node_again.current
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.name == "ansible_node"
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.funcType == "GoThrough"
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.funcTemplateType == "ADC_TWO_ARM"
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.managed == "yes"
+ - update_l4l7_node.current.0.vnsAbsNode.attributes.routingMode == "Redirect"
+
+# VERIFY NODE BINDING TO LOGICAL DEVICE
+- name: Verify Node Binding to Logical Device
+ ansible.builtin.assert:
+ that:
+ - update_l4l7_node.current.0.vnsAbsNode.children.0.vnsRsNodeToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# QUERY L4-L7 NODE
+- name: Query L4-L7 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ state: query
+ register: query_l4l7_node
+
+# VERIFY NODE ATTRIBUTES
+- name: Verify Node has not changed
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_node is not changed
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.name == "ansible_node"
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.funcType == "GoThrough"
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.funcTemplateType == "ADC_TWO_ARM"
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.managed == "yes"
+ - query_l4l7_node.current.0.vnsAbsNode.attributes.routingMode == "Redirect"
+
+# VERIFY NODE BINDING TO LOGICAL DEVICE
+- name: Verify Node Binding to Logical Device
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_node is not changed
+ - query_l4l7_node.current.0.vnsAbsNode.children.0.vnsRsNodeToLDev.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+
+# DELETE L4-L7 NODE
+- name: Remove L4-L7 Service Graph Node in check mode
+ cisco.aci.aci_l4l7_service_graph_template_node: &removel4l7
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ state: absent
+ check_mode: true
+ register: delete_l4l7_node_cm
+
+- name: Remove L4-L7 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *removel4l7
+ register: delete_l4l7_node
+
+# VERIFY NODE REMOVAL
+- name: Verify Node removal
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_node_cm is changed
+ - delete_l4l7_node_cm.proposed == {}
+ - delete_l4l7_node_cm.previous == delete_l4l7_node.previous
+ - delete_l4l7_node is changed
+ - delete_l4l7_node.current == []
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsNode-ansible_node"
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.name == "ansible_node"
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.funcType == "GoThrough"
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.funcTemplateType == "ADC_TWO_ARM"
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.managed == "yes"
+ - delete_l4l7_node.previous.0.vnsAbsNode.attributes.routingMode == "Redirect"
+
+# DELETE L4-L7 NODE AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 Service Graph Node again
+ cisco.aci.aci_l4l7_service_graph_template_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ state: absent
+ register: delete_l4l7_node_again
+
+# VERIFY NODE REMOVAL IDEMPOTENCE
+- name: Verify Node removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_node_again is not changed
+ - delete_l4l7_node_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+- name: Remove ansible_phys_dom
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: absent
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_term_node/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_term_node/tasks/main.yml
new file mode 100644
index 000000000..460a48209
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_term_node/tasks/main.yml
@@ -0,0 +1,233 @@
+# Test code for the ACI modules
+# Copyright: (c) 2025, Tim Cragg (@timcragg)
+# Copyright: (c) 2025, Shreyas Srish (@shrsr)
+
+# 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
+
+# GET Credentials from the inventory
+- 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 ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# ADD SERVICE GRAPH TEMPLATE
+- name: Create L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ state: present
+
+# ADD SERVICE GRAPH TERM NODE
+- name: Create L4-L7 T1 Service Graph Node in check mode
+ cisco.aci.aci_l4l7_service_graph_template_term_node: &add_l4l7_term_node
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T1
+ state: present
+ check_mode: true
+ register: add_l4l7_term_node_cm
+
+- name: Create L4-L7 T1 Service Graph Node in check mode
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *add_l4l7_term_node
+ register: add_l4l7_term_node
+
+# VERIFY TERM NODE CREATION
+- name: Verify Term Node Creation
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_term_node_cm is changed
+ - add_l4l7_term_node is changed
+ - add_l4l7_term_node_cm.proposed.vnsAbsTermNodeCon.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1"
+ - add_l4l7_term_node_cm.proposed.vnsAbsTermNodeCon.attributes.name == "T1"
+ - add_l4l7_term_node.current.0.vnsAbsTermNodeCon.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1"
+ - add_l4l7_term_node.current.0.vnsAbsTermNodeCon.attributes.name == "T1"
+
+# ADD SERVICE GRAPH TERM NODE AGAIN TO CHECK IDEMPOTENCE
+- name: Create L4-L7 T1 Service Graph Node again
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *add_l4l7_term_node
+ register: add_l4l7_term_node_again
+
+# VERIFY TERM NODE UNCHANGED
+- name: Verify Term Node is not changed
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_term_node_again is not changed
+ - add_l4l7_term_node_again.current.0.vnsAbsTermNodeCon.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1"
+ - add_l4l7_term_node_again.current.0.vnsAbsTermNodeCon.attributes.name == "T1"
+
+# QUERY TERM NODE
+- name: Query L4-L7 T1 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T1
+ state: query
+ register: query_l4l7_term_node
+
+# VERIFY TERM NODE QUERY
+- name: Verify Term Node attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_term_node is not changed
+ - query_l4l7_term_node.current.0.vnsAbsTermNodeCon.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1"
+ - query_l4l7_term_node.current.0.vnsAbsTermNodeCon.attributes.name == "T1"
+
+# DELETE TERM NODE T1
+- name: Remove L4-L7 T1 Service Graph Node in check mode
+ cisco.aci.aci_l4l7_service_graph_template_term_node: &remove_l4l7_term_node
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T1
+ state: absent
+ check_mode: true
+ register: delete_l4l7_term_node_cm
+
+- name: Remove L4-L7 T1 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *remove_l4l7_term_node
+ register: delete_l4l7_term_node
+
+- name: Remove L4-L7 T1 Service Graph Node again
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *remove_l4l7_term_node
+ register: delete_l4l7_term_node_again
+
+# VERIFY DELETION
+- name: Verify Term Node attributes
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_term_node_cm is changed
+ - delete_l4l7_term_node is changed
+ - delete_l4l7_term_node.current == []
+ - delete_l4l7_term_node_cm.previous == delete_l4l7_term_node.previous
+ - delete_l4l7_term_node.previous.0.vnsAbsTermNodeCon.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeCon-T1"
+ - delete_l4l7_term_node.previous.0.vnsAbsTermNodeCon.attributes.name == "T1"
+ - delete_l4l7_term_node_again is not changed
+ - delete_l4l7_term_node_again.current == []
+
+# ADD SERVICE GRAPH TERM NODE
+- name: Create L4-L7 T2 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T2
+ state: present
+ register: add_l4l7_term_node2
+
+# VERIFY TERM NODE CREATION
+- name: Verify Term Node Creation
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_term_node2.current.0.vnsAbsTermNodeProv.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2"
+ - add_l4l7_term_node2.current.0.vnsAbsTermNodeProv.attributes.name == "T2"
+
+# ADD SERVICE GRAPH TERM NODE AGAIN TO CHECK IDEMPOTENCE
+- name: Create L4-L7 T2 Service Graph Node again
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T2
+ state: present
+ register: add_l4l7_term_node2_again
+
+# VERIFY TERM NODE UNCHANGED
+- name: Verify Term Node is not changed
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_term_node2_again is not changed
+ - add_l4l7_term_node2_again.current.0.vnsAbsTermNodeProv.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2"
+ - add_l4l7_term_node2_again.current.0.vnsAbsTermNodeProv.attributes.name == "T2"
+
+# QUERY TERM NODE
+- name: Query L4-L7 T2 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T2
+ state: query
+ register: query_l4l7_term_node2
+
+# VERIFY TERM NODE QUERY
+- name: Verify Term Node attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_term_node2 is not changed
+ - query_l4l7_term_node2.current.0.vnsAbsTermNodeProv.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2"
+ - query_l4l7_term_node2.current.0.vnsAbsTermNodeProv.attributes.name == "T2"
+
+# DELETE TERM NODE T2
+- name: Remove L4-L7 T2 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T2
+ state: absent
+ register: delete_l4l7_term_node2
+
+# VERIFY DELETION
+- name: Verify Term Node T2 deletion
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_term_node2 is changed
+ - delete_l4l7_term_node2.current == []
+ - delete_l4l7_term_node2.previous.0.vnsAbsTermNodeProv.attributes.dn == "uni/tn-ansible_tenant/AbsGraph-ansible_graph/AbsTermNodeProv-T2"
+ - delete_l4l7_term_node2.previous.0.vnsAbsTermNodeProv.attributes.name == "T2"
+
+# DELETE TERM NODE T2 AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 T2 Service Graph Node
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T2
+ state: absent
+ register: delete_l4l7_term_node2_again
+
+# VERIFY DELETION IDEMPOTENCE
+- name: Verify Term Node T2 deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_term_node2_again is not changed
+ - delete_l4l7_term_node2_again.current == []
+
+# CLEAN UP
+- name: Remove ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: absent