diff --git a/plugins/module_utils/constants.py b/plugins/module_utils/constants.py index aa4c13ae6..b6ef1236f 100644 --- a/plugins/module_utils/constants.py +++ b/plugins/module_utils/constants.py @@ -92,3 +92,5 @@ MATCH_BEST_PATH_CONTROL_MAPPING = dict(enable="asPathMultipathRelax", disable="") MATCH_GRACEFUL_RESTART_CONTROLS_MAPPING = dict(helper="helper", complete="") + +EP_LOOP_PROTECTION_ACTION_MAPPING = {"bd": "bd-learn-disable", "port": "port-disable"} diff --git a/plugins/modules/aci_system_endpoint_controls.py b/plugins/modules/aci_system_endpoint_controls.py new file mode 100644 index 000000000..7da8afb3f --- /dev/null +++ b/plugins/modules/aci_system_endpoint_controls.py @@ -0,0 +1,338 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Tim Cragg (@timcragg) +# Copyright: (c) 2023, Akini Ross (@akinross) +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = {"metadata_version": "1.1", "status": ["preview"], "supported_by": "certified"} + +DOCUMENTATION = r""" +--- +module: aci_system_endpoint_controls +short_description: Manage System Endpoint Controls (ep:IpAgingP, ep:ControlP, and ep:LoopProtectP) +description: +- Manage System Endpoint Controls on Cisco ACI fabrics. +options: + ip_aging: + description: Configuration container for IP Aging. + type: dict + suboptions: + admin_state: + description: + - Whether to enable IP Aging Controls on the fabric. + type: bool + roque_ep_control: + description: Configuration container for Rogue EP Control. + type: dict + suboptions: + admin_state: + description: + - Whether to enable Rogue EP Control on the fabric. + type: bool + interval: + description: + - The rogue endpoint detection interval in seconds. + type: int + multiplication_factor: + description: + - The rogue endpoint detection multiplication factor. + type: int + hold_interval: + description: + - The rogue endpoint hold interval in seconds. + type: int + ep_loop_protection: + description: Configuration container for EP Loop Protection. + type: dict + suboptions: + admin_state: + description: + - Whether to enable EP Loop Protection on the fabric. + type: bool + interval: + description: + - The loop protection detection interval in seconds. + type: int + multiplication_factor: + description: + - The loop protection detection multiplication factor. + type: int + action: + description: + - The action(s) to take when a loop is detected. + type: list + elements: str + choices: [ bd, port ] + state: + description: + - Use C(present) for updating configuration. + - Use C(query) for showing current configuration. + type: str + choices: [ 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(ep:IpAgingP), B(ep:ControlP), and B(ep:LoopProtectP). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Tim Cragg (@timcragg) +- Akini Ross (@akinross) +""" + +EXAMPLES = r""" +- name: Set system endpoint controls settings + cisco.aci.aci_system_endpoint_controls: + host: apic + username: admin + password: SomeSecretPassword + admin_state: true + ip_aging: + admin_state: true + roque_ep_control: + admin_state: true + interval: 50 + multiplication_factor: 10 + hold_interval: 2000 + ep_loop_protection: + admin_state: true + interval: 70 + multiplication_factor: 15 + action: [ bd, port ] + delegate_to: localhost + +- name: Query system endpoint controls settings + cisco.aci.aci_system_endpoint_controls: + 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 EP_LOOP_PROTECTION_ACTION_MAPPING + + +def main(): + argument_spec = aci_argument_spec() + argument_spec.update(aci_annotation_spec()) + argument_spec.update(aci_owner_spec()) + argument_spec.update( + ip_aging=dict(type="dict", options=dict(admin_state=dict(type="bool"))), + roque_ep_control=dict( + type="dict", + options=dict( + admin_state=dict(type="bool"), + interval=dict(type="int"), + multiplication_factor=dict(type="int"), + hold_interval=dict(type="int"), + ), + ), + ep_loop_protection=dict( + type="dict", + options=dict( + admin_state=dict(type="bool"), + interval=dict(type="int"), + multiplication_factor=dict(type="int"), + action=dict(type="list", elements="str", choices=["bd", "port"]), + ), + ), + state=dict(type="str", default="present", choices=["present", "query"]), + ) + + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + required_if=[["state", "present", ["ip_aging", "roque_ep_control", "ep_loop_protection"], True]], + ) + + aci = ACIModule(module) + state = module.params.get("state") + + aci.construct_url( + root_class=dict( + aci_class="infraInfra", + aci_rn="infra", + ), + child_classes=["epIpAgingP", "epControlP", "epLoopProtectP"], + ) + + aci.get_existing() + + if state == "present": + child_configs = [] + ip_aging = module.params.get("ip_aging") + roque_ep_control = module.params.get("roque_ep_control") + ep_loop_protection = module.params.get("ep_loop_protection") + + if ip_aging: + child_configs.append( + {"epIpAgingP": {"attributes": {"name": "default", "adminSt": aci.boolean(ip_aging.get("admin_state"), "enabled", "disabled")}}} + ) + + if roque_ep_control: + child_configs.append( + { + "epControlP": { + "attributes": { + "name": "default", + "adminSt": aci.boolean(roque_ep_control.get("admin_state"), "enabled", "disabled"), + "rogueEpDetectIntvl": roque_ep_control.get("interval"), + "rogueEpDetectMult": roque_ep_control.get("multiplication_factor"), + "holdIntvl": roque_ep_control.get("hold_interval"), + } + } + } + ) + + if ep_loop_protection: + actions = None + if ep_loop_protection.get("action"): + actions = ",".join(sorted([EP_LOOP_PROTECTION_ACTION_MAPPING.get(action) for action in ep_loop_protection.get("action")])) + child_configs.append( + { + "epLoopProtectP": { + "attributes": { + "name": "default", + "adminSt": aci.boolean(ep_loop_protection.get("admin_state"), "enabled", "disabled"), + "loopDetectIntvl": ep_loop_protection.get("interval"), + "loopDetectMult": ep_loop_protection.get("multiplication_factor"), + "action": actions, + } + } + } + ) + + aci.payload( + aci_class="infraInfra", + class_config=dict(), + child_configs=child_configs, + ) + + aci.get_diff(aci_class="infraInfra") + + aci.post_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/tests/integration/targets/aci_system_endpoint_controls/aliases b/tests/integration/targets/aci_system_endpoint_controls/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_system_endpoint_controls/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_system_endpoint_controls/tasks/main.yml b/tests/integration/targets/aci_system_endpoint_controls/tasks/main.yml new file mode 100644 index 000000000..fa98657f6 --- /dev/null +++ b/tests/integration/targets/aci_system_endpoint_controls/tasks/main.yml @@ -0,0 +1,307 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, 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 + +- 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: Verify Cloud and Non-Cloud Sites in use. + include_tasks: ../../../../../../integration/targets/aci_cloud_provider/tasks/main.yml + +- name: Execute tasks only for non-cloud sites + when: query_cloud.current == [] # This condition will skip execution for cloud sites + block: + + # CONFIGURE SYSTEM ENDPOINT CONTROLS SETTINGS TO DEFAULT + - name: Set system endpoint controls settings to default prior to test + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + ip_aging: + admin_state: false + roque_ep_control: + admin_state: false + interval: 60 + multiplication_factor: 4 + hold_interval: 1800 + ep_loop_protection: + admin_state: false + interval: 60 + multiplication_factor: 4 + action: [ port ] + + # CONFIGURE SYSTEM ENDPOINT CONTROLS SETTINGS + - name: Set system endpoint controls settings (check_mode) + cisco.aci.aci_system_endpoint_controls: &endpoint_controls + <<: *aci_info + ip_aging: + admin_state: true + roque_ep_control: + admin_state: true + interval: 50 + multiplication_factor: 10 + hold_interval: 2000 + ep_loop_protection: + admin_state: true + interval: 70 + multiplication_factor: 15 + action: [ bd, port ] + check_mode: true + register: cm_set_endpoint_controls + + - name: Set system endpoint controls settings + cisco.aci.aci_system_endpoint_controls: + <<: *endpoint_controls + register: nm_set_endpoint_controls + + - name: Set system endpoint controls settings again + cisco.aci.aci_system_endpoint_controls: + <<: *endpoint_controls + register: nm_set_endpoint_controls_again + + - name: Verify set system endpoint controls + ansible.builtin.assert: + that: + - cm_set_endpoint_controls is changed + - cm_set_endpoint_controls.proposed.infraInfra.children.0.epIpAgingP.attributes.adminSt == "enabled" + - cm_set_endpoint_controls.proposed.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - cm_set_endpoint_controls.proposed.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - cm_set_endpoint_controls.proposed.infraInfra.children.1.epControlP.attributes.name == "default" + - cm_set_endpoint_controls.proposed.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - cm_set_endpoint_controls.proposed.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - cm_set_endpoint_controls.proposed.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - cm_set_endpoint_controls.proposed.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - cm_set_endpoint_controls.proposed.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - cm_set_endpoint_controls.proposed.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - cm_set_endpoint_controls.proposed.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - cm_set_endpoint_controls.proposed.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_set_endpoint_controls is changed + - nm_set_endpoint_controls.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_set_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_set_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_set_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_set_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_set_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_set_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_set_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_set_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_set_endpoint_controls_again is not changed + - nm_set_endpoint_controls_again.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_set_endpoint_controls_again.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_set_endpoint_controls_again.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + + - name: Set ip aging system endpoint controls settings to default + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + ip_aging: + admin_state: false + register: nm_ip_aging + + - name: Set roque ep control system endpoint controls settings to default + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + roque_ep_control: + admin_state: false + interval: 60 + multiplication_factor: 4 + hold_interval: 1800 + register: nm_roque_ep_control + + - name: Set ep loop protection system endpoint controls settings without action to default + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + ep_loop_protection: + admin_state: false + interval: 60 + multiplication_factor: 4 + register: nm_ep_loop_protection_no_action + + - name: Set ep loop protection system endpoint controls settings action to default + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + ep_loop_protection: + action: port + register: nm_ep_loop_protection_action + + - name: Set system endpoint controls settings without config in config containers + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + ip_aging: + roque_ep_control: + ep_loop_protection: + register: nm_no_config_in_config_containers + + - name: Set system endpoint controls settings without config (error) + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + register: err_no_config + ignore_errors: true + + - name: Verify individual set system endpoint controls and error + ansible.builtin.assert: + that: + - nm_ip_aging is changed + - nm_ip_aging.previous.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "enabled" + - nm_ip_aging.previous.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ip_aging.previous.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_ip_aging.previous.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ip_aging.previous.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_ip_aging.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_ip_aging.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_ip_aging.previous.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_ip_aging.previous.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ip_aging.previous.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_ip_aging.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_ip_aging.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_ip_aging.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_ip_aging.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ip_aging.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_ip_aging.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ip_aging.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_ip_aging.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_ip_aging.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_ip_aging.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_ip_aging.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ip_aging.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_ip_aging.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_ip_aging.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_roque_ep_control is changed + - nm_roque_ep_control.previous.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_roque_ep_control.previous.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_roque_ep_control.previous.0.infraInfra.children.1.epControlP.attributes.adminSt == "enabled" + - nm_roque_ep_control.previous.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_roque_ep_control.previous.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "2000" + - nm_roque_ep_control.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "50" + - nm_roque_ep_control.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "10" + - nm_roque_ep_control.previous.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_roque_ep_control.previous.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_roque_ep_control.previous.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_roque_ep_control.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_roque_ep_control.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_roque_ep_control.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_roque_ep_control.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_roque_ep_control.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - nm_roque_ep_control.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_roque_ep_control.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - nm_roque_ep_control.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - nm_roque_ep_control.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - nm_roque_ep_control.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_roque_ep_control.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_roque_ep_control.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_roque_ep_control.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_roque_ep_control.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_ep_loop_protection_no_action is changed + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "enabled" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "70" + - nm_ep_loop_protection_no_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "15" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "60" + - nm_ep_loop_protection_no_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "4" + - nm_ep_loop_protection_action is changed + - nm_ep_loop_protection_action.previous.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.action == "bd-learn-disable,port-disable" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "60" + - nm_ep_loop_protection_action.previous.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "4" + - nm_ep_loop_protection_action.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - nm_ep_loop_protection_action.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - nm_ep_loop_protection_action.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - nm_ep_loop_protection_action.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - nm_ep_loop_protection_action.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - nm_ep_loop_protection_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "disabled" + - nm_ep_loop_protection_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - nm_ep_loop_protection_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "port-disable" + - nm_ep_loop_protection_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "60" + - nm_ep_loop_protection_action.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "4" + - nm_no_config_in_config_containers is not changed + - err_no_config is not changed + - err_no_config is failed + - err_no_config.msg == "state is present but any of the following are missing{{":"}} ip_aging, roque_ep_control, ep_loop_protection" + + # QUERY SYSTEM ENDPOINT CONTROLS SETTINGS + - name: Query system endpoint controls settings + cisco.aci.aci_system_endpoint_controls: + <<: *aci_info + state: query + register: query_endpoint_controls + + - name: Verify system endpoint controls query + ansible.builtin.assert: + that: + - query_endpoint_controls is not changed + - query_endpoint_controls.current.0.infraInfra.children.0.epIpAgingP.attributes.adminSt == "disabled" + - query_endpoint_controls.current.0.infraInfra.children.0.epIpAgingP.attributes.name == "default" + - query_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.adminSt == "disabled" + - query_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.name == "default" + - query_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.holdIntvl == "1800" + - query_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectIntvl == "60" + - query_endpoint_controls.current.0.infraInfra.children.1.epControlP.attributes.rogueEpDetectMult == "4" + - query_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.adminSt == "disabled" + - query_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.name == "default" + - query_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.action == "port-disable" + - query_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectIntvl == "60" + - query_endpoint_controls.current.0.infraInfra.children.2.epLoopProtectP.attributes.loopDetectMult == "4"