From 5f39bbbb7f0e7f2ab38d3c0f63bb2f6a1006f8d9 Mon Sep 17 00:00:00 2001 From: Gaspard Micol Date: Fri, 22 Dec 2023 11:38:09 -0500 Subject: [PATCH] [minor_change] Add aci_qos_dot1p_class as a new module and Modify aci_qos_dscp_class and aci_qos_custom_policy modules and test cases --- plugins/modules/aci_qos_custom_policy.py | 18 +- plugins/modules/aci_qos_dot1p_class.py | 347 ++++++++++++++++++ plugins/modules/aci_qos_dscp_class.py | 132 ++----- .../aci_qos_custom_policy/tasks/main.yml | 4 +- .../targets/aci_qos_dot1p_class/aliases | 2 + .../aci_qos_dot1p_class/tasks/main.yml | 167 +++++++++ .../targets/aci_qos_dscp_class/tasks/main.yml | 4 +- 7 files changed, 549 insertions(+), 125 deletions(-) create mode 100644 plugins/modules/aci_qos_dot1p_class.py create mode 100644 tests/integration/targets/aci_qos_dot1p_class/aliases create mode 100644 tests/integration/targets/aci_qos_dot1p_class/tasks/main.yml diff --git a/plugins/modules/aci_qos_custom_policy.py b/plugins/modules/aci_qos_custom_policy.py index a91eb5d2b..6e5ecf35a 100644 --- a/plugins/modules/aci_qos_custom_policy.py +++ b/plugins/modules/aci_qos_custom_policy.py @@ -47,12 +47,12 @@ - cisco.aci.owner notes: -- The C(tenant) must exist before using this module in your playbook. +- The I(tenant) must exist before using this module in your playbook. The M(cisco.aci.aci_tenant) can be used for this. seealso: - module: cisco.aci.aci_tenant - name: APIC Management Information Model reference - description: More information about the internal APIC classes + description: More information about the internal APIC class B(qos:CustomPol). link: https://developer.cisco.com/docs/apic-mim-ref/ author: - Gaspard Micol (@gmicol) @@ -69,36 +69,34 @@ state: present delegate_to: localhost -- name: Delete an QoS Custom Policy +- name: Query a QoS Custom Policy cisco.aci.aci_qos_custom_policy: host: apic username: admin password: SomeSecretPassword tenant: my_tenant qos_custom_policy: my_qos_custom_policy - state: absent + state: query delegate_to: localhost -- name: Query a QoS Custom Policy +- name: Query all QoS Custom Policies in my_tenant cisco.aci.aci_qos_custom_policy: host: apic username: admin password: SomeSecretPassword tenant: my_tenant - qos_custom_policy: my_qos_custom_policy state: query delegate_to: localhost - register: query_result -- name: Query all QoS Custom Policies in my_tenant +- name: Delete a QoS Custom Policy cisco.aci.aci_qos_custom_policy: host: apic username: admin password: SomeSecretPassword tenant: my_tenant - state: query + qos_custom_policy: my_qos_custom_policy + state: absent delegate_to: localhost - register: query_result """ RETURN = r""" diff --git a/plugins/modules/aci_qos_dot1p_class.py b/plugins/modules/aci_qos_dot1p_class.py new file mode 100644 index 000000000..79a7e14c4 --- /dev/null +++ b/plugins/modules/aci_qos_dot1p_class.py @@ -0,0 +1,347 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2023, Gaspard Micol (@gmicol) +# 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_qos_dot1p_class +short_description: Manage QoS Dot1P Class (qos:Dot1PClass) +description: +- Manage Dot1P Class levels for QoS Custom Policies on Cisco ACI fabrics. +- The class level for Dot1P to prioritize the map. +options: + tenant: + description: + - The name of an existing tenant. + type: str + aliases: [ tenant_name ] + qos_custom_policy: + description: + - The name of the QoS Custom Policy. + type: str + aliases: [ qos_custom_policy_name ] + priority: + description: + - The desired QoS class level to be used. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ level1, level2, level3, level4, level5, level6, unspecified ] + aliases: [ prio ] + dot1p_from: + description: + - The Dot1P range starting value. + type: str + choices: [ background, best_effort, excellent_effort, critical_applications, video, voice, internetwork_control, network_control, unspecified ] + dot1p_to: + description: + - The Dot1P range ending value. + type: str + choices: [ background, best_effort, excellent_effort, critical_applications, video, voice, internetwork_control, network_control, unspecified ] + dot1p_target: + description: + - The Dot1P target value. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ AF11, AF12, AF13, AF21, AF22, AF23, AF31, AF32, AF33, AF41, AF42, AF43, CS0, CS1, CS2, CS3, CS4, CS5, CS6, CS7, EF, VA, unspecified ] + aliases: [ target ] + target_cos: + description: + - The target COS to be driven based on the range of input values of Dot1P coming into the fabric. + - The APIC defaults to C(unspecified) when unset during creation. + type: str + choices: [ background, best_effort, excellent_effort, critical_applications, video, voice, internetwork_control, network_control, unspecified ] + 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(qos_custom_policy) must exist before using this module in your playbook. + The M(cisco.aci.aci_tenant) and the M(cisco.aci.aci_qos_custom_policy) can be used for this. +seealso: +- module: cisco.aci.aci_tenant +- module: cisco.aci.aci_qos_custom_policy +- name: APIC Management Information Model reference + description: More information about the internal APIC class B(qos:Dot1PClass). + link: https://developer.cisco.com/docs/apic-mim-ref/ +author: +- Gaspard Micol (@gmicol) +""" + +EXAMPLES = r""" +- name: Add a new QoS dot1P Class + cisco.aci.aci_qos_dot1p_class: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + qos_custom_policy: my_qos_custom_policy + priority: level3 + dot1p_from: best_effort + dot1p_to: excellent_effort + dot1p_target: unspecified + target_cos: unspecified + state: present + delegate_to: localhost + +- name: Query a QoS dot1P Class + cisco.aci.aci_qos_dot1p_class: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + qos_custom_policy: my_qos_custom_policy + dot1p_from: best_effort + dot1p_to: excellent_effort + state: query + delegate_to: localhost + +- name: Query all QoS dot1P Classes in my_qos_custom_policy + cisco.aci.aci_qos_dot1p_class: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + qos_custom_policy: my_qos_custom_policy + state: query + delegate_to: localhost + +- name: Delete a QoS dot1P Class + cisco.aci.aci_qos_dot1p_class: + host: apic + username: admin + password: SomeSecretPassword + tenant: my_tenant + qos_custom_policy: my_qos_custom_policy + dot1p_from: best_effort + dot1p_to: excellent_effort + state: absent + delegate_to: localhost +""" + +RETURN = r""" +current: + description: The existing configuration from the APIC after the module has finished + returned: success + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +error: + description: The error information as returned from the APIC + returned: failure + type: dict + sample: + { + "code": "122", + "text": "unknown managed object class foo" + } +raw: + description: The raw output returned by the APIC REST API (xml or json) + returned: parse error + type: str + sample: '' +sent: + description: The actual/minimal configuration pushed to the APIC + returned: info + type: list + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment" + } + } + } +previous: + description: The original configuration from the APIC before the module has started + returned: info + type: list + sample: + [ + { + "fvTenant": { + "attributes": { + "descr": "Production", + "dn": "uni/tn-production", + "name": "production", + "nameAlias": "", + "ownerKey": "", + "ownerTag": "" + } + } + } + ] +proposed: + description: The assembled configuration from the user-provided parameters + returned: info + type: dict + sample: + { + "fvTenant": { + "attributes": { + "descr": "Production environment", + "name": "production" + } + } + } +filter_string: + description: The filter string used for the request + returned: failure or debug + type: str + sample: ?rsp-prop-include=config-only +method: + description: The HTTP method used for the request to the APIC + returned: failure or debug + type: str + sample: POST +response: + description: The HTTP response from the APIC + returned: failure or debug + type: str + sample: OK (30 bytes) +status: + description: The HTTP status from the APIC + returned: failure or debug + type: int + sample: 200 +url: + description: The HTTP url used for the request to the APIC + returned: failure or debug + type: str + sample: https://10.11.12.13/api/mo/uni/tn-production.json +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.aci.plugins.module_utils.aci import ( + ACIModule, + aci_argument_spec, + aci_annotation_spec, + aci_owner_spec, + aci_contract_dscp_spec, +) +from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_TARGET_COS_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"]), + qos_custom_policy=dict(type="str", aliases=["qos_custom_policy_name"]), + priority=dict( + type="str", + choices=[ + "level1", + "level2", + "level3", + "level4", + "level5", + "level6", + "unspecified", + ], + aliases=["prio"], + ), + dot1p_from=dict(type="str", choices=list(MATCH_TARGET_COS_MAPPING.keys())), + dot1p_to=dict(type="str", choices=list(MATCH_TARGET_COS_MAPPING.keys())), + dot1p_target=aci_contract_dscp_spec(), + target_cos=dict(type="str", choices=list(MATCH_TARGET_COS_MAPPING.keys())), + 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", "qos_custom_policy", "dot1p_from", "dot1p_to"]], + ["state", "present", ["tenant", "qos_custom_policy", "dot1p_from", "dot1p_to"]], + ], + ) + + tenant = module.params.get("tenant") + qos_custom_policy = module.params.get("qos_custom_policy") + priority = module.params.get("priority") + dot1p_from = MATCH_TARGET_COS_MAPPING.get(module.params.get("dot1p_from")) + dot1p_to = MATCH_TARGET_COS_MAPPING.get(module.params.get("dot1p_to")) + dot1p_target = module.params.get("dot1p_target") + target_cos = MATCH_TARGET_COS_MAPPING.get(module.params.get("target_cos")) + state = module.params.get("state") + + 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="qosCustomPol", + aci_rn="qoscustom-{0}".format(qos_custom_policy), + module_object=qos_custom_policy, + target_filter={"name": qos_custom_policy}, + ), + subclass_2=dict( + aci_class="qosDot1PClass", + aci_rn="dot1P-{0}-{1}".format(dot1p_from, dot1p_to), + module_object=qos_custom_policy, + target_filter={"from": dot1p_from, "to": dot1p_to}, + ), + ) + + aci.get_existing() + + if state == "present": + aci.payload( + aci_class="qosDot1PClass", + class_config={ + "prio": priority, + "from": dot1p_from, + "to": dot1p_to, + "target": dot1p_target, + "targetCos": target_cos, + }, + ) + + aci.get_diff(aci_class="qosDot1PClass") + + aci.post_config() + + elif state == "absent": + aci.delete_config() + + aci.exit_json() + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/aci_qos_dscp_class.py b/plugins/modules/aci_qos_dscp_class.py index 66fdcd409..d314c209f 100644 --- a/plugins/modules/aci_qos_dscp_class.py +++ b/plugins/modules/aci_qos_dscp_class.py @@ -71,13 +71,13 @@ - cisco.aci.owner notes: -- The C(tenant) and C(qos_custom_policy) must exist before using this module in your playbook. +- The I(tenant) and I(qos_custom_policy) must exist before using this module in your playbook. The M(cisco.aci.aci_tenant) and the M(cisco.aci.aci_qos_custom_policy) can be used for this. seealso: - module: cisco.aci.aci_tenant - module: cisco.aci.aci_qos_custom_policy - name: APIC Management Information Model reference - description: More information about the internal APIC classes + description: More information about the internal APIC class B(qos:DscpClass). link: https://developer.cisco.com/docs/apic-mim-ref/ author: - Gaspard Micol (@gmicol) @@ -99,7 +99,7 @@ state: present delegate_to: localhost -- name: Delete a QoS DSCP Class +- name: Query a QoS DSCP Class cisco.aci.aci_qos_dscp_class: host: apic username: admin @@ -108,32 +108,30 @@ qos_custom_policy: my_qos_custom_policy dscp_from: AF11 dscp_to: AF21 - state: absent + state: query delegate_to: localhost -- name: Query a QoS DSCP Class +- name: Query all QoS DSCP Classes in my_qos_custom_policy cisco.aci.aci_qos_dscp_class: host: apic username: admin password: SomeSecretPassword tenant: my_tenant qos_custom_policy: my_qos_custom_policy - dscp_from: AF11 - dscp_to: AF21 state: query delegate_to: localhost - register: query_result -- name: Query all QoS DSCP Classes in my_qos_custom_policy +- name: Delete a QoS DSCP Class cisco.aci.aci_qos_dscp_class: host: apic username: admin password: SomeSecretPassword tenant: my_tenant qos_custom_policy: my_qos_custom_policy - state: query + dscp_from: AF11 + dscp_to: AF21 + state: absent delegate_to: localhost - register: query_result """ RETURN = r""" @@ -243,11 +241,18 @@ 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.aci import ( + ACIModule, + aci_argument_spec, + aci_annotation_spec, + aci_owner_spec, + aci_contract_dscp_spec, +) from ansible_collections.cisco.aci.plugins.module_utils.constants import MATCH_TARGET_COS_MAPPING def main(): + new_dscp_spec = dict((k, aci_contract_dscp_spec()[k]) for k in aci_contract_dscp_spec() if k != 'aliases') argument_spec = aci_argument_spec() argument_spec.update(aci_annotation_spec()) argument_spec.update(aci_owner_spec()) @@ -267,105 +272,10 @@ def main(): ], aliases=["prio"], ), - dscp_from=dict( - type="str", - choices=[ - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "EF", - "VA", - "unspecified", - ], - ), - dscp_to=dict( - type="str", - choices=[ - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "EF", - "VA", - "unspecified", - ], - ), - dscp_target=dict( - type="str", - choices=[ - "AF11", - "AF12", - "AF13", - "AF21", - "AF22", - "AF23", - "AF31", - "AF32", - "AF33", - "AF41", - "AF42", - "AF43", - "CS0", - "CS1", - "CS2", - "CS3", - "CS4", - "CS5", - "CS6", - "CS7", - "EF", - "VA", - "unspecified", - ], - aliases=["target"], - ), - target_cos=dict( - type="str", - choices=[ - "background", - "best_effort", - "excellent_effort", - "critical_applications", - "video", - "voice", - "internetwork_control", - "network_control", - "unspecified", - ], - ), + dscp_from=new_dscp_spec, + dscp_to=new_dscp_spec, + dscp_target=aci_contract_dscp_spec(), + target_cos=dict(type="str", choices=list(MATCH_TARGET_COS_MAPPING.keys())), state=dict(type="str", default="present", choices=["absent", "present", "query"]), ) diff --git a/tests/integration/targets/aci_qos_custom_policy/tasks/main.yml b/tests/integration/targets/aci_qos_custom_policy/tasks/main.yml index 07116089f..0a984577a 100644 --- a/tests/integration/targets/aci_qos_custom_policy/tasks/main.yml +++ b/tests/integration/targets/aci_qos_custom_policy/tasks/main.yml @@ -17,7 +17,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - output_level: debug + output_level: '{{ aci_output_level | default("info") }}' # CLEAN ENVIRONMENT BEFORE TESTS - name: Remove the ansible_tenant @@ -128,7 +128,7 @@ - cm_remove_qos_custom_policy.proposed == {} - nm_remove_qos_custom_policy is changed - nm_remove_qos_custom_policy.previous != [] - - nm_remove_qos_custom_policy.method == "DELETE" + - nm_remove_qos_custom_policy.current == [] - nm_remove_qos_custom_policy_idempotency is not changed - nm_remove_qos_custom_policy_idempotency.previous == [] diff --git a/tests/integration/targets/aci_qos_dot1p_class/aliases b/tests/integration/targets/aci_qos_dot1p_class/aliases new file mode 100644 index 000000000..209b793f9 --- /dev/null +++ b/tests/integration/targets/aci_qos_dot1p_class/aliases @@ -0,0 +1,2 @@ +# No ACI simulator yet, so not enabled +# unsupported diff --git a/tests/integration/targets/aci_qos_dot1p_class/tasks/main.yml b/tests/integration/targets/aci_qos_dot1p_class/tasks/main.yml new file mode 100644 index 000000000..efcfa993f --- /dev/null +++ b/tests/integration/targets/aci_qos_dot1p_class/tasks/main.yml @@ -0,0 +1,167 @@ +# Test code for the ACI modules +# Copyright: (c) 2023, Gaspard Micol (@gmicol) + +# 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 + ansible.builtin.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 + ansible.builtin.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 BEFORE TESTS +- name: Remove the ansible_tenant + cisco.aci.aci_tenant: &aci_tenant_absent + <<: *aci_info + tenant: ansible_tenant + state: absent + +- name: Verify Cloud and Non-Cloud Sites in use. + ansible.builtin.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 execute only non-cloud sites + block: # block specifies execution of tasks within, based on conditions + - name: Add a new tenant + cisco.aci.aci_tenant: &aci_tenant_present + <<: *aci_info + tenant: ansible_tenant + description: Ansible tenant + state: present + + - name: Add a QoS Custom policy + cisco.aci.aci_qos_custom_policy: + <<: *aci_info + tenant: ansible_tenant + qos_custom_policy: ansible_qos_custom_policy_1 + description: QoS Custom policy 1 for ansible_tenant tenant + state: present + + # CREATE QOS Dot1P Class + - name: Add a QoS Dot1P Class (check_mode) + cisco.aci.aci_qos_dot1p_class: &aci_qos_dot1p_class_present + <<: *aci_info + tenant: ansible_tenant + qos_custom_policy: ansible_qos_custom_policy_1 + priority: level1 + dot1p_from: best_effort + dot1p_to: excellent_effort + dot1p_target: AF31 + target_cos: best_effort + state: present + check_mode: true + register: cm_add_qos_dot1p_class + + - name: Add a QoS Dot1P Class (normal_mode) + cisco.aci.aci_qos_dot1p_class: + <<: *aci_qos_dot1p_class_present + register: nm_add_qos_dot1p_class + + - name: Add the first QoS Dot1P Class again - testing idempotency + cisco.aci.aci_qos_dot1p_class: + <<: *aci_qos_dot1p_class_present + register: nm_add_qos_dot1p_class_idempotency + + - name: Add a second QoS Dot1P Class (normal_mode) + cisco.aci.aci_qos_dot1p_class: + <<: *aci_info + tenant: ansible_tenant + qos_custom_policy: ansible_qos_custom_policy_1 + dot1p_from: video + dot1p_to: voice + state: present + register: nm_add_qos_dot1p_class_2 + + - name: Asserts for QoS Dot1P Class creation tasks + ansible.builtin.assert: + that: + - cm_add_qos_dot1p_class is changed + - cm_add_qos_dot1p_class.previous == [] + - cm_add_qos_dot1p_class.current == [] + - nm_add_qos_dot1p_class is changed + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.prio == "level1" + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.from == "1" + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.to == "2" + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.target == "AF31" + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.targetCos == "1" + - nm_add_qos_dot1p_class.current.0.qosDot1PClass.attributes.dn == "uni/tn-ansible_tenant/qoscustom-ansible_qos_custom_policy_1/dot1P-1-2" + - nm_add_qos_dot1p_class_idempotency is not changed + - nm_add_qos_dot1p_class_2 is changed + - nm_add_qos_dot1p_class_2.previous == [] + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.prio == "unspecified" + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.from == "4" + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.to == "5" + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.target == "unspecified" + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.targetCos == "unspecified" + - nm_add_qos_dot1p_class_2.current.0.qosDot1PClass.attributes.dn == "uni/tn-ansible_tenant/qoscustom-ansible_qos_custom_policy_1/dot1P-4-5" + + # QUERY QOS Dot1P Class + - name: Query all QoS Custom policies + cisco.aci.aci_qos_dot1p_class: + <<: *aci_info + state: query + register: query_all_qos_dot1p_class + + - name: Query ansible_qos_dot1p_class_1 + cisco.aci.aci_qos_dot1p_class: + <<: *aci_qos_dot1p_class_present + state: query + register: query_ansible_qos_dot1p_class_1 + + - name: Asserts query tasks + ansible.builtin.assert: + that: + - query_all_qos_dot1p_class is not changed + - query_all_qos_dot1p_class.current|length >= 2 + - query_ansible_qos_dot1p_class_1 is not changed + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.prio == "level1" + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.from == "1" + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.to == "2" + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.target == "AF31" + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.targetCos == "1" + - query_ansible_qos_dot1p_class_1.current.0.qosDot1PClass.attributes.dn == "uni/tn-ansible_tenant/qoscustom-ansible_qos_custom_policy_1/dot1P-1-2" + + # DELETE QOS Dot1P Class + - name: Remove QoS Dot1P Class (check_mode) + cisco.aci.aci_qos_dot1p_class: &qos_dot1p_class_absent + <<: *aci_qos_dot1p_class_present + state: absent + check_mode: true + register: cm_remove_qos_dot1p_class + + - name: Remove QoS Dot1P Class (normal_mode) + cisco.aci.aci_qos_dot1p_class: + <<: *qos_dot1p_class_absent + register: nm_remove_qos_dot1p_class + + - name: Remove QoS Dot1P Class - testing idempotency + cisco.aci.aci_qos_dot1p_class: + <<: *qos_dot1p_class_absent + register: nm_remove_qos_dot1p_class_idempotency + + - name: Asserts deletion tasks + ansible.builtin.assert: + that: + - cm_remove_qos_dot1p_class is changed + - cm_remove_qos_dot1p_class.proposed == {} + - nm_remove_qos_dot1p_class is changed + - nm_remove_qos_dot1p_class.previous != [] + - nm_remove_qos_dot1p_class.current == [] + - nm_remove_qos_dot1p_class_idempotency is not changed + - nm_remove_qos_dot1p_class_idempotency.previous == [] + + # CLEAN ENVIRONMENT BEFORE ENDING TESTS + - name: Remove the ansible_tenant - cleanup before ending tests + cisco.aci.aci_tenant: + <<: *aci_tenant_present + state: absent diff --git a/tests/integration/targets/aci_qos_dscp_class/tasks/main.yml b/tests/integration/targets/aci_qos_dscp_class/tasks/main.yml index dc0de377f..b64848d05 100644 --- a/tests/integration/targets/aci_qos_dscp_class/tasks/main.yml +++ b/tests/integration/targets/aci_qos_dscp_class/tasks/main.yml @@ -17,7 +17,7 @@ validate_certs: '{{ aci_validate_certs | default(false) }}' use_ssl: '{{ aci_use_ssl | default(true) }}' use_proxy: '{{ aci_use_proxy | default(true) }}' - output_level: debug + output_level: '{{ aci_output_level | default("info") }}' # CLEAN ENVIRONMENT BEFORE TESTS - name: Remove the ansible_tenant @@ -156,7 +156,7 @@ - cm_remove_qos_dscp_class.proposed == {} - nm_remove_qos_dscp_class is changed - nm_remove_qos_dscp_class.previous != [] - - nm_remove_qos_dscp_class.method == "DELETE" + - nm_remove_qos_dscp_class.current == [] - nm_remove_qos_dscp_class_idempotency is not changed - nm_remove_qos_dscp_class_idempotency.previous == []