diff --git a/plugins/module_utils/aci.py b/plugins/module_utils/aci.py
index 73f34b271..d567089c6 100644
--- a/plugins/module_utils/aci.py
+++ b/plugins/module_utils/aci.py
@@ -1732,3 +1732,19 @@ def api_call(self, method, url, data=None, return_response=False):
except KeyError:
# Connection error
self.fail_json(msg="Connection failed for {url}. {msg}".format_map(info))
+
+ def delete_config_request(self, path):
+ self._config_request(path, "absent")
+ self.result["changed"] = True
+
+ def get_config_request(self, path):
+ self._config_request(path, "query")
+ return self.imdata
+
+ def _config_request(self, path, state):
+ reset_url = self.url
+ reset_state = self.params["state"]
+ self.params["state"] = state
+ self.request(path)
+ self.url = reset_url
+ self.params["state"] = reset_state
diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py
index ee94fc5e3..6ca9ff3b8 100644
--- a/plugins/module_utils/constants.py
+++ b/plugins/module_utils/constants.py
@@ -432,3 +432,13 @@
"yellow",
"yellow_green",
]
+
+NODE_TYPE_MAPPING = {"tier_2": "tier-2-leaf", "remote": "remote-leaf-wan", "virtual": "virtual", "unspecified": "unspecified"}
+
+SPAN_DIRECTION_MAP = {"incoming": "in", "outgoing": "out", "both": "both"}
+
+HTTP_VERSIONS_MAPPING = {"1.0": "HTTP10", "1.1": "HTTP11"}
+
+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"}
diff --git a/plugins/modules/aci_ip_sla_monitoring_policy.py b/plugins/modules/aci_ip_sla_monitoring_policy.py
new file mode 100644
index 000000000..764235f46
--- /dev/null
+++ b/plugins/modules/aci_ip_sla_monitoring_policy.py
@@ -0,0 +1,379 @@
+#!/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_ip_sla_monitoring_policy
+short_description: Manage IP SLA Monitoring Policies (fv:IPSLAMonitoringPol)
+description:
+- Manage IP SLA Monitoring Policies used for L4-L7 Policy Based Redirection
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ name:
+ description:
+ - The SLA Policy name.
+ type: str
+ aliases: [ sla_policy ]
+ sla_type:
+ description:
+ - The type of monitoring.
+ - The APIC defaults to C(icmp) when unset during creation.
+ type: str
+ choices: [ icmp, tcp, l2ping, http ]
+ sla_port:
+ description:
+ - The Port to monitor for TCP SLAs.
+ type: int
+ frequency:
+ description:
+ - How often to probe.
+ - The APIC defaults to C(60) when unset during creation.
+ type: int
+ multiplier:
+ description:
+ - How many probes must fail for the SLA to be down.
+ - The APIC defaults to C(3) when unset during creation.
+ type: int
+ request_data_size:
+ description:
+ - The number of bytes to send in the request.
+ - Only used if I(sla_type) is set to C(http).
+ - The APIC defaults to C(28) when unset during creation.
+ type: int
+ type_of_service:
+ description:
+ - The Type of Service (ToS) value to set in the IPv4 header.
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ aliases: [ tos ]
+ operation_timeout:
+ description:
+ - The amount of time in milliseconds that the IP SLA operation waits for a response from its request packet.
+ - The APIC defaults to C(900) when unset during creation.
+ type: int
+ threshold:
+ description:
+ - The upper threshold value in milliseconds for calculating network monitoring statistics created by the IP SLA operation.
+ - The value specified for this property must not exceed the value specified for operation_timeout.
+ - The APIC defaults to C(900) when unset during creation.
+ type: int
+ traffic_class:
+ description:
+ - The Traffic Class value to set in the IPv6 header.
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ http_version:
+ description:
+ - The HTTP version to use.
+ - The APIC defaults to C(1.0) when unset during creation.
+ type: str
+ choices: [ "1.0", "1.1" ]
+ http_uri:
+ description:
+ - The HTTP URI to use as the SLA destination.
+ - The APIC defaults to C(/) when unset during creation.
+ 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) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(fv:IPSLAMonitoringPol)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new ICMP SLA monitoring policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_policy
+ sla_type: icmp
+ frequency: 40
+ multiplier: 6
+ state: present
+ delegate_to: localhost
+
+- name: Add a new TCP SLA monitoring policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_policy
+ sla_type: tcp
+ sla_port: 2345
+ frequency: 45
+ multiplier: 5
+ state: present
+ delegate_to: localhost
+
+- name: Delete an SLA monitoring policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query an SLA monitoring policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all SLA monitoring policies
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible_collections.cisco.aci.plugins.module_utils.constants import HTTP_VERSIONS_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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name=dict(type="str", aliases=["sla_policy"]),
+ sla_type=dict(type="str", choices=["icmp", "tcp", "l2ping", "http"]),
+ sla_port=dict(type="int"),
+ frequency=dict(type="int"),
+ multiplier=dict(type="int"),
+ request_data_size=dict(type="int"),
+ type_of_service=dict(type="int", aliases=["tos"]),
+ operation_timeout=dict(type="int"),
+ threshold=dict(type="int"),
+ traffic_class=dict(type="int"),
+ http_version=dict(type="str", choices=["1.0", "1.1"]),
+ http_uri=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "name"]],
+ ["state", "present", ["tenant", "name"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ name = module.params.get("name")
+ sla_type = module.params.get("sla_type")
+ sla_port = module.params.get("sla_port")
+ frequency = module.params.get("frequency")
+ multiplier = module.params.get("multiplier")
+ request_data_size = module.params.get("request_data_size")
+ type_of_service = module.params.get("type_of_service")
+ operation_timeout = module.params.get("operation_timeout")
+ threshold = module.params.get("threshold")
+ traffic_class = module.params.get("traffic_class")
+ http_version = HTTP_VERSIONS_MAPPING.get(module.params.get("http_version"))
+ http_uri = module.params.get("http_uri")
+
+ 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="fvIPSLAMonitoringPol", aci_rn="ipslaMonitoringPol-{0}".format(name), module_object=name, target_filter={"name": name}),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ if sla_port is not None and sla_type != "tcp":
+ aci.fail_json("Setting 'sla_port' is not allowed when 'sla_type' is not set to 'tcp'.")
+ if sla_type != "http" and request_data_size is not None:
+ aci.fail_json("Setting 'request_data_size' is not allowed when 'sla_type' is not set to 'http.")
+
+ if sla_type == "http":
+ sla_port = 80
+ elif sla_type != "tcp":
+ sla_port = 0
+
+ aci.payload(
+ aci_class="fvIPSLAMonitoringPol",
+ class_config=dict(
+ name=name,
+ slaType=sla_type,
+ slaPort=sla_port,
+ slaFrequency=frequency,
+ slaDetectMultiplier=multiplier,
+ reqDataSize=request_data_size,
+ ipv4Tos=type_of_service,
+ timeout=operation_timeout,
+ threshold=threshold,
+ ipv6TrfClass=traffic_class,
+ httpVersion=http_version,
+ httpUri=http_uri,
+ ),
+ )
+ aci.get_diff(aci_class="fvIPSLAMonitoringPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_concrete_device.py b/plugins/modules/aci_l4l7_concrete_device.py
new file mode 100644
index 000000000..10c0fa208
--- /dev/null
+++ b/plugins/modules/aci_l4l7_concrete_device.py
@@ -0,0 +1,295 @@
+#!/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_concrete_device
+short_description: Manage L4-L7 Concrete Devices (vns:CDev)
+description:
+- Manage L4-L7 Concrete Devices.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ device:
+ description:
+ - The name of the logical device (vns:lDevVip) the concrete device is attached to.
+ - The logical device can be configured using the M(cisco.aci.aci_l4l7_device) module.
+ type: str
+ aliases: [ device_name, logical_device_name ]
+ name:
+ description:
+ - The name of the concrete device.
+ type: str
+ aliases: [ concrete_device, concrete_device_name ]
+ vcenter_name:
+ description:
+ - The virtual center name on which the device is hosted in the L4-L7 device cluster.
+ type: str
+ vm_name:
+ description:
+ - The virtual center VM name on which the device is hosted in the L4-L7 device cluster.
+ 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) and I(device) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_device) modules can be used for this.
+seealso:
+- module: aci_l4l7_device
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:CDev)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new concrete device
+ cisco.aci.aci_l4l7_concrete_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ state: present
+ delegate_to: localhost
+
+- name: Delete a concrete device
+ cisco.aci.aci_l4l7_concrete_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ state: absent
+ delegate_to: localhost
+
+- name: Query a concrete device
+ cisco.aci.aci_l4l7_concrete_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all concrete devices
+ cisco.aci.aci_l4l7_concrete_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ device=dict(type="str", aliases=["device_name", "logical_device_name"]),
+ name=dict(type="str", aliases=["concrete_device", "concrete_device_name"]),
+ vcenter_name=dict(type="str"),
+ vm_name=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", "device", "name"]],
+ ["state", "present", ["tenant", "device", "name"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ device = module.params.get("device")
+ name = module.params.get("name")
+ vcenter_name = module.params.get("vcenter_name")
+ vm_name = module.params.get("vm_name")
+
+ 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="vnsLDevVip",
+ aci_rn="lDevVip-{0}".format(device),
+ module_object=device,
+ target_filter={"name": device},
+ ),
+ subclass_2=dict(
+ aci_class="vnsCDev",
+ aci_rn="cDev-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsCDev",
+ class_config=dict(
+ name=name,
+ vcenterName=vcenter_name,
+ vmName=vm_name,
+ ),
+ )
+ aci.get_diff(aci_class="vnsCDev")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_concrete_interface.py b/plugins/modules/aci_l4l7_concrete_interface.py
new file mode 100644
index 000000000..557b48743
--- /dev/null
+++ b/plugins/modules/aci_l4l7_concrete_interface.py
@@ -0,0 +1,352 @@
+#!/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_concrete_interface
+short_description: Manage L4-L7 Concrete Interfaces (vns:CIf)
+description:
+- Manage L4-L7 Concrete Interfaces.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ device:
+ description:
+ - The name of an existing logical device.
+ type: str
+ concrete_device:
+ description:
+ - The name of an existing concrete device.
+ type: str
+ name:
+ description:
+ - The name of the concrete interface.
+ type: str
+ aliases: [ concrete_interface ]
+ pod_id:
+ description:
+ - The pod ID the concrete interface exists on.
+ type: int
+ node_id:
+ description:
+ - The node ID the concrete interface exists on.
+ - For Ports and Port-channels this is a single node-id.
+ - For vPCs this is a hyphen separated pair of node-ids, e.g. "201-202".
+ type: str
+ path_ep:
+ description:
+ - The path to the physical interface.
+ - For single ports, this is the port name, e.g. "eth1/15".
+ - For Port-channels and vPCs, this is the Interface Policy Group name.
+ type: str
+ vnic_name:
+ description:
+ - The concrete interface vNIC name.
+ 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
+
+seealso:
+- module: aci_l4l7_device
+- module: aci_l4l7_concrete_device
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:CIf)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new concrete interface on a single port
+ cisco.aci.aci_l4l7_concrete_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ name: my_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: present
+ delegate_to: localhost
+
+- name: Add a new concrete interface on a vPC
+ cisco.aci.aci_l4l7_concrete_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ name: my_concrete_interface
+ pod_id: 1
+ node_id: 201-202
+ path_ep: my_vpc_ipg
+ state: present
+ delegate_to: localhost
+
+- name: Delete a concrete interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ name: my_concrete_interface
+ state: absent
+ delegate_to: localhost
+
+- name: Query a concrete interface
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ concrete_device: my_concrete_device
+ name: my_concrete_interface
+ pod_id: 1
+ node_id: 201-202
+ path_ep: my_vpc_ipg
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all concrete interfaces
+ cisco.aci.aci_l4l7_service_graph_template:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+import re
+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"]),
+ device=dict(type="str"),
+ concrete_device=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ name=dict(type="str", aliases=["concrete_interface"]),
+ pod_id=dict(type="int"),
+ node_id=dict(type="str"),
+ path_ep=dict(type="str"),
+ vnic_name=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "device", "concrete_device", "name"]],
+ ["state", "present", ["tenant", "device", "concrete_device", "name", "pod_id", "node_id", "path_ep"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ device = module.params.get("device")
+ concrete_device = module.params.get("concrete_device")
+ name = module.params.get("name")
+ pod_id = module.params.get("pod_id")
+ node_id = module.params.get("node_id")
+ path_ep = module.params.get("path_ep")
+ vnic_name = module.params.get("vnic_name")
+
+ 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="vnsLDevVip",
+ aci_rn="lDevVip-{0}".format(device),
+ module_object=device,
+ target_filter={"name": device},
+ ),
+ subclass_2=dict(
+ aci_class="vnsCDev",
+ aci_rn="cDev-{0}".format(concrete_device),
+ module_object=concrete_device,
+ target_filter={"name": concrete_device},
+ ),
+ subclass_3=dict(
+ aci_class="vnsCIf",
+ aci_rn="cIf-[{0}]".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["vnsRsCIfPathAtt"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ path_dn = "topology/pod-{0}/{1}-{2}/pathep-[{3}]".format(pod_id, "protpaths" if "-" in node_id else "paths", node_id, path_ep)
+ aci.payload(
+ aci_class="vnsCIf",
+ class_config=dict(
+ name=name,
+ vnicName=vnic_name,
+ ),
+ child_configs=[
+ dict(
+ vnsRsCIfPathAtt=dict(
+ attributes=dict(tDn=path_dn),
+ ),
+ ),
+ ],
+ )
+ aci.get_diff(aci_class="vnsCIf")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_concrete_interface_attach.py b/plugins/modules/aci_l4l7_concrete_interface_attach.py
new file mode 100644
index 000000000..fa9694fcc
--- /dev/null
+++ b/plugins/modules/aci_l4l7_concrete_interface_attach.py
@@ -0,0 +1,302 @@
+#!/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_concrete_interface_attach
+short_description: Manage L4-L7 Concrete Interface Attach (vns:RsCIfAttN)
+description:
+- Manage L4-L7 Concrete Interface Attachment to Logical Interfaces.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ device:
+ description:
+ - The name of an existing Logical Device.
+ type: str
+ logical_interface:
+ description:
+ - The name of an existing Logical Interface.
+ type: str
+ concrete_device:
+ description:
+ - The name of an existing Concrete Device.
+ type: str
+ concrete_interface:
+ description:
+ - The name of an existing Concrete Interface.
+ 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
+
+seealso:
+- module: aci_l4l7_logical_interface
+- module: aci_l4l7_concrete_device
+- module: aci_l4l7_concrete_interface
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:RsCIfAttN)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_log_device
+ logical_interface: my_log_intf
+ concrete_device: my_conc_device
+ concrete_interface: my_conc_intf
+ state: present
+ delegate_to: localhost
+
+- name: Delete a concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_log_device
+ logical_interface: my_log_intf
+ concrete_device: my_conc_device
+ concrete_interface: my_conc_intf
+ state: absent
+ delegate_to: localhost
+
+- name: Query a concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_log_device
+ logical_interface: my_log_intf
+ concrete_device: my_conc_device
+ concrete_interface: my_conc_intf
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all concrete interface attachments
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ device=dict(type="str"),
+ logical_interface=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ concrete_device=dict(type="str"),
+ concrete_interface=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "device", "logical_interface", "concrete_device", "concrete_interface"]],
+ ["state", "present", ["tenant", "device", "logical_interface", "concrete_device", "concrete_interface"]],
+ ],
+ required_together=[
+ ["tenant", "device", "concrete_device", "concrete_interface"],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ device = module.params.get("device")
+ logical_interface = module.params.get("logical_interface")
+ concrete_device = module.params.get("concrete_device")
+ concrete_interface = module.params.get("concrete_interface")
+
+ aci = ACIModule(module)
+
+ tdn = "uni/tn-{0}/lDevVip-{1}/cDev-{2}/cIf-[{3}]".format(tenant, device, concrete_device, concrete_interface) if concrete_interface is not 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="vnsLDevVip",
+ aci_rn="lDevVip-{0}".format(device),
+ module_object=device,
+ target_filter={"name": device},
+ ),
+ subclass_2=dict(
+ aci_class="vnsLIf",
+ aci_rn="lIf-{0}".format(logical_interface),
+ module_object=logical_interface,
+ target_filter={"name": logical_interface},
+ ),
+ subclass_3=dict(
+ aci_class="vnsRsCIfAttN",
+ aci_rn="rscIfAttN-[{0}]".format(tdn),
+ module_object=tdn,
+ target_filter={"tDn": tdn},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsRsCIfAttN",
+ class_config=dict(tDn=tdn),
+ )
+ aci.get_diff(aci_class="vnsRsCIfAttN")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_device.py b/plugins/modules/aci_l4l7_device.py
new file mode 100644
index 000000000..abdf4d1c8
--- /dev/null
+++ b/plugins/modules/aci_l4l7_device.py
@@ -0,0 +1,351 @@
+#!/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_device
+short_description: Manage L4-L7 Devices (vns:LDevVip)
+description:
+- Manage L4-L7 Devices.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ name:
+ description:
+ - The name of the L4-L7 device.
+ type: str
+ aliases: [ device, device_name, logical_device_name ]
+ context_aware:
+ description:
+ - Is device Single or Multi context aware.
+ - The APIC defaults to C(single) when unset during creation.
+ type: str
+ choices: [ multi, single ]
+ dev_type:
+ description:
+ - The type of the device.
+ - The APIC defaults to C(physical) when unset during creation.
+ type: str
+ choices: [ physical, virtual ]
+ func_type:
+ description:
+ - The function type of the device.
+ - The APIC defaults to C(go_to) when unset during creation.
+ type: str
+ choices: [ go_to, go_through, l1, l2 ]
+ managed:
+ description:
+ - Is the device a managed device.
+ - The APIC defaults to C(true) when unset during creation.
+ type: bool
+ prom_mode:
+ description:
+ - Enable promiscuous mode.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ svc_type:
+ description:
+ - The service type running on the device.
+ - The APIC defaults to C(others) when unset during creation.
+ type: str
+ choices: [ adc, fw, others ]
+ trunking:
+ description:
+ - Enable trunking.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ domain:
+ description:
+ - The domain to bind to the device.
+ - The type of domain is controlled by the dev_type setting.
+ 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) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) modules can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:LDevVip)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new L4-L7 device
+ cisco.aci.aci_l4l7_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_device
+ state: present
+ domain: phys
+ func_type: go_to
+ context_aware: single
+ managed: no
+ dev_type: physical
+ svc_type: adc
+ trunking: no
+ prom_mode: yes
+ delegate_to: localhost
+
+- name: Delete an existing L4-L7 device
+ cisco.aci.aci_l4l7_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_device
+ state: absent
+ delegate_to: localhost
+
+- name: Query an L4-L7 device
+ cisco.aci.aci_l4l7_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ name: my_device
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all L4-L7 devices
+ cisco.aci.aci_l4l7_device:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+from ansible_collections.cisco.aci.plugins.module_utils.constants import L4L7_FUNC_TYPES_MAPPING
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ name=dict(type="str", aliases=["device", "device_name", "logical_device_name"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ context_aware=dict(type="str", choices=["single", "multi"]),
+ dev_type=dict(type="str", choices=["physical", "virtual"]),
+ func_type=dict(type="str", choices=["go_to", "go_through", "l1", "l2"]),
+ managed=dict(type="bool"),
+ prom_mode=dict(type="bool"),
+ svc_type=dict(type="str", choices=["adc", "fw", "others"]),
+ trunking=dict(type="bool"),
+ domain=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "name"]],
+ ["state", "present", ["tenant", "name", "dev_type", "domain"]],
+ ],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ name = module.params.get("name")
+ context_aware = module.params.get("context_aware")
+ dev_type = module.params.get("dev_type")
+ func_type = L4L7_FUNC_TYPES_MAPPING.get(module.params.get("func_type"))
+ managed = aci.boolean(module.params.get("managed"))
+ prom_mode = aci.boolean(module.params.get("prom_mode"))
+ svc_type = module.params.get("svc_type")
+ trunking = aci.boolean(module.params.get("trunking"))
+ domain = module.params.get("domain")
+
+ 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="vnsLDevVip",
+ aci_rn="lDevVip-{0}".format(name),
+ module_object=name,
+ target_filter={"name": name},
+ ),
+ child_classes=["vnsRsALDevToPhysDomP", "vnsCDev", "vnsRsALDevToDomP"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if dev_type == "virtual":
+ dom_class = "vnsRsALDevToDomP"
+ tdn = "uni/vmmp-VMware/dom-{0}".format(domain)
+ else:
+ dom_class = "vnsRsALDevToPhysDomP"
+ tdn = "uni/phys-{0}".format(domain)
+ child_configs.append({dom_class: {"attributes": {"tDn": tdn}}})
+
+ aci.payload(
+ aci_class="vnsLDevVip",
+ class_config=dict(
+ name=name,
+ contextAware="{0}-Context".format(context_aware),
+ devtype=dev_type.upper(),
+ funcType=func_type,
+ managed=managed,
+ promMode=prom_mode,
+ svcType=svc_type.upper(),
+ trunking=trunking,
+ ),
+ child_configs=child_configs,
+ )
+
+ aci.get_diff(aci_class="vnsLDevVip")
+
+ 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_if_context.py b/plugins/modules/aci_l4l7_device_selection_if_context.py
new file mode 100644
index 000000000..308533568
--- /dev/null
+++ b/plugins/modules/aci_l4l7_device_selection_if_context.py
@@ -0,0 +1,408 @@
+#!/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_device_selection_if_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_dest:
+ description:
+ - Whether the context is a Layer3 destination.
+ - The APIC defaults to C(true) when unset during creation.
+ type: bool
+ 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
+ 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: aci_l3out
+- module: aci_l3out_logical_node_profile
+- 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)
+"""
+
+EXAMPLES = r"""
+- name: Add a new interface context
+ cisco.aci.aci_l4l7_device_selection_if_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: Delete an interface context
+ cisco.aci.aci_l4l7_device_selection_if_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
+
+- name: Query an interface context
+ cisco.aci.aci_l4l7_device_selection_if_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_if_context:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+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_dest=dict(type="bool"),
+ 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"),
+ )
+ 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_dest = aci.boolean(module.params.get("l3_dest"))
+ 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")
+
+ 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 is not None:
+ 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 is not None:
+ 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 is not None:
+ 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.delete_config_request(
+ "/api/mo/uni/tn-{0}/ldevCtx-c-{1}-g-{2}-n-{3}/lIfCtx-c-{4}/rsLIfCtxToBD.json".format(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_dest, permitLog=permit_log),
+ 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..cfe087cbc
--- /dev/null
+++ b/plugins/modules/aci_l4l7_device_selection_policy.py
@@ -0,0 +1,314 @@
+#!/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_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
+ 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) 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) and M(cisco.aci.aci_l4l7_device) modules can be used for this.
+
+seealso:
+- 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)
+"""
+
+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: 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
+
+- 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
+"""
+
+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"),
+ 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")
+
+ policy_dn = "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=policy_dn,
+ module_object=policy_dn,
+ target_filter={"dn": policy_dn},
+ ),
+ child_classes=["vnsRsLDevCtxToLDev"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if device is not None:
+ 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),
+ 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_logical_interface.py b/plugins/modules/aci_l4l7_logical_interface.py
new file mode 100644
index 000000000..d641d5153
--- /dev/null
+++ b/plugins/modules/aci_l4l7_logical_interface.py
@@ -0,0 +1,283 @@
+#!/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_logical_interface
+short_description: Manage L4-L7 Logical Interface (vns:LIf)
+description:
+- Manage L4-L7 Logical Interfaces.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ device:
+ description:
+ - The name of an existing Logical Device.
+ type: str
+ logical_interface:
+ description:
+ - The name of an existing Logical Interface.
+ type: str
+ encap:
+ description:
+ - The encapsulation of the Logical Interface.
+ 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) and I(device) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_device) modules can be used for this.
+seealso:
+- module: aci_l4l7_device
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:LIf)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new logical interface
+ cisco.aci.aci_l4l7_logical_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ logical_interface: my_log_intf
+ encap: vlan-987
+ state: present
+ delegate_to: localhost
+
+- name: Delete a logical interface
+ cisco.aci.aci_l4l7_logical_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ logical_interface: my_log_intf
+ state: absent
+ delegate_to: localhost
+
+- name: Query a logical interface
+ cisco.aci.aci_l4l7_logical_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ device: my_device
+ logical_interface: my_log_intf
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all logical interfaces
+ cisco.aci.aci_l4l7_logical_interface:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ device=dict(type="str"),
+ logical_interface=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ encap=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "device", "logical_interface"]],
+ ["state", "present", ["tenant", "device", "logical_interface"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ device = module.params.get("device")
+ logical_interface = module.params.get("logical_interface")
+ encap = module.params.get("encap")
+
+ 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="vnsLDevVip",
+ aci_rn="lDevVip-{0}".format(device),
+ module_object=device,
+ target_filter={"name": device},
+ ),
+ subclass_2=dict(
+ aci_class="vnsLIf",
+ aci_rn="lIf-{0}".format(logical_interface),
+ module_object=logical_interface,
+ target_filter={"name": logical_interface},
+ ),
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsLIf",
+ class_config=dict(name=logical_interface, encap=encap),
+ )
+ aci.get_diff(aci_class="vnsLIf")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_policy_based_redirect.py b/plugins/modules/aci_l4l7_policy_based_redirect.py
new file mode 100644
index 000000000..ce5c2c0ea
--- /dev/null
+++ b/plugins/modules/aci_l4l7_policy_based_redirect.py
@@ -0,0 +1,387 @@
+#!/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_policy_based_redirect
+short_description: Manage L4-L7 Policy Based Redirection (vns:SvcRedirectPol)
+description:
+- Manage L4-L7 Policy Based Redirection
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ policy_name:
+ description:
+ - The name of the Policy Based Redirection Policy.
+ type: str
+ description:
+ description:
+ - The description of the Policy Based Redirection Policy.
+ type: str
+ dest_type:
+ description:
+ - The destination type.
+ - The APIC defaults to C(l3) when unset during creation.
+ type: str
+ choices: [ l1, l2, l3 ]
+ hash_algorithm:
+ description:
+ - The hashing algorithm.
+ - The APIC defaults to C(ip_and_protocol) when unset during creation.
+ type: str
+ choices: [ source_ip, destination_ip, ip_and_protocol ]
+ threshold_enable:
+ description:
+ - Whether to enable the threshold for the policy.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ max_threshold:
+ description:
+ - The maximum percent when threshold is enabled.
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ min_threshold:
+ description:
+ - The minimum percent when threshold is enabled.
+ - The APIC defaults to C(0) when unset during creation.
+ type: int
+ threshold_down_action:
+ description:
+ - The action to take when threshold is breached.
+ - The APIC defaults to C(permit) when unset during creation.
+ type: str
+ choices: [ deny, permit ]
+ resilient_hash:
+ description:
+ - Whether to enable resilient hashing.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ pod_aware:
+ description:
+ - Whether to enable Pod ID aware redirection.
+ - The APIC defaults to C(false) when unset during creation.
+ type: bool
+ anycast_enabled:
+ description:
+ - Whether to enable anycast services.
+ - The APIC defaults to C(false) when unset during creation.
+ - Only available when I(dest_type=l3)
+ type: bool
+ monitor_policy:
+ description:
+ - The name of the IP SLA Monitoring Policy to bind to the L4-L7 Redirect Policy.
+ - To remove an existing binding to an IP SLA Monitoring Policy, submit a request with I(state=present) and no I(monitor_policy) value.
+ type: str
+ aliases: [ sla, sla_policy ]
+ rewrite_source_mac:
+ description:
+ - Whether to rewrite the source MAC address of forwarded traffic.
+ - 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) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) module can be used for this.
+seealso:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class, B(vns:SvcRedirectPol)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy_name: my_pbr_policy
+ dest_type: l3
+ hash_algorithm: destination_ip
+ resilient_hash: yes
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy_name: my_pbr_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query a Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy_name: my_pbr_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Policy Based Redirects
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+from ansible_collections.cisco.aci.plugins.module_utils.constants import L4L7_HASH_ALGORITHMS_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"]),
+ policy_name=dict(type="str"),
+ description=dict(type="str"),
+ dest_type=dict(type="str", choices=["l1", "l2", "l3"]),
+ hash_algorithm=dict(type="str", choices=["source_ip", "destination_ip", "ip_and_protocol"]),
+ threshold_enable=dict(type="bool"),
+ max_threshold=dict(type="int"),
+ min_threshold=dict(type="int"),
+ threshold_down_action=dict(type="str", choices=["permit", "deny"]),
+ resilient_hash=dict(type="bool"),
+ pod_aware=dict(type="bool"),
+ anycast_enabled=dict(type="bool"),
+ monitor_policy=dict(type="str", aliases=["sla", "sla_policy"]),
+ rewrite_source_mac=dict(type="bool"),
+ 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", "policy_name"]],
+ ["state", "present", ["tenant", "policy_name"]],
+ ],
+ )
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ policy_name = module.params.get("policy_name")
+ description = module.params.get("description")
+ dest_type = module.params.get("dest_type").upper() if module.params.get("dest_type") is not None else None
+ hash_algorithm = L4L7_HASH_ALGORITHMS_MAPPING.get(module.params.get("hash_algorithm"))
+ threshold_enable = aci.boolean(module.params.get("threshold_enable"))
+ max_threshold = module.params.get("max_threshold")
+ min_threshold = module.params.get("min_threshold")
+ threshold_down_action = module.params.get("threshold_down_action")
+ resilient_hash = aci.boolean(module.params.get("resilient_hash"))
+ pod_aware = aci.boolean(module.params.get("pod_aware"))
+ anycast_enabled = aci.boolean(module.params.get("anycast_enabled"))
+ monitor_policy = module.params.get("monitor_policy")
+ rewrite_source_mac = aci.boolean(module.params.get("rewrite_source_mac"))
+
+ 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="vnsSvcRedirectPol",
+ aci_rn="svcCont/svcRedirectPol-{0}".format(policy_name),
+ module_object=policy_name,
+ target_filter={"name": policy_name},
+ ),
+ child_classes=["vnsRsIPSLAMonitoringPol"],
+ )
+
+ aci.get_existing()
+
+ if state == "present":
+ child_configs = []
+ if monitor_policy is not None:
+ monitor_tdn = "uni/tn-{0}/ipslaMonitoringPol-{1}".format(tenant, monitor_policy)
+ child_configs.append({"vnsRsIPSLAMonitoringPol": {"attributes": {"tDn": monitor_tdn}}})
+ else:
+ monitor_tdn = None
+ if isinstance(aci.existing, list) and len(aci.existing) > 0:
+ for child in aci.existing[0].get("vnsSvcRedirectPol", {}).get("children", {}):
+ if child.get("vnsRsIPSLAMonitoringPol") and child.get("vnsRsIPSLAMonitoringPol").get("attributes").get("tDn") != monitor_tdn:
+ child_configs.append(
+ {
+ "vnsRsIPSLAMonitoringPol": {
+ "attributes": {
+ "dn": child.get("vnsRsIPSLAMonitoringPol").get("attributes").get("dn"),
+ "status": "deleted",
+ }
+ }
+ }
+ )
+ aci.payload(
+ aci_class="vnsSvcRedirectPol",
+ class_config=dict(
+ name=policy_name,
+ descr=description,
+ destType=dest_type,
+ hashingAlgorithm=hash_algorithm,
+ maxThresholdPercent=max_threshold,
+ minThresholdPercent=min_threshold,
+ programLocalPodOnly=pod_aware,
+ resilientHashEnabled=resilient_hash,
+ thresholdDownAction=threshold_down_action,
+ thresholdEnable=threshold_enable,
+ AnycastEnabled=anycast_enabled,
+ srcMacRewriteEnabled=rewrite_source_mac,
+ ),
+ child_configs=child_configs,
+ )
+ aci.get_diff(aci_class="vnsSvcRedirectPol")
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_policy_based_redirect_dest.py b/plugins/modules/aci_l4l7_policy_based_redirect_dest.py
new file mode 100644
index 000000000..8661b6ff9
--- /dev/null
+++ b/plugins/modules/aci_l4l7_policy_based_redirect_dest.py
@@ -0,0 +1,390 @@
+#!/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_policy_based_redirect_dest
+short_description: Manage L4-L7 Policy Based Redirect Destinations (vns:RedirectDest and vns:L1L2RedirectDest)
+description:
+- Manage L4-L7 Policy Based Redirect Destinations
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ policy:
+ description:
+ - The name of an existing Policy Based Redirect Policy.
+ type: str
+ aliases: [ policy_name ]
+ ip:
+ description:
+ - The destination IP for redirection.
+ - Only used if I(dest_type=l3)
+ aliases: [ redirect_ip ]
+ type: str
+ additional_ip:
+ description:
+ - The Additional IP Address for the Destination.
+ - Only used if I(dest_type=l3)
+ type: str
+ logical_dev:
+ description:
+ - The destination Logical Device for redirection.
+ - Only used if I(dest_type=l1/l2)
+ type: str
+ concrete_dev:
+ description:
+ - The destination Concrete Device for redirection.
+ - Only used if I(dest_type=l1/l2)
+ type: str
+ concrete_intf:
+ description:
+ - The destination Concrete Interface for redirection.
+ - Only used if I(dest_type=l1/l2)
+ type: str
+ mac:
+ description:
+ - The destination MAC address for redirection.
+ type: str
+ aliases: [ redirect_mac ]
+ dest_name:
+ description:
+ - The name for Policy Based Redirect destination.
+ type: str
+ dest_type:
+ description:
+ - The destination type.
+ type: str
+ choices: [ l1/l2, l3 ]
+ default: l3
+ pod_id:
+ description:
+ - The Pod ID to deploy Policy Based Redirect destination on.
+ - The APIC defaults to C(1) when unset during creation.
+ type: int
+ health_group:
+ description:
+ - The Health Group to bind the Policy Based Redirection Destination to.
+ - To remove an existing binding from a Health Group, submit a request with I(state=present) and no I(health_group) value.
+ 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) and I(policy) must exist before using this module in your playbook.
+ The M(cisco.aci.aci_tenant) and M(cisco.aci.aci_l4l7_policy_based_redirect) modules can be used for this.
+seealso:
+- module: aci_l4l7_policy_based_redirect
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:RedirectDest)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add destination to a Policy Based Redirect Policy
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy: my_pbr_policy
+ dest_type: l3
+ ip: 192.168.10.1
+ mac: AB:CD:EF:12:34:56
+ dest_name: redirect_dest
+ pod_id: 1
+ state: present
+ delegate_to: localhost
+
+- name: Remove destination from a Policy Based Redirect Policy
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy: my_pbr_policy
+ state: absent
+ delegate_to: localhost
+
+- name: Query destinations for a Policy Based Redirect Policy
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ policy: my_pbr_policy
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query destinations for all Policy Based Redirect Policies
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec
+
+
+def main():
+ argument_spec = aci_argument_spec()
+ argument_spec.update(aci_annotation_spec())
+ argument_spec.update(
+ tenant=dict(type="str", aliases=["tenant_name"]),
+ policy=dict(type="str", aliases=["policy_name"]),
+ ip=dict(type="str", aliases=["redirect_ip"]),
+ additional_ip=dict(type="str"),
+ mac=dict(type="str", aliases=["redirect_mac"]),
+ logical_dev=dict(type="str"),
+ concrete_dev=dict(type="str"),
+ concrete_intf=dict(type="str"),
+ dest_name=dict(type="str"),
+ dest_type=dict(type="str", default="l3", choices=["l1/l2", "l3"]),
+ health_group=dict(type="str"),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ pod_id=dict(type="int"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[["state", "absent", ["tenant", "policy"]], ["state", "present", ["tenant", "policy"]]],
+ )
+
+ aci = ACIModule(module)
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ policy = module.params.get("policy")
+ ip = module.params.get("ip")
+ additional_ip = module.params.get("additional_ip")
+ mac = module.params.get("mac")
+ logical_dev = module.params.get("logical_dev")
+ concrete_dev = module.params.get("concrete_dev")
+ concrete_intf = module.params.get("concrete_intf")
+ dest_name = module.params.get("dest_name")
+ dest_type = module.params.get("dest_type")
+ health_group = module.params.get("health_group")
+ state = module.params.get("state")
+ pod_id = module.params.get("pod_id")
+
+ if dest_type == "l3":
+ aci_class = "vnsRedirectDest"
+ aci_rn = "RedirectDest_ip-[{0}]".format(ip)
+ module_object = ip
+ target_filter = {"ip": ip}
+ child_classes = ["vnsRsRedirectHealthGroup"]
+ redirect_hg_class = "vnsRsRedirectHealthGroup"
+ elif dest_type == "l1/l2":
+ aci_class = "vnsL1L2RedirectDest"
+ aci_rn = "L1L2RedirectDest-[{0}]".format(dest_name)
+ module_object = dest_name
+ target_filter = {"destName": dest_name}
+ child_classes = ["vnsRsL1L2RedirectHealthGroup", "vnsRsToCIf"]
+ redirect_hg_class = "vnsRsL1L2RedirectHealthGroup"
+
+ 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="vnsSvcRedirectPol",
+ aci_rn="svcCont/svcRedirectPol-{0}".format(policy),
+ module_object=policy,
+ target_filter={"name": policy},
+ ),
+ subclass_2=dict(
+ aci_class=aci_class,
+ aci_rn=aci_rn,
+ module_object=module_object,
+ target_filter=target_filter,
+ ),
+ child_classes=child_classes,
+ )
+ aci.get_existing()
+
+ if state == "present":
+ if dest_type == "l1/l2" and additional_ip is not None:
+ aci.fail_json(msg="You cannot provide an additional_ip when configuring an l1/l2 destination")
+ elif dest_type == "l3" and (logical_dev, concrete_dev, concrete_intf) != (None, None, None):
+ aci.fail_json(msg="You cannot provide a logical_dev, concrete_dev or concrete_intf when configuring an l3 destination")
+ elif dest_type == "l1/l2" and (logical_dev, concrete_dev, concrete_intf) == (None, None, None):
+ aci.fail_json(msg="You must provide a logical_dev, concrete_dev and concrete_intf when configuring an l1/l2 destination")
+ elif dest_type == "l1/l2" and ip is not None:
+ aci.fail_json(msg="You cannot provide an ip when configuring an l1/l2 destination")
+ if dest_type == "l3":
+ child_configs = []
+ elif dest_type == "l1/l2":
+ child_configs = [
+ {"vnsRsToCIf": {"attributes": {"tDn": "uni/tn-{0}/lDevVip-{1}/cDev-{2}/cIf-[{3}]".format(tenant, logical_dev, concrete_dev, concrete_intf)}}}
+ ]
+ if health_group is not None:
+ health_group_tdn = "uni/tn-{0}/svcCont/redirectHealthGroup-{1}".format(tenant, health_group)
+ child_configs.append({redirect_hg_class: {"attributes": {"tDn": health_group_tdn}}})
+ else:
+ health_group_tdn = None
+ if isinstance(aci.existing, list) and len(aci.existing) > 0:
+ for child in aci.existing[0].get(aci_class, {}).get("children", {}):
+ if child.get(redirect_hg_class) and child.get(redirect_hg_class).get("attributes").get("tDn") != health_group_tdn:
+ child_configs.append(
+ {
+ redirect_hg_class: {
+ "attributes": {
+ "dn": child.get(redirect_hg_class).get("attributes").get("dn"),
+ "status": "deleted",
+ }
+ }
+ }
+ )
+ aci.payload(
+ aci_class=aci_class,
+ class_config=dict(ip=ip, mac=mac, destName=dest_name, podId=pod_id, ip2=additional_ip),
+ child_configs=child_configs,
+ )
+ aci.get_diff(aci_class=aci_class)
+
+ aci.post_config()
+
+ elif state == "absent":
+ aci.delete_config()
+
+ aci.exit_json()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/plugins/modules/aci_l4l7_redirect_health_group.py b/plugins/modules/aci_l4l7_redirect_health_group.py
new file mode 100644
index 000000000..9cc8cff46
--- /dev/null
+++ b/plugins/modules/aci_l4l7_redirect_health_group.py
@@ -0,0 +1,260 @@
+#!/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_redirect_health_group
+short_description: Manage L4-L7 Redirect Health Groups (vns:RedirectHealthGroup)
+description:
+- Manage L4-L7 Redirect Health Groups.
+options:
+ tenant:
+ description:
+ - The name of an existing tenant.
+ type: str
+ aliases: [ tenant_name ]
+ health_group:
+ description:
+ - The name of the Health Group.
+ 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:
+- name: APIC Management Information Model reference
+ description: More information about the internal APIC class B(vns:RedirectHealthGroup)
+ link: https://developer.cisco.com/docs/apic-mim-ref/
+author:
+- Tim Cragg (@timcragg)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ health_group: my_health_group
+ state: present
+ delegate_to: localhost
+
+- name: Delete a Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ health_group: my_health_group
+ state: absent
+ delegate_to: localhost
+
+- name: Query a Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ health_group: my_health_group
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+- name: Query all Redirect Health Groups
+ cisco.aci.aci_l4l7_redirect_health_group:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+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"]),
+ state=dict(type="str", default="present", choices=["absent", "present", "query"]),
+ health_group=dict(type="str"),
+ )
+
+ module = AnsibleModule(
+ argument_spec=argument_spec,
+ supports_check_mode=True,
+ required_if=[
+ ["state", "absent", ["tenant", "health_group"]],
+ ["state", "present", ["tenant", "health_group"]],
+ ],
+ )
+
+ tenant = module.params.get("tenant")
+ state = module.params.get("state")
+ health_group = module.params.get("health_group")
+
+ 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="vnsRedirectHealthGroup",
+ aci_rn="svcCont/redirectHealthGroup-{0}".format(health_group),
+ module_object=health_group,
+ target_filter={"name": health_group},
+ ),
+ )
+ aci.get_existing()
+
+ if state == "present":
+ aci.payload(
+ aci_class="vnsRedirectHealthGroup",
+ class_config=dict(name=health_group),
+ )
+ aci.get_diff(aci_class="vnsRedirectHealthGroup")
+
+ 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..f2d7ad822
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template.py
@@ -0,0 +1,266 @@
+#!/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
+short_description: Manage L4-L7 Service Graph Templates (vns:AbsGraph)
+description:
+- Manage 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.
+ 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
+
+seealso:
+- 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)
+"""
+
+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: 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
+
+- 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
+
+"""
+
+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"]),
+ ui_template_type=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 = module.params.get("ui_template_type")
+
+ 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),
+ )
+ 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_conn.py b/plugins/modules/aci_l4l7_service_graph_template_abs_conn.py
new file mode 100644
index 000000000..2c9131999
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_abs_conn.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_conn
+short_description: Manage L4-L7 Service Graph Template Abs Connections (vns:AbsConnection)
+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 the connection, e.g C1 or C2.
+ 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 adjacecncy Layer2 or Layer3.
+ - The APIC defaults to C(l2) when unset during creation.
+ type: str
+ choices: [ l2, l3 ]
+ 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_node) modules can be used for this.
+
+seealso:
+- module: 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)
+"""
+
+EXAMPLES = r"""
+- name: Add a new connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_graph
+ connection_name: C1
+ state: present
+ delegate_to: localhost
+
+- name: Delete a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: my_graph
+ connection_name: C1
+ state: absent
+ delegate_to: localhost
+
+- name: Query a connection
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ 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_conn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+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"]),
+ )
+
+ 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"))
+
+ 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),
+ )
+ 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..34e96e7ff
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_abs_connection_conns.py
@@ -0,0 +1,315 @@
+#!/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_node)
+ and M(cisco.aci.aci_l4l7_service_graph_template_abs_conn) modules can be used for this.
+
+seealso:
+- module: aci_l4l7_service_graph_template
+- module: aci_l4l7_service_graph_template_abs_connection
+- module: 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)
+"""
+
+EXAMPLES = r"""
+- name: Add a new connection to a node
+ 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
+ connected_node: my_node
+ state: present
+ delegate_to: localhost
+
+- name: Add a new connection to a terminal node
+ 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: 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
+
+- 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
+
+"""
+
+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_func_conn.py b/plugins/modules/aci_l4l7_service_graph_template_func_conn.py
new file mode 100644
index 000000000..89aabe308
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_func_conn.py
@@ -0,0 +1,296 @@
+#!/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_func_conn
+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 ]
+ 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: aci_l4l7_service_graph_template
+- module: 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)
+"""
+
+EXAMPLES = r"""
+- name: Add a new Consumer Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ 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: Delete a Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ tenant: my_tenant
+ service_graph: test-graph
+ node: test-node
+ connection_name: consumer
+ state: absent
+ delegate_to: localhost
+
+- name: Query a Functional Connection
+ cisco.aci.aci_l4l7_service_graph_template_func_conn:
+ 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_func_conn:
+ host: apic
+ username: admin
+ password: SomeSecretPassword
+ state: query
+ delegate_to: localhost
+ register: query_result
+
+"""
+
+RETURN = r"""
+current:
+ description: The existing configuration from the APIC after the module has finished
+ returned: success
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+error:
+ description: The error information as returned from the APIC
+ returned: failure
+ type: dict
+ sample:
+ {
+ "code": "122",
+ "text": "unknown managed object class foo"
+ }
+raw:
+ description: The raw output returned by the APIC REST API (xml or json)
+ returned: parse error
+ type: str
+ sample: ''
+sent:
+ description: The actual/minimal configuration pushed to the APIC
+ returned: info
+ type: list
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment"
+ }
+ }
+ }
+previous:
+ description: The original configuration from the APIC before the module has started
+ returned: info
+ type: list
+ sample:
+ [
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production",
+ "dn": "uni/tn-production",
+ "name": "production",
+ "nameAlias": "",
+ "ownerKey": "",
+ "ownerTag": ""
+ }
+ }
+ }
+ ]
+proposed:
+ description: The assembled configuration from the user-provided parameters
+ returned: info
+ type: dict
+ sample:
+ {
+ "fvTenant": {
+ "attributes": {
+ "descr": "Production environment",
+ "name": "production"
+ }
+ }
+ }
+filter_string:
+ description: The filter string used for the request
+ returned: failure or debug
+ type: str
+ sample: ?rsp-prop-include=config-only
+method:
+ description: The HTTP method used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: POST
+response:
+ description: The HTTP response from the APIC
+ returned: failure or debug
+ type: str
+ sample: OK (30 bytes)
+status:
+ description: The HTTP status from the APIC
+ returned: failure or debug
+ type: int
+ sample: 200
+url:
+ description: The HTTP url used for the request to the APIC
+ returned: failure or debug
+ type: str
+ sample: https://10.11.12.13/api/mo/uni/tn-production.json
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.cisco.aci.plugins.module_utils.aci import ACIModule, aci_argument_spec, aci_annotation_spec, aci_owner_spec
+
+
+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"]),
+ )
+
+ 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"]],
+ ],
+ )
+
+ 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")
+
+ 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},
+ ),
+ 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),
+ )
+ 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..1ad7bf129
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_node.py
@@ -0,0 +1,344 @@
+#!/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_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
+ node:
+ description:
+ - The name of the Service Graph Template Node.
+ type: str
+ func_template_type:
+ description:
+ - The functional template type for the node.
+ - The APIC defaults to C(other) when unset during creation.
+ type: str
+ choices: [ fw_trans, fw_routed, adc_one_arm, adc_two_arm, other ]
+ func_type:
+ description:
+ - The type of connection.
+ - The APIC defaults to C(go_to) when unset during creation.
+ type: str
+ choices: [ go_to, go_through, l1, l2 ]
+ 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.
+ 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_node) modules can be used for this.
+seealso:
+- module: aci_l4l7_service_graph_template
+- module: 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)
+"""
+
+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
+ func_template_type: adc_one_arm
+ func_type: GoTo
+ device: test-device
+ managed: no
+ routing_mode: Redirect
+ state: present
+ delegate_to: localhost
+
+- 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
+
+- 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
+
+"""
+
+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
+
+
+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"]),
+ func_template_type=dict(type="str", choices=["fw_trans", "fw_routed", "adc_one_arm", "adc_two_arm", "other"]),
+ func_type=dict(type="str", choices=["go_to", "go_through", "l1", "l2"]),
+ device=dict(type="str"),
+ device_tenant=dict(type="str"),
+ managed=dict(type="bool"),
+ routing_mode=dict(type="str"),
+ )
+
+ 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")
+ func_template_type = module.params.get("func_template_type")
+ func_type = L4L7_FUNC_TYPES_MAPPING.get(module.params.get("func_type"))
+ device = module.params.get("device")
+ device_tenant = module.params.get("device_tenant")
+ managed = aci.boolean(module.params.get("managed"))
+ routing_mode = module.params.get("routing_mode")
+
+ if func_template_type:
+ func_template_upper = func_template_type.upper()
+ else:
+ func_template_upper = 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="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=func_template_upper, funcType=func_type, managed=managed, routingMode=routing_mode),
+ 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..de4ab3987
--- /dev/null
+++ b/plugins/modules/aci_l4l7_service_graph_template_term_node.py
@@ -0,0 +1,292 @@
+#!/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_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_node) modules can be used for this.
+
+seealso:
+- module: 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)
+"""
+
+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: 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
+
+- 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
+
+"""
+
+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_ip_sla_monitoring_policy/aliases b/tests/integration/targets/aci_ip_sla_monitoring_policy/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_ip_sla_monitoring_policy/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_ip_sla_monitoring_policy/tasks/main.yml b/tests/integration/targets/aci_ip_sla_monitoring_policy/tasks/main.yml
new file mode 100644
index 000000000..a898b05bc
--- /dev/null
+++ b/tests/integration/targets/aci_ip_sla_monitoring_policy/tasks/main.yml
@@ -0,0 +1,252 @@
+# 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 ICMP SLA POLICY
+- name: Create ICMP SLA policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ sla_type: icmp
+ frequency: 40
+ multiplier: 6
+ state: present
+ register: create_icmp_sla
+
+- name: Verify SLA creation
+ ansible.builtin.assert:
+ that:
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.dn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_sla"
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.name == "ansible_sla"
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaDetectMultiplier == "6"
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaFrequency == "40"
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaPort == "0"
+ - create_icmp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaType == "icmp"
+
+# CREATE ICMP SLA POLICY AGAIN TO TEST IDEMPOTENCE
+- name: Create ICMP SLA policy again
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ sla_type: icmp
+ frequency: 40
+ multiplier: 6
+ state: present
+ register: create_icmp_sla_again
+
+- name: Verify SLA creation idempotence
+ ansible.builtin.assert:
+ that:
+ - create_icmp_sla_again is not changed
+
+# CREATE SECOND SLA POLICY
+- name: Create Second SLA policy again
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: second_ansible_sla
+ sla_type: icmp
+ frequency: 40
+ multiplier: 6
+ state: present
+
+# CREATE HTTP SLA POLICY
+- name: Create HTTP SLA policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_http_sla
+ sla_type: http
+ frequency: 40
+ multiplier: 6
+ http_version: 1.1
+ http_uri: /test
+ request_data_size: 100
+ operation_timeout: 2100
+ threshold: 2000
+ state: present
+ when: version.current.0.topSystem.attributes.version is version('5.2', '>=')
+ register: http_sla
+
+- name: Verify HTTP SLA creation
+ ansible.builtin.assert:
+ that:
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.dn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_http_sla"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.name == "ansible_http_sla"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.slaDetectMultiplier == "6"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.slaFrequency == "40"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.slaPort == "80"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.slaType == "http"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.reqDataSize == "100"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.timeout == "2100"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.threshold == "2000"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.httpVersion == "HTTP11"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.httpUri == "/test"
+ - http_sla.current.0.fvIPSLAMonitoringPol.attributes.httpMethod == "get"
+ when: version.current.0.topSystem.attributes.version is version('5.2', '>=')
+
+# TEST ERROR CHECKING
+- name: Create ICMP Monitoring Policy with sla_port
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_http_sla
+ sla_type: icmp
+ sla_port: 8080
+ state: present
+ ignore_errors: true
+ register: icmp_with_sla_port
+
+- name: Create ICMP Monitoring Policy with request_data_size
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_http_sla
+ sla_type: icmp
+ request_data_size: 2000
+ state: present
+ ignore_errors: true
+ register: icmp_with_request_data_size
+
+- name: Verify Errors
+ ansible.builtin.assert:
+ that:
+ - icmp_with_sla_port is failed
+ - icmp_with_request_data_size is failed
+ - icmp_with_sla_port.msg == "Setting 'sla_port' is not allowed when 'sla_type' is not set to 'tcp'."
+ - icmp_with_request_data_size.msg == "Setting 'request_data_size' is not allowed when 'sla_type' is not set to 'http."
+
+# MODIFY SLA POLICY
+- name: Convert ICMP SLA policy to TCP
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ sla_type: tcp
+ sla_port: 8080
+ frequency: 20
+ multiplier: 5
+ state: present
+ register: update_tcp_sla
+
+- name: Verify SLA update
+ ansible.builtin.assert:
+ that:
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.dn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_sla"
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.name == "ansible_sla"
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaDetectMultiplier == "5"
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaFrequency == "20"
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaPort == "8080"
+ - update_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaType == "tcp"
+
+# QUERY IP SLA POLICY
+- name: Query IP SLA monitor
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ state: query
+ register: query_tcp_sla
+
+- name: Verify SLA query
+ ansible.builtin.assert:
+ that:
+ - query_tcp_sla is not changed
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.dn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_sla"
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.name == "ansible_sla"
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaDetectMultiplier == "5"
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaFrequency == "20"
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaPort == "8080"
+ - query_tcp_sla.current.0.fvIPSLAMonitoringPol.attributes.slaType == "tcp"
+
+# QUERY ALL SLA POLICIES
+- name: Query all IP SLA monitor policies
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ state: query
+ register: query_sla_all
+
+- name: Verify all SLA query
+ ansible.builtin.assert:
+ that:
+ - query_sla_all is not changed
+ - query_sla_all.current | length > 1
+
+# DELETE SLA POLICY
+- name: Remove IP SLA monitor
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ state: absent
+ register: remove_tcp_sla
+
+- name: Verify IP SLA deletion
+ ansible.builtin.assert:
+ that:
+ - remove_tcp_sla is changed
+ - remove_tcp_sla.current == []
+ - remove_tcp_sla.previous.0.fvIPSLAMonitoringPol.attributes.dn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_sla"
+ - remove_tcp_sla.previous.0.fvIPSLAMonitoringPol.attributes.name == "ansible_sla"
+
+# DELETE IP SLA POLICY AGAIN TO TEST IDEMPOTENCE
+- name: Remove IP SLA monitor again
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ sla_policy: ansible_sla
+ state: absent
+ register: remove_tcp_sla_again
+
+- name: Verify IP SLA deletion
+ ansible.builtin.assert:
+ that:
+ - remove_tcp_sla_again is not changed
+
+# 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_concrete_device/aliases b/tests/integration/targets/aci_l4l7_concrete_device/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_device/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_concrete_device/tasks/main.yml b/tests/integration/targets/aci_l4l7_concrete_device/tasks/main.yml
new file mode 100644
index 000000000..e8e112454
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_device/tasks/main.yml
@@ -0,0 +1,175 @@
+# 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
+
+# ADD L4-L7 CONCRETE DEVICE
+- name: Create L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: present
+ register: add_l4l7_concrete_device
+
+- name: Verify L4-L7 Concrete Device Attributes
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_concrete_device.current.0.vnsCDev.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device"
+ - add_l4l7_concrete_device.current.0.vnsCDev.attributes.name == "ansible_concrete_device"
+
+# ADD L4-L7 CONCRETE DEVICE AGAIN TO CHECK IDEMPOTENCE
+- name: Add L4-L7 Concrete Device again
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: present
+ register: add_l4l7_concrete_device_again
+
+- name: Verify L4-L7 Concrete Device Attributes
+ ansible.builtin.assert:
+ that:
+ - add_l4l7_concrete_device_again is not changed
+ - add_l4l7_concrete_device_again.current.0.vnsCDev.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device"
+ - add_l4l7_concrete_device_again.current.0.vnsCDev.attributes.name == "ansible_concrete_device"
+
+# QUERY L4-L7 CONCRETE DEVICE
+- name: Query L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: query
+ register: query_l4l7_concrete_device
+
+- name: Verify L4-L7 Concrete Device Attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_concrete_device is not changed
+ - query_l4l7_concrete_device.current.0.vnsCDev.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device"
+ - query_l4l7_concrete_device.current.0.vnsCDev.attributes.name == "ansible_concrete_device"
+
+- name: Query All L4-L7 Concrete Devices
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ state: query
+ register: query_l4l7_concrete_device_all
+
+- name: Verify L4-L7 Concrete Device Attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_concrete_device_all is not changed
+
+# DELETE L4-L7 CONCRETE DEVICE
+- name: Remove L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: absent
+ register: delete_l4l7_concrete_device
+
+- name: Verify L4-L7 Concrete Device Deletion
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_concrete_device is changed
+ - delete_l4l7_concrete_device.current == []
+ - delete_l4l7_concrete_device.previous.0.vnsCDev.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device"
+ - delete_l4l7_concrete_device.previous.0.vnsCDev.attributes.name == "ansible_concrete_device"
+
+# DELETE L4-L7 CONCRETE DEVICE AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: absent
+ register: delete_l4l7_concrete_device_again
+
+- name: Verify L4-L7 Concrete Device Deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_concrete_device_again is not changed
+ - delete_l4l7_concrete_device_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_concrete_interface/aliases b/tests/integration/targets/aci_l4l7_concrete_interface/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_interface/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_concrete_interface/tasks/main.yml b/tests/integration/targets/aci_l4l7_concrete_interface/tasks/main.yml
new file mode 100644
index 000000000..776e61336
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_interface/tasks/main.yml
@@ -0,0 +1,220 @@
+# 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
+
+# ADD L4-L7 CONCRETE DEVICE
+- name: Create L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: present
+
+# ADD L4-L7 CONCRETE INTERFACE
+- name: Create L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: present
+ register: add_concrete_interface
+
+- name: Verify L4-L7 Concrete Interface Attributes
+ ansible.builtin.assert:
+ that:
+ - add_concrete_interface.current.0.vnsCIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - add_concrete_interface.current.0.vnsCIf.attributes.name == "ansible_concrete_interface"
+
+- name: Verify Path Attribute
+ ansible.builtin.assert:
+ that:
+ - add_concrete_interface.current.0.vnsCIf.children.0.vnsRsCIfPathAtt.attributes.tDn == "topology/pod-1/paths-201/pathep-[eth1/16]"
+
+# ADD L4-L7 CONCRETE INTERFACE AGAIN TO CHECK IDEMPOTENCE
+- name: Create L4-L7 Concrete Interface again
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: present
+ register: add_concrete_interface_again
+
+- name: Verify L4-L7 Concrete Interface Attributes
+ ansible.builtin.assert:
+ that:
+ - add_concrete_interface_again is not changed
+ - add_concrete_interface_again.current.0.vnsCIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - add_concrete_interface_again.current.0.vnsCIf.attributes.name == "ansible_concrete_interface"
+
+- name: Verify Path Attribute
+ ansible.builtin.assert:
+ that:
+ - add_concrete_interface_again.current.0.vnsCIf.children.0.vnsRsCIfPathAtt.attributes.tDn == "topology/pod-1/paths-201/pathep-[eth1/16]"
+
+# QUERY L4-L7 CONCRETE INTERFACE
+- name: Query L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: query
+ register: query_concrete_interface
+
+- name: Verify L4-L7 Concrete Interface Attributes
+ ansible.builtin.assert:
+ that:
+ - query_concrete_interface is not changed
+ - query_concrete_interface.current.0.vnsCIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - query_concrete_interface.current.0.vnsCIf.attributes.name == "ansible_concrete_interface"
+
+- name: Verify Path Attribute
+ ansible.builtin.assert:
+ that:
+ - query_concrete_interface.current.0.vnsCIf.children.0.vnsRsCIfPathAtt.attributes.tDn == "topology/pod-1/paths-201/pathep-[eth1/16]"
+
+# QUERY ALL L4-L7 CONCRETE INTERFACES
+- name: Query all L4-L7 Concrete Interfaces
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ state: query
+ register: query_concrete_interface_all
+
+- name: Verify L4-L7 Concrete Interface Attributes
+ ansible.builtin.assert:
+ that:
+ - query_concrete_interface is not changed
+
+# DELETE L4-L7 CONCRETE INTERFACE
+- name: Remove L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: absent
+ register: delete_concrete_interface
+
+- name: Verify L4-L7 Concrete Interface Deletion
+ ansible.builtin.assert:
+ that:
+ - delete_concrete_interface is changed
+ - delete_concrete_interface.current == []
+ - delete_concrete_interface.previous.0.vnsCIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - delete_concrete_interface.previous.0.vnsCIf.attributes.name == "ansible_concrete_interface"
+
+# DELETE L4-L7 CONCRETE INTERFACE AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: absent
+ register: delete_concrete_interface_again
+
+- name: Verify L4-L7 Concrete Interface Deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_concrete_interface_again is not changed
+ - delete_concrete_interface_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_concrete_interface_attach/aliases b/tests/integration/targets/aci_l4l7_concrete_interface_attach/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_interface_attach/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_concrete_interface_attach/tasks/main.yml b/tests/integration/targets/aci_l4l7_concrete_interface_attach/tasks/main.yml
new file mode 100644
index 000000000..3833c3635
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_concrete_interface_attach/tasks/main.yml
@@ -0,0 +1,248 @@
+# 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 L4-L7 LOGICAL INTERFACE
+- name: Add 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
+
+- name: Add Second Logical Interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: second_log_intf
+ encap: vlan-988
+ state: present
+
+# CREATE L4-L7 CONCRETE INTERFACE
+- name: Create L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: present
+
+# CREATE L4-L7 CONCRETE INTERFACE
+- name: Create L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/16
+ state: present
+
+- name: Create Second L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_second_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/17
+ state: present
+
+- name: Add a new concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ state: present
+ register: add_concrete_intf_attach
+
+- name: Add a second concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: second_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_second_concrete_interface
+ state: present
+
+- name: Verify interface attachment
+ ansible.builtin.assert:
+ that:
+ - add_concrete_intf_attach.current.0.vnsRsCIfAttN.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf/rscIfAttN-[uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]]"
+ - add_concrete_intf_attach.current.0.vnsRsCIfAttN.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+
+# ADD CONCRETE INTERFACE ATTACHMENT AGAIN TO TEST IDEMPOTENCE
+- name: Add concrete interface attachment again
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ state: present
+ register: add_concrete_intf_attach_again
+
+- name: Verify interface attachment
+ ansible.builtin.assert:
+ that:
+ - add_concrete_intf_attach_again is not changed
+ - add_concrete_intf_attach_again.current.0.vnsRsCIfAttN.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf/rscIfAttN-[uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]]"
+ - add_concrete_intf_attach_again.current.0.vnsRsCIfAttN.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+
+# QUERY CONCRETE INTERFACE ATTACHMENT
+- name: Query concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ state: query
+ register: query_concrete_intf_attach
+
+- name: Verify interface attachment
+ ansible.builtin.assert:
+ that:
+ - query_concrete_intf_attach is not changed
+ - query_concrete_intf_attach.current.0.vnsRsCIfAttN.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf/rscIfAttN-[uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]]"
+ - query_concrete_intf_attach.current.0.vnsRsCIfAttN.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+
+- name: Query all concrete interface attachments
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ state: query
+ register: query_all_attachments
+
+- name: Verify query all
+ ansible.builtin.assert:
+ that:
+ - query_all_attachments is not changed
+ - query_all_attachments.current | length > 1
+
+# DELETE CONCRETE INTERFACE ATTACHMENT
+- name: Remove concrete interface attachment
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ state: absent
+ register: delete_concrete_intf_attach
+
+- name: Verify interface attachment removal
+ ansible.builtin.assert:
+ that:
+ - delete_concrete_intf_attach is changed
+ - delete_concrete_intf_attach.current == []
+ - delete_concrete_intf_attach.previous.0.vnsRsCIfAttN.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf/rscIfAttN-[uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]]"
+ - delete_concrete_intf_attach.previous.0.vnsRsCIfAttN.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+
+# DELETE CONCRETE INTERFACE ATTACHMENT AGAIN TO TEST IDEMPOTENCE
+- name: Remove concrete interface attachment again
+ cisco.aci.aci_l4l7_concrete_interface_attach:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ state: absent
+ register: delete_concrete_intf_attach_again
+
+- name: Verify interface attachment removal idemptence
+ ansible.builtin.assert:
+ that:
+ - delete_concrete_intf_attach_again is not changed
+ - delete_concrete_intf_attach_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_device/aliases b/tests/integration/targets/aci_l4l7_device/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_device/tasks/main.yml b/tests/integration/targets/aci_l4l7_device/tasks/main.yml
new file mode 100644
index 000000000..c9129bc95
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device/tasks/main.yml
@@ -0,0 +1,320 @@
+# 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
+
+- name: Remove ansible_vmm_domain if it already exists
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_vmm_dom
+ domain_type: vmm
+ vm_provider: vmware
+ state: absent
+
+# CREATE TENANT
+- name: Create ansible_tenant
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE DOMAINS
+- name: Create ansible_phys_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_phys_dom
+ domain_type: phys
+ state: present
+
+- name: Create ansible_vmm_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_vmm_dom
+ domain_type: vmm
+ vm_provider: vmware
+ state: present
+
+# ADD L4-L7 DEVICE
+- name: Create L4-L7 Physical 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
+ register: create_l4l7_device
+
+- name: Create L4-L7 Virtual Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_virt_device
+ domain: ansible_vmm_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: virtual
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+ register: create_virt_l4l7_device
+
+- name: Verify L4-L7 device has been created
+ ansible.builtin.assert:
+ that:
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.name == "ansible_device"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.contextAware == "single-Context"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.devtype == "PHYSICAL"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.funcType == "GoTo"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.managed == "no"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.promMode == "yes"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.svcType == "ADC"
+ - create_l4l7_device.current.0.vnsLDevVip.attributes.trunking == "no"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_virt_device"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.name == "ansible_virt_device"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.contextAware == "single-Context"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.devtype == "VIRTUAL"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.funcType == "GoTo"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.managed == "no"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.promMode == "yes"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.svcType == "ADC"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.attributes.trunking == "no"
+
+- name: Verify domain binding object has been created
+ ansible.builtin.assert:
+ that:
+ - create_l4l7_device.current.0.vnsLDevVip.children.0.vnsRsALDevToPhysDomP.attributes.tDn == "uni/phys-ansible_phys_dom"
+ - create_virt_l4l7_device.current.0.vnsLDevVip.children.0.vnsRsALDevToDomP.attributes.tDn == "uni/vmmp-VMware/dom-ansible_vmm_dom"
+
+# ADD device again to check idempotence
+- name: Create L4-L7 device again
+ 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
+ register: create_l4l7_device_again
+
+- name: Create L4-L7 Virtual Device again
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_virt_device
+ domain: ansible_vmm_dom
+ func_type: go_to
+ context_aware: single
+ managed: false
+ dev_type: virtual
+ svc_type: adc
+ trunking: false
+ prom_mode: true
+ state: present
+ register: create_virt_l4l7_device_again
+
+- name: Verify L4-L7 device idempotence
+ ansible.builtin.assert:
+ that:
+ - create_l4l7_device_again is not changed
+ - create_virt_l4l7_device_again is not changed
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.name == "ansible_device"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.contextAware == "single-Context"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.devtype == "PHYSICAL"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.funcType == "GoTo"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.managed == "no"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.promMode == "yes"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.svcType == "ADC"
+ - create_l4l7_device_again.current.0.vnsLDevVip.attributes.trunking == "no"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_virt_device"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.name == "ansible_virt_device"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.contextAware == "single-Context"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.devtype == "VIRTUAL"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.funcType == "GoTo"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.managed == "no"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.promMode == "yes"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.svcType == "ADC"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.attributes.trunking == "no"
+
+- name: Verify domain binding object is still correct
+ ansible.builtin.assert:
+ that:
+ - create_l4l7_device_again.current.0.vnsLDevVip.children.0.vnsRsALDevToPhysDomP.attributes.tDn == "uni/phys-ansible_phys_dom"
+ - create_virt_l4l7_device_again.current.0.vnsLDevVip.children.0.vnsRsALDevToDomP.attributes.tDn == "uni/vmmp-VMware/dom-ansible_vmm_dom"
+
+# MODIFY L4-L7 Device
+- name: Update L4-L7 device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ domain: ansible_phys_dom
+ func_type: go_through
+ context_aware: multi
+ managed: false
+ dev_type: physical
+ svc_type: fw
+ trunking: true
+ prom_mode: false
+ state: present
+ register: update_l4l7_device
+
+- name: Verify L4-L7 device has been updated
+ ansible.builtin.assert:
+ that:
+ - update_l4l7_device is changed
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.name == "ansible_device"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.contextAware == "multi-Context"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.devtype == "PHYSICAL"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.funcType == "GoThrough"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.managed == "no"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.promMode == "no"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.svcType == "FW"
+ - update_l4l7_device.current.0.vnsLDevVip.attributes.trunking == "yes"
+
+- name: Verify domain binding object
+ ansible.builtin.assert:
+ that:
+ - update_l4l7_device.current.0.vnsLDevVip.children.0.vnsRsALDevToPhysDomP.attributes.tDn == "uni/phys-ansible_phys_dom"
+
+# QUERY DEVICE
+- name: Query L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ state: query
+ register: query_l4l7_device
+
+- name: Verify L4-L7 Device attributes
+ ansible.builtin.assert:
+ that:
+ - query_l4l7_device is not changed
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.name == "ansible_device"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.contextAware == "multi-Context"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.devtype == "PHYSICAL"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.funcType == "GoThrough"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.isCopy == "no"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.managed == "no"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.promMode == "no"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.svcType == "FW"
+ - query_l4l7_device.current.0.vnsLDevVip.attributes.trunking == "yes"
+
+- name: Query all L4-L7 Devices
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ state: query
+ register: query_l4l7_device_all
+
+- name: Verify L4-L7 Device query idempotence
+ ansible.builtin.assert:
+ that:
+ query_l4l7_device_all is not changed
+
+# DELETE DEVICE
+- name: Delete L4-L7 Device
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ state: absent
+ register: remove_l4l7_device
+
+- name: Verify L4-L7 Device deletion
+ ansible.builtin.assert:
+ that:
+ - remove_l4l7_device is changed
+ - remove_l4l7_device.current == []
+ - remove_l4l7_device.previous.0.vnsLDevVip.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device"
+ - remove_l4l7_device.previous.0.vnsLDevVip.attributes.name == "ansible_device"
+
+# DELETE DEVICE AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Device again
+ cisco.aci.aci_l4l7_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ state: absent
+ register: remove_l4l7_device_again
+
+- name: Verify L4-L7 Device deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - remove_l4l7_device_again is not changed
+ - remove_l4l7_device_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_vmm_domain
+ cisco.aci.aci_domain:
+ <<: *aci_info
+ domain: ansible_vmm_dom
+ domain_type: vmm
+ vm_provider: vmware
+ state: absent
\ No newline at end of file
diff --git a/tests/integration/targets/aci_l4l7_device_selection_if_context/aliases b/tests/integration/targets/aci_l4l7_device_selection_if_context/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_if_context/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_device_selection_if_context/tasks/main.yml b/tests/integration/targets/aci_l4l7_device_selection_if_context/tasks/main.yml
new file mode 100644
index 000000000..29f9b13e6
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_if_context/tasks/main.yml
@@ -0,0 +1,341 @@
+# 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 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
+ delegate_to: localhost
+
+# 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
+ delegate_to: localhost
+
+# CREATE L4-L7 DEVICE SELECTION LOGICAL INTERFACE CONTEXT
+- name: Create 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_bd
+ logical_device: ansible_device
+ logical_interface: ansible_log_intf
+ redirect_policy: ansible_pbr_policy
+ state: present
+ register: add_context
+
+- name: Verify context creation
+ ansible.builtin.assert:
+ that:
+ - 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"
+ - add_context.current.0.vnsLIfCtx.children.1.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-ansible_tenant/BD-ansible_bd"
+ - add_context.current.0.vnsLIfCtx.children.2.vnsRsLIfCtxToLIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - add_context.current.0.vnsLIfCtx.children.0.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:
+ <<: *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
+ register: add_context_again
+
+- 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"
+ - add_context_again.current.0.vnsLIfCtx.children.1.vnsRsLIfCtxToBD.attributes.tDn == "uni/tn-ansible_tenant/BD-ansible_bd"
+ - add_context_again.current.0.vnsLIfCtx.children.2.vnsRsLIfCtxToLIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - add_context_again.current.0.vnsLIfCtx.children.0.vnsRsLIfCtxToSvcRedirectPol.attributes.tDn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+
+# 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
+ 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: absent
+ register: delete_context
+
+- name: Verify context deletion
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ context: provider
+ state: absent
+ 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/aliases b/tests/integration/targets/aci_l4l7_device_selection_policy/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_policy/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
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..11935f18b
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_device_selection_policy/tasks/main.yml
@@ -0,0 +1,233 @@
+# 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
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ device: ansible_device
+ state: present
+ register: add_l4l7_device_selection_policy
+
+- name: Verify L4-L7 Device Selection Policy attributes
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ device: ansible_device
+ state: present
+ 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
+ cisco.aci.aci_l4l7_device_selection_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: absent
+ register: delete_l4l7_device_selection_policy
+
+- name: Verify L4-L7 Device Selection Policy removal
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ contract: ansible_contract
+ graph: ansible_graph
+ node: ansible_node
+ state: absent
+ 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_logical_interface/aliases b/tests/integration/targets/aci_l4l7_logical_interface/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_logical_interface/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_logical_interface/tasks/main.yml b/tests/integration/targets/aci_l4l7_logical_interface/tasks/main.yml
new file mode 100644
index 000000000..4f0d42fcf
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_logical_interface/tasks/main.yml
@@ -0,0 +1,201 @@
+# 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 L4-L7 LOGICAL INTERFACE
+- name: Add 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
+ register: add_logical_interface
+
+- name: Verify Logical Interface Attributes
+ ansible.builtin.assert:
+ that:
+ - add_logical_interface.current.0.vnsLIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - add_logical_interface.current.0.vnsLIf.attributes.name == "ansible_log_intf"
+ - add_logical_interface.current.0.vnsLIf.attributes.encap == "vlan-987"
+
+# ADD L4-L7 LOGICAL INTERFACE AGAIN TO CHECK IDEMPOTENCE
+- name: Add Logical Interface again
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ encap: vlan-987
+ state: present
+ register: add_logical_interface_again
+
+- name: Verify Logical Interface Attributes have not changed
+ ansible.builtin.assert:
+ that:
+ - add_logical_interface_again is not changed
+ - add_logical_interface_again.current.0.vnsLIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - add_logical_interface_again.current.0.vnsLIf.attributes.name == "ansible_log_intf"
+ - add_logical_interface_again.current.0.vnsLIf.attributes.encap == "vlan-987"
+
+# MODIFY L4-L7 LOGICAL INTERFACE
+- name: Update L4-L7 Logical Interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ encap: vlan-988
+ state: present
+ register: update_logical_interface
+
+- name: Verify Logical Interface update
+ ansible.builtin.assert:
+ that:
+ - update_logical_interface is changed
+ - update_logical_interface.current.0.vnsLIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - update_logical_interface.current.0.vnsLIf.attributes.name == "ansible_log_intf"
+ - update_logical_interface.current.0.vnsLIf.attributes.encap == "vlan-988"
+
+# QUERY L4-L7 LOGICAL INTERFACE
+- name: Query L4-L7 Logical Interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ state: query
+ register: query_logical_interface
+
+- name: Verify Logical Interface Attributes have not changed
+ ansible.builtin.assert:
+ that:
+ - query_logical_interface is not changed
+ - query_logical_interface.current.0.vnsLIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - query_logical_interface.current.0.vnsLIf.attributes.name == "ansible_log_intf"
+ - query_logical_interface.current.0.vnsLIf.attributes.encap == "vlan-988"
+
+# QUERY ALL L4-L7 LOGICAL INTERFACES
+- name: Query All L4-L7 Logical Interfaces
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ state: query
+ register: query_logical_interface_all
+
+- name: Verify Logical Interface Attributes have not changed
+ ansible.builtin.assert:
+ that:
+ - query_logical_interface_all is not changed
+
+# DELETE L4-L7 LOGICAL INTERFACE
+- name: Remove L4-L7 Logical Interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ state: absent
+ register: delete_logical_interface
+
+- name: Verify Logical Interface Deletion
+ ansible.builtin.assert:
+ that:
+ - delete_logical_interface is changed
+ - delete_logical_interface.current == []
+ - delete_logical_interface.previous.0.vnsLIf.attributes.dn == "uni/tn-ansible_tenant/lDevVip-ansible_device/lIf-ansible_log_intf"
+ - delete_logical_interface.previous.0.vnsLIf.attributes.name == "ansible_log_intf"
+ - delete_logical_interface.previous.0.vnsLIf.attributes.encap == "vlan-988"
+
+# DELETE L4-L7 LOGICAL INTERFACE AGAIN TO TEST IDEMPOTENCE
+- name: Remove L4-L7 Logical Interface
+ cisco.aci.aci_l4l7_logical_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ logical_interface: ansible_log_intf
+ state: absent
+ register: delete_logical_interface_again
+
+- name: Verify Logical Interface Deletion
+ ansible.builtin.assert:
+ that:
+ - delete_logical_interface_again is not changed
+ - delete_logical_interface_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_policy_based_redirect/aliases b/tests/integration/targets/aci_l4l7_policy_based_redirect/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_policy_based_redirect/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_policy_based_redirect/tasks/main.yml b/tests/integration/targets/aci_l4l7_policy_based_redirect/tasks/main.yml
new file mode 100644
index 000000000..d34c0399a
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_policy_based_redirect/tasks/main.yml
@@ -0,0 +1,382 @@
+# 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
+
+- name: Query system information
+ cisco.aci.aci_system:
+ <<: *aci_info
+ id: 1
+ state: query
+ register: version
+
+- name: Create IP SLA Monitoring Policy
+ cisco.aci.aci_ip_sla_monitoring_policy:
+ <<: *aci_info
+ tenant: ansible_tenant
+ name: ansible_ip_sla_mon_policy
+ sla_type: icmp
+ state: present
+
+# CREATE L4-L7 POLICY BASED REDIRECT
+- name: Create L4-L7 Policy Based Redirect (v4)
+ 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: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+ register: add_pbr_policy_v4
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Create L4-L7 Policy Based Redirect (v3)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ hash_algorithm: destination_ip
+ resilient_hash: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+ register: add_pbr_policy_v3
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Verify PBR Attributes v4
+ ansible.builtin.assert:
+ that:
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.destType == "L3"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "dip"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "90"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "60"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "yes"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "yes"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "permit"
+ - add_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "yes"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Verify PBR Attributes v3
+ ansible.builtin.assert:
+ that:
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "dip"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "90"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "60"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "yes"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "yes"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "permit"
+ - add_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "yes"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+# ADD L4-L7 PBR AGAIN TO CHECK IDEMPOTENCY
+- name: Add L4-L7 Policy Based Redirect again (v4)
+ 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: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+ register: add_pbr_policy_v4_again
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Add L4-L7 Policy Based Redirect again (v3)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ hash_algorithm: destination_ip
+ resilient_hash: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+ register: add_pbr_policy_v3_again
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Verify PBR Attributes (v4)
+ ansible.builtin.assert:
+ that:
+ - add_pbr_policy_v4_again is not changed
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.destType == "L3"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "dip"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "90"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "60"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "yes"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "yes"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "permit"
+ - add_pbr_policy_v4_again.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "yes"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Verify PBR Attributes (v3)
+ ansible.builtin.assert:
+ that:
+ - add_pbr_policy_v3_again is not changed
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "dip"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "90"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "60"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "yes"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "yes"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "permit"
+ - add_pbr_policy_v3_again.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "yes"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+# MODIFY L4-L7 PBR POLICY
+- name: Update L4-L7 Policy Based Redirect (v4)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ dest_type: l2
+ hash_algorithm: ip_and_protocol
+ resilient_hash: false
+ min_threshold: 50
+ max_threshold: 80
+ threshold_enable: false
+ threshold_down_action: deny
+ pod_aware: false
+ monitor_policy: ansible_ip_sla_mon_policy
+ anycast_enabled: false
+ state: present
+ register: update_pbr_policy_v4
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Update L4-L7 Policy Based Redirect (v3)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ hash_algorithm: ip_and_protocol
+ resilient_hash: false
+ min_threshold: 50
+ max_threshold: 80
+ threshold_enable: false
+ threshold_down_action: deny
+ pod_aware: false
+ anycast_enabled: false
+ state: present
+ register: update_pbr_policy_v3
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Verify PBR Attributes (v4)
+ ansible.builtin.assert:
+ that:
+ - update_pbr_policy_v4 is changed
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.destType == "L2"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "sip-dip-prototype"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "80"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "50"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "no"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "no"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "deny"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "no"
+ - update_pbr_policy_v4.current.0.vnsSvcRedirectPol.children.0.vnsRsIPSLAMonitoringPol.attributes.tDn == "uni/tn-ansible_tenant/ipslaMonitoringPol-ansible_ip_sla_mon_policy"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Verify PBR Attributes (v3)
+ ansible.builtin.assert:
+ that:
+ - update_pbr_policy_v3 is changed
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "sip-dip-prototype"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "80"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "50"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "no"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "no"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "deny"
+ - update_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "no"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Remove Monitoring Policy
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ dest_type: l2
+ state: present
+ register: remove_monitoring_policy
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Verify Monitoring Policy Removal
+ ansible.builtin.assert:
+ that:
+ - remove_monitoring_policy.current.0.children is not defined
+
+# QUERY L4-L7 PBR POLICY
+- name: Query L4-L7 Policy Based Redirect (v4)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ state: query
+ register: query_pbr_policy_v4
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Query L4-L7 Policy Based Redirect (v3)
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ state: query
+ register: query_pbr_policy_v3
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Verify PBR Attributes (v4)
+ ansible.builtin.assert:
+ that:
+ - query_pbr_policy_v4 is not changed
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.destType == "L2"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "sip-dip-prototype"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "80"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "50"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "no"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "no"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "deny"
+ - query_pbr_policy_v4.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "no"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '>=')
+
+- name: Verify PBR Attributes (v3)
+ ansible.builtin.assert:
+ that:
+ - query_pbr_policy_v3 is not changed
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "sip-dip-prototype"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "80"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "50"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "no"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "no"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "deny"
+ - query_pbr_policy_v3.current.0.vnsSvcRedirectPol.attributes.thresholdEnable == "no"
+ when: version.current.0.topSystem.attributes.version is version('4.2', '<')
+
+- name: Query all L4-L7 Policy Based Redirect Policies
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ state: query
+ register: query_pbr_policy_all
+
+- name: Verify PBR Attributes are unchanged
+ ansible.builtin.assert:
+ that:
+ - query_pbr_policy_all is not changed
+
+# DELETE L4-L7 POLICY BASED REDIRECT POLICY
+- name: Delete L4-L7 Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ state: absent
+ register: delete_pbr_policy
+
+- name: Confirm PBR Policy Removal
+ ansible.builtin.assert:
+ that:
+ - delete_pbr_policy is changed
+ - delete_pbr_policy.current == []
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.name == "ansible_pbr_policy"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.AnycastEnabled == "no"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.hashingAlgorithm == "sip-dip-prototype"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.maxThresholdPercent == "80"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.minThresholdPercent == "50"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.programLocalPodOnly == "no"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.resilientHashEnabled == "no"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.thresholdDownAction == "deny"
+ - delete_pbr_policy.previous.0.vnsSvcRedirectPol.attributes.thresholdEnable == "no"
+
+# DELETE L4-L7 POLICY BASED REDIRECT POLICY AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Policy Based Redirect again
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ state: absent
+ register: delete_pbr_policy_again
+
+- name: Confirm PBR Policy Removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_pbr_policy_again is not changed
+ - delete_pbr_policy_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_policy_based_redirect_dest/aliases b/tests/integration/targets/aci_l4l7_policy_based_redirect_dest/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_policy_based_redirect_dest/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_policy_based_redirect_dest/tasks/main.yml b/tests/integration/targets/aci_l4l7_policy_based_redirect_dest/tasks/main.yml
new file mode 100644
index 000000000..e6b949f92
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_policy_based_redirect_dest/tasks/main.yml
@@ -0,0 +1,519 @@
+# 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
+
+# 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
+
+# ADD L4-L7 CONCRETE DEVICE
+- name: Create L4-L7 Concrete Device
+ cisco.aci.aci_l4l7_concrete_device:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ state: present
+
+# ADD L4-L7 CONCRETE INTERFACE
+- name: Create L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/12
+ state: present
+
+- name: Create second L4-L7 Concrete Interface
+ cisco.aci.aci_l4l7_concrete_interface:
+ <<: *aci_info
+ tenant: ansible_tenant
+ device: ansible_device
+ concrete_device: ansible_concrete_device
+ concrete_interface: ansible_second_concrete_interface
+ pod_id: 1
+ node_id: 201
+ path_ep: eth1/13
+ state: present
+
+# CREATE L4-L7 POLICY BASED REDIRECT
+- name: Create L4-L7 Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_policy
+ hash_algorithm: destination_ip
+ resilient_hash: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+
+# CREATE L4-L7 L2 POLICY BASED REDIRECT
+- name: Create L4-L7 L2 Policy Based Redirect
+ cisco.aci.aci_l4l7_policy_based_redirect:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy_name: ansible_pbr_l2_policy
+ hash_algorithm: destination_ip
+ dest_type: l2
+ resilient_hash: true
+ min_threshold: 60
+ max_threshold: 90
+ threshold_enable: true
+ threshold_down_action: permit
+ pod_aware: true
+ anycast_enabled: false
+ state: present
+
+# CREATE HEALTH GROUP
+- name: Add a new Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: present
+
+# CREATE L4-L7 PBR DESTINATION
+- name: Create L4-L7 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ additional_ip: 192.168.50.1
+ redirect_mac: AB:CD:EF:12:34:56
+ dest_name: redirect_dest
+ health_group: ansible_health_group
+ pod_id: 1
+ state: present
+ register: add_pbr_dest
+
+- name: Create L4-L7 L1/L2 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ health_group: ansible_health_group
+ pod_id: 1
+ state: present
+ register: add_pbr_l1l2_dest
+
+- name: Create Second L4-L7 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.30.1
+ redirect_mac: AB:CD:EF:12:34:60
+ dest_name: second_redirect_dest
+ pod_id: 1
+ state: present
+
+- name: Create Second L4-L7 L1/L2 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:61
+ dest_name: second_l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_second_concrete_interface
+ pod_id: 1
+ state: present
+
+- name: Verify L4-L7 Policy Based Redirect Destination creation
+ ansible.builtin.assert:
+ that:
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy/RedirectDest_ip-[192.168.10.1]"
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.ip == "192.168.10.1"
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.ip2 == "192.168.50.1"
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.mac == "AB:CD:EF:12:34:56"
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.destName == "redirect_dest"
+ - add_pbr_dest.current.0.vnsRedirectDest.attributes.podId == "1"
+ - add_pbr_dest.current.0.vnsRedirectDest.children.0.vnsRsRedirectHealthGroup.attributes.tDn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_l2_policy/L1L2RedirectDest-l1l2_redirect_dest"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.mac == "AB:CD:EF:12:34:57"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.destName == "l1l2_redirect_dest"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.podId == "1"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.children.0.vnsRsToCIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - add_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.children.1.vnsRsL1L2RedirectHealthGroup.attributes.tDn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+
+# ADD L4-L7 PBR DESTINATION AGAIN TO TEST IDEMPOTENCE
+- name: Add L4-L7 Policy Based Redirect Destination again
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ additional_ip: 192.168.50.1
+ redirect_mac: AB:CD:EF:12:34:56
+ dest_name: redirect_dest
+ health_group: ansible_health_group
+ pod_id: 1
+ state: present
+ register: add_pbr_dest_again
+
+- name: Add L4-L7 L1/L2 Policy Based Redirect Destination again
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ health_group: ansible_health_group
+ pod_id: 1
+ state: present
+ register: add_pbr_l1l2_dest_again
+
+- name: Verify L4-L7 Policy Based Redirect Destination
+ ansible.builtin.assert:
+ that:
+ - add_pbr_dest_again is not changed
+ - add_pbr_l1l2_dest_again is not changed
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy/RedirectDest_ip-[192.168.10.1]"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.ip == "192.168.10.1"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.ip2 == "192.168.50.1"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.mac == "AB:CD:EF:12:34:56"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.destName == "redirect_dest"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.attributes.podId == "1"
+ - add_pbr_dest_again.current.0.vnsRedirectDest.children.0.vnsRsRedirectHealthGroup.attributes.tDn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_l2_policy/L1L2RedirectDest-l1l2_redirect_dest"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.attributes.mac == "AB:CD:EF:12:34:57"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.attributes.destName == "l1l2_redirect_dest"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.attributes.podId == "1"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.children.0.vnsRsToCIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - add_pbr_l1l2_dest_again.current.0.vnsL1L2RedirectDest.children.1.vnsRsL1L2RedirectHealthGroup.attributes.tDn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+
+# TEST ERROR CHECKING
+- name: Add L4-L7 L1/L2 Policy Based Redirect Destination with ip
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ health_group: ansible_health_group
+ pod_id: 1
+ ip: 10.20.30.40
+ state: present
+ register: add_pbr_l1l2_ip
+ ignore_errors: true
+
+- name: Add L4-L7 L1/L2 Policy Based Redirect Destination with additional ip
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ health_group: ansible_health_group
+ pod_id: 1
+ additional_ip: 10.20.30.40
+ state: present
+ register: add_pbr_l1l2_add_ip
+ ignore_errors: true
+
+- name: Add L4-L7 L3 Policy Based Redirect Destination with redirect interface
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l3_redirect_dest
+ dest_type: l3
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ health_group: ansible_health_group
+ pod_id: 1
+ ip: 10.20.30.40
+ state: present
+ register: add_pbr_l3_redirect_intf
+ ignore_errors: true
+
+- name: Add L4-L7 L1/L2 Policy Based Redirect Destination with no redirect interface
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ health_group: ansible_health_group
+ pod_id: 1
+ state: present
+ register: add_pbr_l1l2_no_redirect_intf
+ ignore_errors: true
+
+- name: Validate error messages
+ ansible.builtin.assert:
+ that:
+ - add_pbr_l1l2_ip is failed
+ - add_pbr_l1l2_ip.msg == "You cannot provide an ip when configuring an l1/l2 destination"
+ - add_pbr_l1l2_add_ip is failed
+ - add_pbr_l1l2_add_ip.msg == "You cannot provide an additional_ip when configuring an l1/l2 destination"
+ - add_pbr_l3_redirect_intf is failed
+ - add_pbr_l3_redirect_intf.msg == "You cannot provide a logical_dev, concrete_dev or concrete_intf when configuring an l3 destination"
+ - add_pbr_l1l2_no_redirect_intf is failed
+ - add_pbr_l1l2_no_redirect_intf.msg == "You must provide a logical_dev, concrete_dev and concrete_intf when configuring an l1/l2 destination"
+
+# MODIFY L4-L7 PBR DESTINATION
+- name: Modify L4-L7 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ redirect_mac: AB:CD:EF:12:34:57
+ dest_name: updated_redirect_dest
+ pod_id: 1
+ state: present
+ register: update_pbr_dest
+
+- name: Modify L4-L7 L1/L2 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ redirect_mac: AB:CD:EF:12:34:58
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ logical_dev: ansible_device
+ concrete_dev: ansible_concrete_device
+ concrete_intf: ansible_concrete_interface
+ pod_id: 1
+ state: present
+ register: update_pbr_l1l2_dest
+
+- name: Verify L4-L7 Policy Based Redirect Destination update, including removal of health group bindings
+ ansible.builtin.assert:
+ that:
+ - update_pbr_dest is changed
+ - update_pbr_dest.current.0.vnsRedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy/RedirectDest_ip-[192.168.10.1]"
+ - update_pbr_dest.current.0.vnsRedirectDest.attributes.ip == "192.168.10.1"
+ - update_pbr_dest.current.0.vnsRedirectDest.attributes.mac == "AB:CD:EF:12:34:57"
+ - update_pbr_dest.current.0.vnsRedirectDest.attributes.destName == "updated_redirect_dest"
+ - update_pbr_dest.current.0.vnsRedirectDest.attributes.podId == "1"
+ - update_pbr_dest.current.0.vnsRedirectDest.children is not defined
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_l2_policy/L1L2RedirectDest-l1l2_redirect_dest"
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.mac == "AB:CD:EF:12:34:58"
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.destName == "l1l2_redirect_dest"
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.attributes.podId == "1"
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.children.0.vnsRsToCIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+ - update_pbr_l1l2_dest.current.0.vnsL1L2RedirectDest.children | length == 1
+
+# QUERY L4-L7 PBR DESTINATION
+- name: Query L4-L7 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ state: query
+ register: query_pbr_dest
+
+- name: Query L4-L7 Policy Based Redirect L1/L2 Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ state: query
+ register: query_l1l2_pbr_dest
+
+- name: Verify L4-L7 Policy Based Redirect Destination query
+ ansible.builtin.assert:
+ that:
+ - query_pbr_dest is not changed
+ - query_l1l2_pbr_dest is not changed
+ - query_pbr_dest.current.0.vnsRedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy/RedirectDest_ip-[192.168.10.1]"
+ - query_pbr_dest.current.0.vnsRedirectDest.attributes.ip == "192.168.10.1"
+ - query_pbr_dest.current.0.vnsRedirectDest.attributes.mac == "AB:CD:EF:12:34:57"
+ - query_pbr_dest.current.0.vnsRedirectDest.attributes.destName == "updated_redirect_dest"
+ - query_pbr_dest.current.0.vnsRedirectDest.attributes.podId == "1"
+ - query_l1l2_pbr_dest.current.0.vnsL1L2RedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_l2_policy/L1L2RedirectDest-l1l2_redirect_dest"
+ - query_l1l2_pbr_dest.current.0.vnsL1L2RedirectDest.attributes.mac == "AB:CD:EF:12:34:58"
+ - query_l1l2_pbr_dest.current.0.vnsL1L2RedirectDest.attributes.destName == "l1l2_redirect_dest"
+ - query_l1l2_pbr_dest.current.0.vnsL1L2RedirectDest.attributes.podId == "1"
+ - query_l1l2_pbr_dest.current.0.vnsL1L2RedirectDest.children.0.vnsRsToCIf.attributes.tDn == "uni/tn-ansible_tenant/lDevVip-ansible_device/cDev-ansible_concrete_device/cIf-[ansible_concrete_interface]"
+
+- name: Query All L4-L7 Policy Based Redirect L3 Destinations
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ state: query
+ register: query_pbr_dest_all
+
+- name: Query All L4-L7 Policy Based Redirect L1/L2 Destinations
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ state: query
+ register: query_pbr_l1l2_dest_all
+
+- name: Verify query_pbr_dest_all
+ ansible.builtin.assert:
+ that:
+ - query_pbr_dest_all is not changed
+ - query_pbr_l1l2_dest_all is not changed
+ - query_pbr_dest_all.current | length > 1
+ - query_pbr_l1l2_dest_all.current | length > 1
+
+# DELETE L4-L7 PBR DESTINATION
+- name: Delete L4-L7 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ state: absent
+ register: delete_pbr_dest
+
+- name: Delete L4-L7 L1/L2 Policy Based Redirect Destination
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ state: absent
+ register: delete_pbr_l1l2_dest
+
+- name: Verify L4-L7 Policy Based Redirect Destination deletion
+ ansible.builtin.assert:
+ that:
+ - delete_pbr_dest is changed
+ - delete_pbr_dest.current == []
+ - delete_pbr_l1l2_dest is changed
+ - delete_pbr_l1l2_dest.current == []
+ - delete_pbr_dest.previous.0.vnsRedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_policy/RedirectDest_ip-[192.168.10.1]"
+ - delete_pbr_dest.previous.0.vnsRedirectDest.attributes.ip == "192.168.10.1"
+ - delete_pbr_dest.previous.0.vnsRedirectDest.attributes.mac == "AB:CD:EF:12:34:57"
+ - delete_pbr_dest.previous.0.vnsRedirectDest.attributes.destName == "updated_redirect_dest"
+ - delete_pbr_dest.previous.0.vnsRedirectDest.attributes.podId == "1"
+ - delete_pbr_l1l2_dest.previous.0.vnsL1L2RedirectDest.attributes.dn == "uni/tn-ansible_tenant/svcCont/svcRedirectPol-ansible_pbr_l2_policy/L1L2RedirectDest-l1l2_redirect_dest"
+ - delete_pbr_l1l2_dest.previous.0.vnsL1L2RedirectDest.attributes.mac == "AB:CD:EF:12:34:58"
+ - delete_pbr_l1l2_dest.previous.0.vnsL1L2RedirectDest.attributes.destName == "l1l2_redirect_dest"
+ - delete_pbr_l1l2_dest.previous.0.vnsL1L2RedirectDest.attributes.podId == "1"
+
+
+# DELETE L4-L7 PBR DESTINATION AGAIN TO TEST IDEMPOTENCE
+- name: Delete L4-L7 Policy Based Redirect Destination again
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_policy
+ redirect_ip: 192.168.10.1
+ state: absent
+ register: delete_pbr_dest_again
+
+- name: Delete L4-L7 L1/L2 Policy Based Redirect Destination again
+ cisco.aci.aci_l4l7_policy_based_redirect_dest:
+ <<: *aci_info
+ tenant: ansible_tenant
+ policy: ansible_pbr_l2_policy
+ dest_name: l1l2_redirect_dest
+ dest_type: l1/l2
+ state: absent
+ register: delete_pbr_l1l2_dest_again
+
+- name: Verify L4-L7 Policy Based Redirect Destination deletion idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_pbr_dest_again is not changed
+ - delete_pbr_dest_again.current == []
+ - delete_pbr_l1l2_dest_again is not changed
+ - delete_pbr_l1l2_dest_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
\ No newline at end of file
diff --git a/tests/integration/targets/aci_l4l7_redirect_health_group/aliases b/tests/integration/targets/aci_l4l7_redirect_health_group/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_redirect_health_group/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_redirect_health_group/tasks/main.yml b/tests/integration/targets/aci_l4l7_redirect_health_group/tasks/main.yml
new file mode 100644
index 000000000..e7c21c039
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_redirect_health_group/tasks/main.yml
@@ -0,0 +1,121 @@
+# 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: Remove ansible_tenant if it already exists
+ cisco.aci.aci_tenant:
+ <<: *aci_info
+ name: ansible_tenant
+ state: present
+
+# CREATE REDIRECT HEALTH GROUP
+- name: Add a new Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: present
+ register: add_health_group
+
+- name: Verify L4-L7 Health Group
+ ansible.builtin.assert:
+ that:
+ - add_health_group.current.0.vnsRedirectHealthGroup.attributes.dn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - add_health_group.current.0.vnsRedirectHealthGroup.attributes.name == "ansible_health_group"
+
+# CREATE REDIRECT HEALTH GROUP AGAIN TO TEST IDEMPOTENCE
+- name: Add a new Redirect Health Group again
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: present
+ register: add_health_group_again
+
+- name: Verify L4-L7 Health Group idempotence
+ ansible.builtin.assert:
+ that:
+ - add_health_group_again is not changed
+ - add_health_group_again.current.0.vnsRedirectHealthGroup.attributes.dn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - add_health_group_again.current.0.vnsRedirectHealthGroup.attributes.name == "ansible_health_group"
+
+# QUERY REDIRECT HEALTH GROUP
+- name: Query Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: query
+ register: query_health_group
+
+- name: Verify L4-L7 Health Group query
+ ansible.builtin.assert:
+ that:
+ - query_health_group is not changed
+ - query_health_group.current.0.vnsRedirectHealthGroup.attributes.dn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - query_health_group.current.0.vnsRedirectHealthGroup.attributes.name == "ansible_health_group"
+
+# REMOVE L4-L7 HEALTH GROUP
+- name: Remove Redirect Health Group
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: absent
+ register: delete_health_group
+
+- name: Verify L4-L7 Health Group removal
+ ansible.builtin.assert:
+ that:
+ - delete_health_group is changed
+ - delete_health_group.current == []
+ - delete_health_group.previous.0.vnsRedirectHealthGroup.attributes.dn == "uni/tn-ansible_tenant/svcCont/redirectHealthGroup-ansible_health_group"
+ - delete_health_group.previous.0.vnsRedirectHealthGroup.attributes.name == "ansible_health_group"
+
+# REMOVE L4-L7 HEALTH GROUP AGAIN TO TEST IDEMPOTENCE
+- name: Remove Redirect Health Group again
+ cisco.aci.aci_l4l7_redirect_health_group:
+ <<: *aci_info
+ tenant: ansible_tenant
+ health_group: ansible_health_group
+ state: absent
+ register: delete_health_group_again
+
+- name: Verify L4-L7 Health Group removal idempotence
+ ansible.builtin.assert:
+ that:
+ - delete_health_group_again is not changed
+ - delete_health_group_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/aliases b/tests/integration/targets/aci_l4l7_service_graph_template/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
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..77a81efe5
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template/tasks/main.yml
@@ -0,0 +1,132 @@
+# 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_service_graph
+ state: present
+ register: create_sgt
+
+- name: Verify L4-L7 Service Graph Template has been created
+ ansible.builtin.assert:
+ that:
+ - 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"
+
+# ADD service graph template again to check idempotence
+- name: Create L4-L7 Service Graph Template again
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: present
+ register: create_sgt_again
+
+- name: Verify L4-L7 Service Graph Template attributes
+ ansible.builtin.assert:
+ that:
+ - create_sgt_again is not changed
+ - 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"
+
+# QUERY service graph template
+- 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
+
+# DELETE service graph template
+- name: Remove L4-L7 Service Graph Template
+ cisco.aci.aci_l4l7_service_graph_template:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: absent
+ register: remove_sgt
+
+- name: Verify L4-L7 Service Graph Template deletion
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_service_graph
+ state: absent
+ 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_conn/aliases b/tests/integration/targets/aci_l4l7_service_graph_template_abs_conn/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_conn/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_abs_conn/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_abs_conn/tasks/main.yml
new file mode 100644
index 000000000..993f60eb0
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_conn/tasks/main.yml
@@ -0,0 +1,203 @@
+# 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
+ 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
+ register: add_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION
+- name: Verify Connection Attributes
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ direct_connect: true
+ unicast_route: true
+ adjacency_type: l3
+ state: present
+ 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
+ cisco.aci.aci_l4l7_service_graph_template_abs_conn:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ state: absent
+ register: delete_l4l7_abs_conn
+
+# VERIFY SERVICE GRAPH ABS CONNECTION DELETION
+- name: Verify Connection Deletion
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ connection_name: C1
+ state: absent
+ 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/aliases b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
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..8b27fe4aa
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_abs_connection_conns/tasks/main.yml
@@ -0,0 +1,695 @@
+# 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
+ 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: present
+ register: add_c1_node_conn
+
+- name: Add 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: present
+ register: add_c1_term_conn
+
+- name: Add 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: present
+ register: add_c2_prov_conn
+
+- name: Add 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: present
+ register: add_c2_cons_conn
+
+- name: Add 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: present
+ register: add_c3_node_conn
+
+- name: Add 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: present
+ register: add_c3_term_conn
+
+# VERIFY CONNECTION ATTRIBUTES
+- name: Verify C1 Node Connection
+ ansible.builtin.assert:
+ that:
+ - 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.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.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.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.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.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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ connected_node: ansible_node1
+ state: present
+ register: add_c1_node_conn_again
+
+- name: Add C1 term connection again
+ 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: present
+ register: add_c1_term_conn_again
+
+- name: Add C2 provider connection again
+ 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: present
+ register: add_c2_prov_conn_again
+
+- name: Add C2 consumer connection again
+ 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: present
+ register: add_c2_cons_conn_again
+
+- name: Add C3 node connection again
+ 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: present
+ register: add_c3_node_conn_again
+
+- name: Add C3 term connection again
+ 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: present
+ 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
+- name: Delete 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: absent
+ register: delete_c1_node_conn
+
+- name: Delete 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: absent
+ register: delete_c1_term_conn
+
+- name: Delete 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: absent
+ register: delete_c2_prov_conn
+
+- name: Delete 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: absent
+ register: delete_c2_cons_conn
+
+- name: Delete 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: absent
+ register: delete_c3_node_conn
+
+- name: Delete 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: absent
+ register: delete_c3_term_conn
+
+- name: Verify C1 Node Connection Removal
+ ansible.builtin.assert:
+ that:
+ - 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 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 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 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 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 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ direction: consumer
+ connection_name: C1
+ connected_node: ansible_node1
+ state: absent
+ register: delete_c1_node_conn_again
+
+- name: Delete C1 term connection again
+ 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: absent
+ register: delete_c1_term_conn_again
+
+- name: Delete C2 provider connection again
+ 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: absent
+ register: delete_c2_prov_conn_again
+
+- name: Delete C2 consumer connection again
+ 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: absent
+ register: delete_c2_cons_conn_again
+
+- name: Delete C3 node connection again
+ 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: absent
+ register: delete_c3_node_conn_again
+
+- name: Delete C3 term connection again
+ 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: absent
+ 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_func_conn/aliases b/tests/integration/targets/aci_l4l7_service_graph_template_func_conn/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_func_conn/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
diff --git a/tests/integration/targets/aci_l4l7_service_graph_template_func_conn/tasks/main.yml b/tests/integration/targets/aci_l4l7_service_graph_template_func_conn/tasks/main.yml
new file mode 100644
index 000000000..507e7bd1a
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_func_conn/tasks/main.yml
@@ -0,0 +1,262 @@
+# 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
+ 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
+ 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: present
+ register: add_cons_func_conn
+
+- name: Create 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: present
+ register: add_prov_func_conn
+
+# VERIFY ATTRIBUTES
+- name: Verify Consumer Func Conn Creation
+ ansible.builtin.assert:
+ that:
+ - 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.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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: consumer
+ state: present
+ register: add_cons_func_conn_again
+
+- name: Add Provider Func Conn again
+ 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: present
+ 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"
+
+# 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
+
+# DELETE FUNC CONNS
+- name: Delete 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: absent
+ register: delete_cons_func_conn
+
+- name: Delete 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: absent
+ register: delete_prov_func_conn
+
+# VERIFY ATTRIBUTES
+- name: Verify Consumer Func Conn Removal
+ ansible.builtin.assert:
+ that:
+ - 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 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node: ansible_node
+ connection_name: consumer
+ state: absent
+ 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/aliases b/tests/integration/targets/aci_l4l7_service_graph_template_node/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_node/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
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..56cd79132
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_node/tasks/main.yml
@@ -0,0 +1,250 @@
+# 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
+ 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
+ register: add_l4l7_node
+
+# VERIFY NODE CREATION
+- name: Verify Node has been created correctly
+ ansible.builtin.assert:
+ that:
+ - 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"
+
+# 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: Add Node again to check idempotence
+ 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
+ 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.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 is not changed
+ - 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.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:
+ - add_l4l7_node_again is not changed
+ - add_l4l7_node_again.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
+ 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
+
+# VERIFY NODE REMOVAL
+- name: Verify Node removal
+ ansible.builtin.assert:
+ that:
+ - 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/aliases b/tests/integration/targets/aci_l4l7_service_graph_template_term_node/aliases
new file mode 100644
index 000000000..209b793f9
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_term_node/aliases
@@ -0,0 +1,2 @@
+# No ACI simulator yet, so not enabled
+# unsupported
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..aab769e76
--- /dev/null
+++ b/tests/integration/targets/aci_l4l7_service_graph_template_term_node/tasks/main.yml
@@ -0,0 +1,211 @@
+# 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 TERM NODE
+- name: Create 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: present
+ register: add_l4l7_term_node
+
+# VERIFY TERM NODE CREATION
+- name: Verify Term Node Creation
+ ansible.builtin.assert:
+ that:
+ - 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:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T1
+ state: present
+ 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
+ cisco.aci.aci_l4l7_service_graph_template_term_node:
+ <<: *aci_info
+ tenant: ansible_tenant
+ service_graph: ansible_graph
+ node_name: T1
+ state: absent
+ register: delete_l4l7_term_node
+
+# VERIFY DELETION
+- name: Verify Term Node attributes
+ ansible.builtin.assert:
+ that:
+ - delete_l4l7_term_node is changed
+ - delete_l4l7_term_node.current == []
+ - 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"
+
+# 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